#+TITLE: HDF5 back end
#+PROPERTY: comments org
#+SETUPFILE: ../../docs/theme.setup
# -*- mode: org -*-

* Constant file prefixes (not used by the generator)               :noexport:

  #+begin_src emacs-lisp
  (setq-local org-babel-default-header-args:c '((:comments . "both")))
  org-babel-default-header-args:c
  #+end_src

  #+RESULTS:
: ((:comments . both))

  #+NAME:header
  #+begin_src c
/* This file was generated from the org-mode file.
   To generate it, open templator_hdf5.org file in Emacs and execute
   M-x org-babel-tangle
*/

  #+end_src


  #+begin_src c :tangle prefix_hdf5.h :noweb yes
<<header>>
#ifndef TREXIO_HDF5_H
#define TREXIO_HDF5_H

#include "trexio.h"
#include "trexio_private.h"
#include "trexio_s.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/stat.h>

#ifdef HAVE_CONFIG_H
  #include "config.h"
#endif
#
#ifdef HAVE_HDF5
#include "hdf5.h"

  #+end_src

  #+begin_src c :tangle prefix_hdf5.c :noweb yes
<<header>>
#include "trexio_hdf5.h"
#ifdef HAVE_CONFIG_H
  #include "config.h"
#endif
#
#ifdef HAVE_HDF5
  #+end_src

* Template for HDF5 definitions

  #+begin_src c :tangle def_hdf5.c
#define $GROUP$_GROUP_NAME          "$group$"
#define $GROUP_NUM$_NAME            "$group_num$"
#define $GROUP_DSET$_NAME           "$group_dset$"
#define $GROUP_STR$_NAME            "$group_str$"
  #+end_src

* Template for HDF5 structures

   Polymorphism of the ~trexio_t~ type is handled by ensuring that the
   corresponding types for all back ends can be safely casted to
   ~trexio_t~. This is done by making the back-end structs start with
   ~trexio_t parent~ attribute:

  #+begin_src c :tangle struct_hdf5.h
typedef struct trexio_hdf5_s {
  trexio_t     parent ;
  hid_t        file_id;
  hid_t        $group$_group;
} trexio_hdf5_t;
  #+end_src

* Template for HDF5 init/deinit

  #+begin_src c :tangle struct_hdf5.h :exports none
trexio_exit_code trexio_hdf5_init(trexio_t* const file);
trexio_exit_code trexio_hdf5_deinit(trexio_t* const file);
trexio_exit_code trexio_hdf5_inquire(const char* file_name);
trexio_exit_code trexio_hdf5_flush(trexio_t* const file);
  #+end_src

  #+begin_src c :tangle basic_hdf5.c
trexio_exit_code
trexio_hdf5_flush(trexio_t* const file)
{

  trexio_hdf5_t* f = (trexio_hdf5_t*) file;

  herr_t rc = H5Fflush(f->file_id, H5F_SCOPE_GLOBAL);
  if (rc < 0) return TREXIO_FAILURE;

  return TREXIO_SUCCESS;
}
  #+end_src

  #+begin_src c :tangle basic_hdf5.c
trexio_exit_code
trexio_hdf5_inquire(const char* file_name)
{
  /* H5Fis_hdf5 determines whether file is in HDF5 format */
  htri_t rc = H5Fis_hdf5(file_name);
  if (rc > 0 ) {
    return TREXIO_SUCCESS;    //exists and HDF5
  } else if (rc == 0) {
    return TREXIO_FILE_ERROR; //exists but not HDF5
  } else {
    return TREXIO_FAILURE;    //does not exist or function fails
  }
}
  #+end_src

  #+begin_src c :tangle basic_hdf5.c
trexio_exit_code
trexio_hdf5_init (trexio_t* const file)
{

  trexio_hdf5_t* const f = (trexio_hdf5_t*) file;

  /* If file doesn't exist, create it */
  int f_exists = 0;
  struct stat st;

  if (stat(file->file_name, &st) == 0) f_exists = 1;

  if (f_exists == 1) {

    switch (file->mode) {
    case 'r':
      // reading the existing file -> open as RDONLY
      f->file_id = H5Fopen(file->file_name, H5F_ACC_RDONLY, H5P_DEFAULT);
      break;
    case 'u':
    case 'w':
      // writing the existing file -> open as RDWRITE
      f->file_id = H5Fopen(file->file_name, H5F_ACC_RDWR, H5P_DEFAULT);
      break;
    }

  } else {

    switch (file->mode) {
    case 'r':
      // reading non-existing file -> error
      return TREXIO_FAILURE;
    case 'u':
    case 'w':
      // writing non-existing file -> create it
      f->file_id = H5Fcreate(file->file_name, H5F_ACC_EXCL, H5P_DEFAULT, H5P_DEFAULT);
      break;
    }

  }

  /* Create or open groups in the hdf5 file assuming that they exist if file exists */
  switch (file->mode) {
    case 'r':
      if (H5Lexists(f->file_id, $GROUP$_GROUP_NAME, H5P_DEFAULT) > 0) f->$group$_group = H5Gopen(f->file_id, $GROUP$_GROUP_NAME, H5P_DEFAULT);
      if (H5Lexists(f->file_id, $GROUP$_GROUP_NAME, H5P_DEFAULT) == 0) f->$group$_group = (hid_t) 0;
      break;
    case 'u':
    case 'w':
      if (f_exists == 1) {
        if (H5Lexists(f->file_id, $GROUP$_GROUP_NAME, H5P_DEFAULT) >  0) f->$group$_group = H5Gopen(f->file_id, $GROUP$_GROUP_NAME, H5P_DEFAULT);
        if (H5Lexists(f->file_id, $GROUP$_GROUP_NAME, H5P_DEFAULT) == 0) f->$group$_group = H5Gcreate(f->file_id, $GROUP$_GROUP_NAME, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
      } else {
        f->$group$_group = H5Gcreate(f->file_id, $GROUP$_GROUP_NAME, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
      }
      break;
  }
  if (f->$group$_group < (hid_t) 0) return TREXIO_INVALID_ID;

  return TREXIO_SUCCESS;
}
  #+end_src

  #+begin_src c :tangle basic_hdf5.c
trexio_exit_code
trexio_hdf5_deinit (trexio_t* const file)
{

  trexio_hdf5_t* f = (trexio_hdf5_t*) file;

  if (f->$group$_group != (hid_t) 0) H5Gclose(f->$group$_group);
  f->$group$_group = 0;

  H5Fclose(f->file_id);
  f->file_id = 0;

  return TREXIO_SUCCESS;

}
  #+end_src

* Template for HDF5 has a group

  #+begin_src c :tangle hrw_group_hdf5.h :exports none
trexio_exit_code trexio_hdf5_has_$group$ (trexio_t* const file);
  #+end_src


  #+begin_src c :tangle has_group_hdf5.c
trexio_exit_code
trexio_hdf5_has_$group$ (trexio_t* const file)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;

  const trexio_hdf5_t* f = (const trexio_hdf5_t*) file;

  struct H5G_info_t group_info;

  /* H5Gget_info return info about the HDF5 group as a group_info struct */
  herr_t status = H5Gget_info(f->$group$_group, &group_info);
  if (status < 0) return TREXIO_FAILURE;

  /* If nlinks==0 --> the group is empty, i.e. non-existent */
  if (group_info.nlinks == (hsize_t) 0) {
    return TREXIO_HAS_NOT;
  } else {
    return TREXIO_SUCCESS;
  }

}
    #+end_src

* Template for HDF5 has/read/write a numerical attribute

  #+begin_src c :tangle hrw_attr_num_hdf5.h :exports none
trexio_exit_code trexio_hdf5_has_$group_num$ (trexio_t* const file);
trexio_exit_code trexio_hdf5_read_$group_num$ (trexio_t* const file, $group_num_dtype_double$* const num);
trexio_exit_code trexio_hdf5_write_$group_num$(trexio_t* const file, const $group_num_dtype_double$ num);
  #+end_src


  #+begin_src c :tangle read_attr_num_hdf5.c
trexio_exit_code
trexio_hdf5_read_$group_num$ (trexio_t* const file, $group_num_dtype_double$* const num)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if (num  == NULL) return TREXIO_INVALID_ARG_2;

  const trexio_hdf5_t* f = (const trexio_hdf5_t*) file;
  /* Quit if the dimensioning attribute is missing in the file */
  if (H5Aexists(f->$group$_group, $GROUP_NUM$_NAME) == 0) return TREXIO_FAILURE;

  /* Read the $group_num$ attribute of $group$ group */
  const hid_t num_id = H5Aopen(f->$group$_group, $GROUP_NUM$_NAME, H5P_DEFAULT);
  if (num_id <= 0) return TREXIO_INVALID_ID;

  const herr_t status = H5Aread(num_id, H5T_$GROUP_NUM_H5_DTYPE$, num);

  H5Aclose(num_id);

  if (status < 0) return TREXIO_FAILURE;

  return TREXIO_SUCCESS;

}
   #+end_src


  #+begin_src c :tangle write_attr_num_hdf5.c
trexio_exit_code
trexio_hdf5_write_$group_num$ (trexio_t* const file, const $group_num_dtype_double$ num)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;

  trexio_hdf5_t* const f = (trexio_hdf5_t*) file;

  /* Delete the attribute if it exists and if the file is open in UNSAFE mode */
  if (trexio_hdf5_has_$group_num$(file) == TREXIO_SUCCESS && file->mode == 'u') {
    herr_t status_del = H5Adelete(f->$group$_group, $GROUP_NUM$_NAME);
    if (status_del < 0) return TREXIO_FAILURE;
  }

  /* Setup the dataspace */
  const hid_t dtype_id = H5Tcopy(H5T_$GROUP_NUM_H5_DTYPE$);
  if (dtype_id <= 0) return TREXIO_INVALID_ID;

  const hid_t dspace_id = H5Screate(H5S_SCALAR);
  if (dspace_id <= 0) {
    H5Tclose(dtype_id);
    return TREXIO_INVALID_ID;
  }

  const hid_t num_id = H5Acreate(f->$group$_group,
                                 $GROUP_NUM$_NAME,
                                 dtype_id, dspace_id,
                                 H5P_DEFAULT, H5P_DEFAULT);
  if (num_id <= 0) {
    H5Sclose(dspace_id);
    H5Tclose(dtype_id);
    return TREXIO_INVALID_ID;
  }

  const herr_t status = H5Awrite(num_id, dtype_id, &num);

  H5Sclose(dspace_id);
  H5Aclose(num_id);
  H5Tclose(dtype_id);
  if (status < 0) return TREXIO_FAILURE;

  return TREXIO_SUCCESS;
}
    #+end_src

  #+begin_src c :tangle has_attr_num_hdf5.c
trexio_exit_code
trexio_hdf5_has_$group_num$ (trexio_t* const file)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;

  const trexio_hdf5_t* f = (const trexio_hdf5_t*) file;
  if (f->$group$_group == (hsize_t) 0) return TREXIO_HAS_NOT;

  htri_t status = H5Aexists(f->$group$_group, $GROUP_NUM$_NAME);
  /* H5Aexists returns positive value if attribute exists, 0 if does not, negative if error */
  if (status > 0){
    return TREXIO_SUCCESS;
  } else if (status == 0) {
    return TREXIO_HAS_NOT;
  } else {
    return TREXIO_FAILURE;
  }

}
    #+end_src

* Template for HDF5 has/read/write a dataset of numerical data

   #+begin_src c :tangle hrw_dset_data_hdf5.h :exports none
trexio_exit_code trexio_hdf5_has_$group_dset$(trexio_t* const file);
trexio_exit_code trexio_hdf5_read_$group_dset$(trexio_t* const file, $group_dset_dtype$* const $group_dset$, const uint32_t rank, const uint64_t* dims);
trexio_exit_code trexio_hdf5_write_$group_dset$(trexio_t* const file, const $group_dset_dtype$* $group_dset$, const uint32_t rank, const uint64_t* dims);
   #+end_src

   #+begin_src c :tangle read_dset_data_hdf5.c
trexio_exit_code
trexio_hdf5_read_$group_dset$ (trexio_t* const file, $group_dset_dtype$* const $group_dset$, const uint32_t rank, const uint64_t* dims)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if ($group_dset$ == NULL) return TREXIO_INVALID_ARG_2;

  const trexio_hdf5_t* f = (const trexio_hdf5_t*) file;

  // open the dataset to get its dimensions
  hid_t dset_id = H5Dopen(f->$group$_group, $GROUP_DSET$_NAME, H5P_DEFAULT);
  if (dset_id <= 0) return TREXIO_INVALID_ID;

  // allocate space for the dimensions to be read
  hsize_t* ddims = CALLOC( (int) rank, hsize_t);
  if (ddims == NULL) return TREXIO_FAILURE;

  // get the dataspace of the dataset
  hid_t dspace_id = H5Dget_space(dset_id);
  // get the rank and dimensions of the dataset
  int rrank = H5Sget_simple_extent_dims(dspace_id, ddims, NULL);
  // check that dimensions are consistent
  if (rrank != (int) rank) {
    FREE(ddims);
    H5Sclose(dspace_id);
    H5Dclose(dset_id);
    return TREXIO_INVALID_ARG_3;
  }

  for (uint32_t i=0; i<rank; ++i){
    if (ddims[i] != dims[i]) {
      FREE(ddims);
      H5Sclose(dspace_id);
      H5Dclose(dset_id);
      return TREXIO_INVALID_ARG_4;
    }
  }

  FREE(ddims);

  /* Read dataset */
  herr_t status = H5Dread(dset_id,
                          H5T_$GROUP_DSET_H5_DTYPE$,
                          H5S_ALL, H5S_ALL, H5P_DEFAULT, 
                          $group_dset$);

  H5Sclose(dspace_id);
  H5Dclose(dset_id);

  if (status < 0) return TREXIO_FAILURE;

  return TREXIO_SUCCESS;
}

   #+end_src

   #+begin_src c :tangle write_dset_data_hdf5.c
trexio_exit_code
trexio_hdf5_write_$group_dset$ (trexio_t* const file, const $group_dset_dtype$* $group_dset$, const uint32_t rank, const uint64_t* dims)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if ($group_dset$ == NULL) return TREXIO_INVALID_ARG_2;

  trexio_hdf5_t* f = (trexio_hdf5_t*) file;

  /*
     Try to delete an existing dataset by unlinking it from the group (UNSAFE mode).
     NOTE: In principle, HDF5 should see the deallocated (unused) file space and free it,
     thus reducing the size of the HDF5 file. In practic, this is not always the case.

     Consider using HDF5-native h5repack utility after deleting/overwriting big datasets.
  ,*/
  
  if ((trexio_hdf5_has_$group_dset$(file) == TREXIO_SUCCESS) && (file->mode == 'u')) {
    herr_t status_del = H5Ldelete(f->$group$_group, $GROUP_DSET$_NAME, H5P_DEFAULT);
    if (status_del < 0) return TREXIO_FAILURE;
  }

  hid_t dspace_id = H5Screate_simple( (int) rank, (const hsize_t*) dims, NULL);
  if (dspace_id <= 0) return TREXIO_INVALID_ID;

  hid_t dset_id = H5Dcreate (f->$group$_group,
                             $GROUP_DSET$_NAME,
                             H5T_$GROUP_DSET_H5_DTYPE$,
                             dspace_id,
                             H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  if (dset_id <= 0) {
    H5Sclose(dspace_id);
    return TREXIO_INVALID_ID;
  }

  herr_t status = H5Dwrite(dset_id,
                           H5T_$GROUP_DSET_H5_DTYPE$,
                           H5S_ALL,
                           dspace_id,
                           H5P_DEFAULT,
                           $group_dset$);
  H5Dclose(dset_id);
  H5Sclose(dspace_id);
  if (status < 0) return TREXIO_FAILURE;

  return TREXIO_SUCCESS;

}
   #+end_src

   #+begin_src c :tangle has_dset_data_hdf5.c
trexio_exit_code
trexio_hdf5_has_$group_dset$ (trexio_t* const file)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;

  trexio_hdf5_t* f = (trexio_hdf5_t*) file;
  if (f->$group$_group == (hsize_t) 0) return TREXIO_HAS_NOT;

  htri_t exists = H5Lexists(f->$group$_group, $GROUP_DSET$_NAME, H5P_DEFAULT);
  if (exists > 0) {
    return TREXIO_SUCCESS;
  } else if (exists < 0) {
    return TREXIO_FAILURE;
  } else {
    return TREXIO_HAS_NOT;
  }

}
   #+end_src

* Template for HDF5 has/read/write a dataset of sparse data

  Sparse data is stored using extensible datasets of HDF5. Extensibility is required
  due to the fact that the sparse data will be written in chunks of user-defined size.

   #+begin_src c :tangle hrw_dset_sparse_hdf5.h :exports none
trexio_exit_code trexio_hdf5_has_$group_dset$(trexio_t* const file);
trexio_exit_code trexio_hdf5_read_$group_dset$(trexio_t* const file, const int64_t offset_file, const int64_t size, const int64_t size_max, int64_t* const eof_read_size, int32_t* const index_sparse, double* const value_sparse);
trexio_exit_code trexio_hdf5_write_$group_dset$(trexio_t* const file, const int64_t offset_file, const int64_t size, const int64_t size_max, const int32_t* index_sparse, const double* value_sparse);
trexio_exit_code trexio_hdf5_read_$group_dset$_size(trexio_t* const file, int64_t* const size_max);
   #+end_src


   #+begin_src c :tangle write_dset_sparse_hdf5.c
trexio_exit_code
trexio_hdf5_write_$group_dset$ (trexio_t* const file,
                                const int64_t offset_file,
                                const int64_t size,
                                const int64_t size_max,
                                const int32_t* index_sparse,
                                const double* value_sparse)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;

  trexio_hdf5_t* f = (trexio_hdf5_t*) file;

  hid_t index_dtype;
  const void* index_p = NULL;
  void* index_p_non_const = NULL;
  uint64_t size_ranked = (uint64_t) size * $group_dset_rank$;
  /* Determine the optimal type for storing indices depending on the size_max (usually mo_num or ao_num) */
  if (size_max < UINT8_MAX) {
    uint8_t* index = CALLOC(size_ranked, uint8_t);
    if (index == NULL) return TREXIO_ALLOCATION_FAILED;
    for (uint64_t i=0; i<size_ranked; ++i){
      index[i] = (uint8_t) index_sparse[i];
    }
    index_p = index;
    index_p_non_const = index;
    index_dtype = H5T_NATIVE_UINT8;
  } else if (size_max < UINT16_MAX) {
    uint16_t* index = CALLOC(size_ranked, uint16_t);
    if (index == NULL) return TREXIO_ALLOCATION_FAILED;
    for (uint64_t i=0; i<size_ranked; ++i){
      index[i] = (uint16_t) index_sparse[i];
    }
    index_p = index;
    index_p_non_const = index;
    index_dtype = H5T_NATIVE_UINT16;
  } else {
    index_p = index_sparse;
    index_dtype = H5T_NATIVE_INT32;
  }

  /* Store float values in double precision */
  hid_t value_dtype = H5T_NATIVE_DOUBLE;
  /* Arrays of chunk dims that will be used for chunking the dataset */
  const hsize_t chunk_i_dims[1] = {size_ranked};
  const hsize_t chunk_v_dims[1] = {size};

  /* Indices and values are stored as 2 independent datasets in the HDF5 file */
  char dset_index_name[256];
  char dset_value_name[256];
  /* Build the names of the datasets */
  strncpy(dset_index_name, $GROUP_DSET$_NAME "_indices", 256);
  strncpy(dset_value_name, $GROUP_DSET$_NAME "_values", 256);

  trexio_exit_code rc_write = TREXIO_FAILURE;
  /* NOTE: chunk size is set upon creation of the HDF5 dataset and cannot be changed ! */
  if (trexio_hdf5_has_$group_dset$(file) == TREXIO_HAS_NOT) {
  /* If the file does not exist -> create it and write */

  /* Create chunked dataset with index_dtype datatype and write indices into it */
    rc_write = trexio_hdf5_create_write_dset_sparse(f->$group$_group, dset_index_name, index_dtype, chunk_i_dims, index_p);
    if (index_p != index_sparse) FREE(index_p_non_const);
    if (rc_write != TREXIO_SUCCESS) return rc_write;

  /* Create chunked dataset with value_dtype datatype and write values into it */
    rc_write = trexio_hdf5_create_write_dset_sparse(f->$group$_group, dset_value_name, value_dtype, chunk_v_dims, value_sparse);
    if (rc_write != TREXIO_SUCCESS) return rc_write;

  } else {
  /* If the file exists -> open it and write */
    hsize_t offset_i[1] = {(hsize_t) offset_file * $group_dset_rank$};
    hsize_t offset_v[1] = {(hsize_t) offset_file};

  /* Create chunked dataset with index_dtype datatype and write indices into it */
    rc_write = trexio_hdf5_open_write_dset_sparse(f->$group$_group, dset_index_name, index_dtype, chunk_i_dims, offset_i, index_p);
    if (index_p != index_sparse) FREE(index_p_non_const);
    if (rc_write != TREXIO_SUCCESS) return rc_write;

  /* Create chunked dataset with value_dtype datatype and write values into it */
    rc_write = trexio_hdf5_open_write_dset_sparse(f->$group$_group, dset_value_name, value_dtype, chunk_v_dims, offset_v, value_sparse);
    if (rc_write != TREXIO_SUCCESS) return rc_write;

  }

  return TREXIO_SUCCESS;
}
   #+end_src


   #+begin_src c :tangle read_dset_sparse_hdf5.c
trexio_exit_code
trexio_hdf5_read_$group_dset$ (trexio_t* const file,
                               const int64_t offset_file,
                               const int64_t size,
                               const int64_t size_max,
                               int64_t* const eof_read_size,
                               int32_t* const index_read,
                               double* const value_read)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if (eof_read_size == NULL) return TREXIO_INVALID_ARG_5;

  const trexio_hdf5_t* f = (const trexio_hdf5_t*) file;

  /* Indices and values are stored as 2 independent datasets in the HDF5 file */
  char dset_index_name[256];
  char dset_value_name[256];
  /* Build the names of the datasets */
  strncpy(dset_index_name, $GROUP_DSET$_NAME "_indices", 256);
  strncpy(dset_value_name, $GROUP_DSET$_NAME "_values", 256);

  hsize_t offset_i[1] = {(hsize_t) offset_file * $group_dset_rank$};
  hsize_t count_i[1] = {(hsize_t) size * $group_dset_rank$};

  hsize_t offset_v[1] = {(hsize_t) offset_file};
  hsize_t count_v[1] = {(hsize_t) size};

  int is_index = 1, is_value = 0;
  trexio_exit_code rc_read;

  // attempt to read indices
  rc_read = trexio_hdf5_open_read_dset_sparse(f->$group$_group, dset_index_name, $group_dset_rank$, offset_i, count_i, NULL, is_index, index_read);
  if (rc_read != TREXIO_SUCCESS && rc_read != TREXIO_END) return rc_read;
  // attempt to read values
  // when EOF is encountered - the count_v[0] is modified and contains the number of elements being read
  rc_read = trexio_hdf5_open_read_dset_sparse(f->$group$_group, dset_value_name, 1, offset_v, count_v, eof_read_size, is_value, value_read);
  if (rc_read != TREXIO_SUCCESS && rc_read != TREXIO_END) return rc_read;

  return rc_read;
}
   #+end_src


   #+begin_src c :tangle read_dset_sparse_hdf5.c
trexio_exit_code
trexio_hdf5_read_$group_dset$_size (trexio_t* const file, int64_t* const size_max)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if (size_max == NULL) return TREXIO_INVALID_ARG_2;

  const trexio_hdf5_t* f = (const trexio_hdf5_t*) file;

  hid_t dset_id = H5Dopen(f->$group$_group, $GROUP_DSET$_NAME "_values", H5P_DEFAULT);
  if (dset_id <= 0) return TREXIO_INVALID_ID;

  hid_t fspace_id = H5Dget_space(dset_id);
  if (fspace_id < 0) {
    H5Dclose(dset_id);
    return TREXIO_INVALID_ID;
  }

  // allocate space for the dimensions to be read
  hsize_t ddims[1] = {0};

  // get the rank and dimensions of the dataset
  H5Sget_simple_extent_dims(fspace_id, ddims, NULL);

  H5Dclose(dset_id);
  H5Sclose(fspace_id);

  *size_max = (int64_t) ddims[0];

  return TREXIO_SUCCESS;
}
   #+end_src


   #+begin_src c :tangle has_dset_sparse_hdf5.c
trexio_exit_code
trexio_hdf5_has_$group_dset$ (trexio_t* const file)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;

  trexio_hdf5_t* f = (trexio_hdf5_t*) file;
  if (f->$group$_group == (hsize_t) 0) return TREXIO_HAS_NOT;

  htri_t exists = H5Lexists(f->$group$_group, $GROUP_DSET$_NAME "_values", H5P_DEFAULT);
  if (exists > 0) {
    return TREXIO_SUCCESS;
  } else if (exists < 0) {
    return TREXIO_FAILURE;
  } else {
    return TREXIO_HAS_NOT;
  }

}
   #+end_src

* Template for HDF5 has/read/write a dataset of buffered vectors

   Chunked I/O in HDF5 for ~buffered~ data.

   #+begin_src c :tangle hrw_buffered_hdf5.h :exports none
trexio_exit_code trexio_hdf5_has_$group_dset$(trexio_t* const file);
trexio_exit_code trexio_hdf5_read_$group_dset$(trexio_t* const file, const int64_t offset_file, const uint32_t rank, const uint64_t* dims, int64_t* const eof_read_size, double* const dset);
trexio_exit_code trexio_hdf5_write_$group_dset$(trexio_t* const file, const int64_t offset_file, const uint32_t rank, const uint64_t* dims, const double* dset);
trexio_exit_code trexio_hdf5_read_$group_dset$_size(trexio_t* const file, int64_t* const size_max);
   #+end_src

   #+begin_src c :tangle read_buffered_hdf5.c
trexio_exit_code trexio_hdf5_read_$group_dset$(trexio_t* const file,
                                               const int64_t offset_file,
                                               const uint32_t rank,
                                               const uint64_t* dims,
                                               int64_t* const eof_read_size,
                                               double* const dset)
{
  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if (eof_read_size == NULL) return TREXIO_INVALID_ARG_5;
  if (dset == NULL) return TREXIO_INVALID_ARG_6;

  const char* dset_name = "$group_dset$";

  const trexio_hdf5_t* f = (const trexio_hdf5_t*) file;

  hsize_t offset[1] = {(hsize_t) offset_file};
  hsize_t count[1]  = {(hsize_t) dims[0]};

  /* Attempt to read values (if EOF -> eof_read_size is modified with the number of elements read and return code is TREXIO_END)
     0 argument below is requires to skip internal treatment specific to sparse indices (i.e. their de-compression).*/
  return trexio_hdf5_open_read_dset_sparse(f->$group$_group, dset_name, 1, offset, count, eof_read_size, 0, dset);
}
   #+end_src

   #+begin_src c :tangle write_buffered_hdf5.c
trexio_exit_code trexio_hdf5_write_$group_dset$(trexio_t* const file,
                                                const int64_t offset_file,
		                                const uint32_t rank,
                                                const uint64_t* dims,
                                                const double* dset)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if (dset == NULL) return TREXIO_INVALID_ARG_5;

  const char* dset_name = "$group_dset$";

  trexio_hdf5_t* f = (trexio_hdf5_t*) file;

  hid_t dtype = H5T_NATIVE_DOUBLE;

  /* Arrays of chunk dims that will be used for chunking the dataset */
  const hsize_t chunk_dims[1] = {(hsize_t) dims[0]};

  trexio_exit_code rc_write = TREXIO_FAILURE;
  /* NOTE: chunk size is set upon creation of the HDF5 dataset and cannot be changed ! */
  if (trexio_hdf5_has_$group_dset$(file) == TREXIO_HAS_NOT) {
  /* If the file does not exist -> create it and write */

  /* Create chunked dataset with dtype datatype and write indices into it */
    rc_write = trexio_hdf5_create_write_dset_sparse(f->$group$_group, dset_name, dtype, chunk_dims, dset);
    if (rc_write != TREXIO_SUCCESS) return rc_write;

  } else {
  /* If the file exists -> open it and write */
    hsize_t offset_data[1] = {(hsize_t) offset_file};

  /* Create chunked dataset with dtype datatype and write indices into it */
    rc_write = trexio_hdf5_open_write_dset_sparse(f->$group$_group, dset_name, dtype, chunk_dims, offset_data, dset);
    if (rc_write != TREXIO_SUCCESS) return rc_write;

  }

  return TREXIO_SUCCESS;
}

trexio_exit_code
trexio_hdf5_read_$group_dset$_size (trexio_t* const file, int64_t* const size_max)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if (size_max == NULL) return TREXIO_INVALID_ARG_2;

  const char* dset_name = "$group_dset$";

  const trexio_hdf5_t* f = (const trexio_hdf5_t*) file;

  hid_t dset_id = H5Dopen(f->$group$_group, dset_name, H5P_DEFAULT);
  if (dset_id <= 0) return TREXIO_INVALID_ID;

  hid_t fspace_id = H5Dget_space(dset_id);
  if (fspace_id < 0) {
    H5Dclose(dset_id);
    return TREXIO_INVALID_ID;
  }

  // allocate space for the dimensions to be read
  hsize_t ddims[1] = {0};

  // get the rank and dimensions of the dataset
  H5Sget_simple_extent_dims(fspace_id, ddims, NULL);

  H5Dclose(dset_id);
  H5Sclose(fspace_id);

  *size_max = (int64_t) ddims[0];

  return TREXIO_SUCCESS;
}
   #+end_src

   #+begin_src c :tangle has_buffered_hdf5.c
trexio_exit_code trexio_hdf5_has_$group_dset$(trexio_t* const file)
{
  if (file == NULL) return TREXIO_INVALID_ARG_1;

  trexio_hdf5_t* f = (trexio_hdf5_t*) file;
  if (f->$group$_group == (hsize_t) 0) return TREXIO_HAS_NOT;

  const char* dset_name = "$group_dset$";

  htri_t exists = H5Lexists(f->$group$_group, dset_name, H5P_DEFAULT);
  if (exists > 0) {
    return TREXIO_SUCCESS;
  } else if (exists < 0) {
    return TREXIO_FAILURE;
  } else {
    return TREXIO_HAS_NOT;
  }
}
   #+end_src

* Template for HDF5 has/read/write a dataset of strings

   #+begin_src c :tangle hrw_dset_str_hdf5.h :exports none
trexio_exit_code trexio_hdf5_has_$group_dset$(trexio_t* const file);
trexio_exit_code trexio_hdf5_read_$group_dset$(trexio_t* const file, char* const $group_dset$, const uint32_t rank, const uint64_t* dims, const uint32_t max_str_len);
trexio_exit_code trexio_hdf5_write_$group_dset$(trexio_t* const file, const char** $group_dset$, const uint32_t rank, const uint64_t* dims);
   #+end_src

   #+begin_src c :tangle read_dset_str_hdf5.c
trexio_exit_code
trexio_hdf5_read_$group_dset$ (trexio_t* const file, char* const $group_dset$, const uint32_t rank, const uint64_t* dims, const uint32_t max_str_len)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if ($group_dset$  == NULL) return TREXIO_INVALID_ARG_2;

  const trexio_hdf5_t* f = (const trexio_hdf5_t*) file;

  herr_t status;

  // open the dataset to get its dimensions
  hid_t dset_id = H5Dopen(f->$group$_group, $GROUP_DSET$_NAME, H5P_DEFAULT);
  if (dset_id <= 0) return TREXIO_INVALID_ID;

  // allocate space for the dimensions to be read
  hsize_t* ddims = CALLOC( (int) rank, hsize_t);
  if (ddims == NULL) {
    H5Dclose(dset_id);
    return TREXIO_ALLOCATION_FAILED;
  }

  hid_t dspace = H5Dget_space(dset_id);
  if (dset_id <= 0) {
    FREE(ddims);
    H5Dclose(dset_id);
    return TREXIO_INVALID_ID;
  }

  // get the rank of the dataset in a file
  int rrank = H5Sget_simple_extent_dims(dspace, ddims, NULL);
  if (rrank != (int) rank) {
    FREE(ddims);
    H5Dclose(dset_id);
    H5Sclose(dspace);
    return TREXIO_INVALID_ARG_3;
  }

  for (int i=0; i<rrank; i++) {
    if (ddims[i] != dims[i]) {
      H5Dclose(dset_id);
      H5Sclose(dspace);
      FREE(ddims);
      return TREXIO_INVALID_ARG_4;
    }
  }
  FREE(ddims);

  hid_t memtype = H5Tcopy (H5T_C_S1);
  status = H5Tset_size(memtype, H5T_VARIABLE);
  if (status < 0 || memtype <= 0) {
    H5Dclose(dset_id);
    H5Sclose(dspace);
    return TREXIO_FAILURE;
  }

  char** rdata = CALLOC(dims[0], char*);
  if (rdata == NULL) {
    H5Dclose(dset_id);
    H5Sclose(dspace);
    H5Tclose(memtype);
    return TREXIO_ALLOCATION_FAILED;
  }

  status = H5Dread(dset_id, memtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, rdata);
  if (status < 0) {
    FREE(rdata);
    H5Dclose(dset_id);
    H5Sclose(dspace);
    H5Tclose(memtype);
    return TREXIO_FAILURE;
  }

  // copy contents of temporary rdata buffer into the group_dset otherwise they are lost
  // after calling H5Treclaim or H5Dvlen_reclaim functions
  strcpy($group_dset$, "");
  for (uint64_t i=0; i<dims[0]; i++) {
    strncat($group_dset$, rdata[i], max_str_len);
    strcat($group_dset$, TREXIO_DELIM);
  }

  // H5Dvlen_reclaim is deprecated and replaced by H5Treclaim in HDF5 v.1.12.0
  #if (H5_VERS_MAJOR <= 1 && H5_VERS_MINOR < 12)
    status = H5Dvlen_reclaim(memtype, dspace, H5P_DEFAULT, rdata);
  #else
    status = H5Treclaim(memtype, dspace, H5P_DEFAULT, rdata);
  #endif

  if (status < 0) {
    FREE(rdata);
    H5Dclose(dset_id);
    H5Sclose(dspace);
    H5Tclose(memtype);
    return TREXIO_FAILURE;
  }

  FREE(rdata);
  H5Dclose(dset_id);
  H5Sclose(dspace);
  H5Tclose(memtype);

  return TREXIO_SUCCESS;
}
   #+end_src

   #+begin_src c :tangle write_dset_str_hdf5.c
trexio_exit_code
trexio_hdf5_write_$group_dset$ (trexio_t* const file, const char** $group_dset$, const uint32_t rank, const uint64_t* dims)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if ($group_dset$  == NULL) return TREXIO_INVALID_ARG_2;

  trexio_hdf5_t* f = (trexio_hdf5_t*) file;

  /*
     Try to delete an existing dataset by unlinking it from the group (UNSAFE mode).
     NOTE: In principle, HDF5 should see the deallocated (unused) file space and free it,
     thus reducing the size of the HDF5 file. In practic, this is not always the case.

     Consider using HDF5-provided h5repack utility after deleting/overwriting big datasets.
  ,*/
  if ( (trexio_hdf5_has_$group_dset$(file) == TREXIO_SUCCESS) && (file->mode == 'u') ) {
    herr_t status_del = H5Ldelete(f->$group$_group, $GROUP_DSET$_NAME, H5P_DEFAULT);
    if (status_del < 0) return TREXIO_FAILURE;
  }

  herr_t status;
  hid_t dset_id;

  /* we are going to write variable-length strings */
  hid_t memtype = H5Tcopy (H5T_C_S1);
  if (memtype <= 0) return TREXIO_INVALID_ID;

  status = H5Tset_size (memtype, H5T_VARIABLE);
  if (status < 0) return TREXIO_FAILURE;

  hid_t dspace = H5Screate_simple( (int) rank, (const hsize_t*) dims, NULL);
  if (dspace <= 0) return TREXIO_INVALID_ID;

  /* code to create dataset */
  hid_t filetype = H5Tcopy (H5T_FORTRAN_S1);
  if (filetype <= 0) return TREXIO_INVALID_ID;

  status = H5Tset_size (filetype, H5T_VARIABLE);
  if (status < 0) return TREXIO_FAILURE;

  dset_id = H5Dcreate (f->$group$_group, $GROUP_DSET$_NAME, filetype, dspace,
                       H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  if (dset_id <= 0) return TREXIO_INVALID_ID;

  status = H5Dwrite (dset_id, memtype,
                     H5S_ALL, H5S_ALL, H5P_DEFAULT,
                     $group_dset$);

  H5Dclose (dset_id);
  H5Sclose (dspace);
  H5Tclose (filetype);
  H5Tclose (memtype);

  if (status < 0) return TREXIO_FAILURE;

  return TREXIO_SUCCESS;

}
   #+end_src

   #+begin_src c :tangle has_dset_str_hdf5.c
trexio_exit_code
trexio_hdf5_has_$group_dset$ (trexio_t* const file)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;

  trexio_hdf5_t* f = (trexio_hdf5_t*) file;
  if (f->$group$_group == (hsize_t) 0) return TREXIO_HAS_NOT;

  htri_t exists = H5Lexists(f->$group$_group, $GROUP_DSET$_NAME, H5P_DEFAULT);
  if (exists > 0) {
    return TREXIO_SUCCESS;
  } else if (exists < 0) {
    return TREXIO_FAILURE;
  } else {
    return TREXIO_HAS_NOT;
  }

}
   #+end_src

* Template for HDF5 has/read/write a string attribute

  #+begin_src c :tangle hrw_attr_str_hdf5.h :exports none
trexio_exit_code trexio_hdf5_has_$group_str$ (trexio_t* const file);
trexio_exit_code trexio_hdf5_read_$group_str$ (trexio_t* const file, char* const str, const uint32_t max_str_len);
trexio_exit_code trexio_hdf5_write_$group_str$(trexio_t* const file, const char* str);
  #+end_src


  #+begin_src c :tangle read_attr_str_hdf5.c
trexio_exit_code
trexio_hdf5_read_$group_str$ (trexio_t* const file, char* const str, const uint32_t max_str_len)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if (str  == NULL) return TREXIO_INVALID_ARG_2;

  const trexio_hdf5_t* f = (const trexio_hdf5_t*) file;
  /* Quit if the string attribute is missing in the file */
  if (H5Aexists(f->$group$_group, $GROUP_STR$_NAME) == 0) return TREXIO_HAS_NOT;

  /* Read the $group_str$ attribute of $group$ group */
  const hid_t str_id = H5Aopen(f->$group$_group, $GROUP_STR$_NAME, H5P_DEFAULT);
  if (str_id <= 0) return TREXIO_INVALID_ID;

  const hid_t ftype_id = H5Aget_type(str_id);
  if (ftype_id <= 0) return TREXIO_INVALID_ID;
  uint64_t sdim = H5Tget_size(ftype_id);
  sdim++;                         /* Make room for null terminator */

  const hid_t mem_id = H5Tcopy(H5T_C_S1);
  if (mem_id <= 0) return TREXIO_INVALID_ID;

  herr_t status;
  status = (max_str_len+1) > sdim ? H5Tset_size(mem_id, sdim) : H5Tset_size(mem_id, max_str_len+1) ;
  if (status < 0) return TREXIO_FAILURE;

  status = H5Aread(str_id, mem_id, str);
  if (status < 0) return TREXIO_FAILURE;

  H5Aclose(str_id);
  H5Tclose(mem_id);
  H5Tclose(ftype_id);

  return TREXIO_SUCCESS;

}
   #+end_src


  #+begin_src c :tangle write_attr_str_hdf5.c
trexio_exit_code
trexio_hdf5_write_$group_str$ (trexio_t* const file, const char* str)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if (str  == NULL) return TREXIO_INVALID_ARG_2;

  trexio_hdf5_t* const f = (trexio_hdf5_t*) file;

  /* Delete the attribute if it exists and if the file is open in UNSAFE mode */
  if (trexio_hdf5_has_$group_str$(file) == TREXIO_SUCCESS && file->mode == 'u') {
    herr_t status_del = H5Adelete(f->$group$_group, $GROUP_STR$_NAME);
    if (status_del < 0) return TREXIO_FAILURE;
  }

  /* Setup the datatype for variable length string */
  const hid_t dtype_id = H5Tcopy(H5T_C_S1);
  if (dtype_id <= 0) return TREXIO_INVALID_ID;

  size_t str_attr_len = strlen(str) + 1;

  herr_t status;
  status = H5Tset_size(dtype_id, str_attr_len);
  if (status < 0) return TREXIO_FAILURE;

  status = H5Tset_strpad(dtype_id, H5T_STR_NULLTERM);
  if (status < 0) return TREXIO_FAILURE;

  /* Setup the dataspace */
  const hid_t dspace_id = H5Screate(H5S_SCALAR);
  if (dspace_id <= 0) return TREXIO_INVALID_ID;

  /* Create the $group_str$ attribute of $group$ group */
  const hid_t str_id = H5Acreate(f->$group$_group,
                                 $GROUP_STR$_NAME,
                                 dtype_id, dspace_id,
                                 H5P_DEFAULT, H5P_DEFAULT);

  if (str_id <= 0) {
    H5Sclose(dspace_id);
    H5Tclose(dtype_id);
    return TREXIO_INVALID_ID;
  }

  status = H5Awrite(str_id, dtype_id, str);

  H5Aclose(str_id);
  H5Sclose(dspace_id);
  H5Tclose(dtype_id);

  if (status < 0) return TREXIO_FAILURE;

  return TREXIO_SUCCESS;
}
    #+end_src

  #+begin_src c :tangle has_attr_str_hdf5.c
trexio_exit_code
trexio_hdf5_has_$group_str$ (trexio_t* const file)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;

  const trexio_hdf5_t* f = (const trexio_hdf5_t*) file;
  if (f->$group$_group == (hsize_t) 0) return TREXIO_HAS_NOT;

  htri_t status = H5Aexists(f->$group$_group, $GROUP_STR$_NAME);
  /* H5Aexists returns positive value if attribute exists, 0 if does not, negative if error */
  if (status > 0){
    return TREXIO_SUCCESS;
  } else if (status == 0) {
    return TREXIO_HAS_NOT;
  } else {
    return TREXIO_FAILURE;
  }

}
    #+end_src
* Template for HDF5 delete a group (UNSAFE mode)

  **Note:** in early versions of the HDF5 library (v < 1.10) unlinking an object was not working as expected
  and the associated memory was not necessarily freed (see [[https://stackoverflow.com/questions/1124994/removing-data-from-a-hdf5-file][this StackOverflow discussion]] for example).
  Nevertheless, some space might remain occupied even after deleting the associated object in recent version.
  To take the best use of the deleted file space, we recommend to write the deleted group within the same session
  (i.e. before closing the TREXIO file).

  In principle, one can use HDF5-provided ~h5repack~ binary, which copies all existing objects from one file into another.
  Thus, any corrupted/lost file space will remain in the first file. The use of ~h5repack~ is highly encouraged.

  #+begin_src c :tangle delete_group_hdf5.h :exports none
trexio_exit_code trexio_hdf5_delete_$group$ (trexio_t* const file);
  #+end_src

  #+begin_src c :tangle delete_group_hdf5.c
trexio_exit_code
trexio_hdf5_delete_$group$ (trexio_t* const file)
{
  if (file == NULL) return TREXIO_INVALID_ARG_1;

  trexio_hdf5_t* f = (trexio_hdf5_t*) file;

  // delete the link to the existing group: this should free the associated space
  H5Gclose(f->$group$_group);
  f->$group$_group = 0;
  herr_t status = H5Ldelete(f->file_id, $GROUP$_GROUP_NAME, H5P_DEFAULT);
  if (status < 0) return TREXIO_FAILURE;

  // re-create the group (with the new link ?)
  f->$group$_group = H5Gcreate(f->file_id, $GROUP$_GROUP_NAME, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  if (f->$group$_group <= 0L) return TREXIO_INVALID_ID;

  return TREXIO_SUCCESS;
}
   #+end_src

* Source code for the determinant part

   Each array is stored in a separate HDF5 dataset due to the fact that determinant I/O has to be decoupled.
   Chunks are used to read/write the data to prevent memory overflow. Chunks have a given ~int64_t dims[0]*dims[1]~.
   Size specifies the number of data items (e.g. determinants) to process.

   #+begin_src c :tangle hrw_determinant_hdf5.h :exports none
trexio_exit_code trexio_hdf5_has_determinant_list(trexio_t* const file);
trexio_exit_code trexio_hdf5_read_determinant_list(trexio_t* const file, const int64_t offset_file, const uint32_t rank, const uint64_t* dims, int64_t* const eof_read_size, int64_t* const list);
trexio_exit_code trexio_hdf5_write_determinant_list(trexio_t* const file, const int64_t offset_file, const uint32_t rank, const uint64_t* dims, const int64_t* list);
   #+end_src

   #+begin_src c :tangle read_determinant_hdf5.c
trexio_exit_code trexio_hdf5_read_determinant_list(trexio_t* const file,
                                                   const int64_t offset_file,
                                                   const uint32_t rank,
                                                   const uint64_t* dims,
                                                   int64_t* const eof_read_size,
                                                   int64_t* const list)
{
  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if (eof_read_size == NULL) return TREXIO_INVALID_ARG_5;
  if (list == NULL) return TREXIO_INVALID_ARG_6;

  const trexio_hdf5_t* f = (const trexio_hdf5_t*) file;

  char dset_det_name[256] = "determinant_list";

  hsize_t offset[1] = {(hsize_t) offset_file * dims[1]};
  hsize_t count[1]  = {(hsize_t) dims[0] * dims[1]};

  /* Attempt to read determinants (if EOF -> eof_read_size is modified with the number of elements read and return code is TREXIO_END)
     0 argument below is requires to skip internal treatment specific to sparse indices (i.e. their de-compression).*/
  return trexio_hdf5_open_read_dset_sparse(f->determinant_group, dset_det_name, (uint32_t) dims[1], offset, count, eof_read_size, 0, list);
}
   #+end_src

   #+begin_src c :tangle write_determinant_hdf5.c
trexio_exit_code trexio_hdf5_write_determinant_list(trexio_t* const file,
                                                    const int64_t offset_file,
		                                    const uint32_t rank,
                                                    const uint64_t* dims,
                                                    const int64_t* list)
{

  if (file == NULL) return TREXIO_INVALID_ARG_1;
  if (list == NULL) return TREXIO_INVALID_ARG_5;

  trexio_hdf5_t* f = (trexio_hdf5_t*) file;

  hid_t det_dtype = H5T_NATIVE_INT64;
  uint64_t size_ranked = dims[1]*dims[0];

  /* Arrays of chunk dims that will be used for chunking the dataset */
  const hsize_t chunk_dims[1] = {size_ranked};

  /* Indices and values are stored as 2 independent datasets in the HDF5 file */
  char dset_det_name[256] = "determinant_list";

  trexio_exit_code rc_write = TREXIO_FAILURE;
  /* NOTE: chunk size is set upon creation of the HDF5 dataset and cannot be changed ! */
  if ( trexio_hdf5_has_determinant_list(file) == TREXIO_HAS_NOT ) {
  /* If the file does not exist -> create it and write */

  /* Create chunked dataset with det_dtype datatype and write indices into it */
    rc_write = trexio_hdf5_create_write_dset_sparse(f->determinant_group, dset_det_name, det_dtype, chunk_dims, list);
    if (rc_write != TREXIO_SUCCESS) return rc_write;

  } else {
  /* If the file exists -> open it and write */
    hsize_t offset_data[1] = {(hsize_t) offset_file * dims[1]};

  /* Create chunked dataset with det_dtype datatype and write indices into it */
    rc_write = trexio_hdf5_open_write_dset_sparse(f->determinant_group, dset_det_name, det_dtype, chunk_dims, offset_data, list);
    if (rc_write != TREXIO_SUCCESS) return rc_write;

  }

  return TREXIO_SUCCESS;
}
   #+end_src

   #+begin_src c :tangle has_determinant_hdf5.c
trexio_exit_code trexio_hdf5_has_determinant_list(trexio_t* const file)
{
  if (file == NULL) return TREXIO_INVALID_ARG_1;

  trexio_hdf5_t* f = (trexio_hdf5_t*) file;
  if (f->determinant_group == (hsize_t) 0) return TREXIO_HAS_NOT;

  htri_t exists = H5Lexists(f->determinant_group, "determinant_list", H5P_DEFAULT);
  if (exists > 0) {
    return TREXIO_SUCCESS;
  } else if (exists < 0) {
    return TREXIO_FAILURE;
  } else {
    return TREXIO_HAS_NOT;
  }
}
   #+end_src

* Helper functions

  #+begin_src c :tangle helpers_hdf5.c
trexio_exit_code
trexio_hdf5_create_write_dset_sparse (const hid_t group_id,
                                      const char* dset_name,
                                      const hid_t dtype_id,
                                      const hsize_t* chunk_dims,
                                      const void* data_sparse)
{
  const int h5_rank = 1;
  const hsize_t maxdims[1] = {H5S_UNLIMITED};

  hid_t dspace = H5Screate_simple(h5_rank, chunk_dims, maxdims);
  if (dspace < 0) return TREXIO_INVALID_ID;

  hid_t prop = H5Pcreate(H5P_DATASET_CREATE);
  if (prop < 0) {
    H5Sclose(dspace);
    return TREXIO_INVALID_ID;
  }

  herr_t status = H5Pset_chunk(prop, h5_rank, chunk_dims);
  if (status < 0) {
    H5Sclose(dspace);
    H5Pclose(prop);
    return TREXIO_INVALID_ID;
  }

  hid_t dset_id = H5Dcreate(group_id,
                            dset_name,
                            dtype_id,
                            dspace,
                            H5P_DEFAULT,
                            prop,
                            H5P_DEFAULT);
  if (dset_id < 0) {
    H5Sclose(dspace);
    H5Pclose(prop);
    return TREXIO_INVALID_ID;
  }

  status = H5Dwrite(dset_id,
                    dtype_id,
                    H5S_ALL, H5S_ALL, H5P_DEFAULT,
                    data_sparse);
  H5Sclose(dspace);
  H5Pclose(prop);
  H5Dclose(dset_id);
  if (status < 0) return TREXIO_FAILURE;

  return TREXIO_SUCCESS;
}


trexio_exit_code
trexio_hdf5_open_write_dset_sparse (const hid_t group_id,
                                    const char* dset_name,
                                    const hid_t dtype_id,
                                    const hsize_t* chunk_dims,
                                    const hsize_t* offset_file,
                                    const void* data_sparse)
{
  const int h5_rank = 1;

  hid_t dset_id = H5Dopen(group_id, dset_name, H5P_DEFAULT);
  if (dset_id <= 0) return TREXIO_INVALID_ID;

  hid_t fspace = H5Dget_space(dset_id);
  if (fspace < 0) {
    H5Dclose(dset_id);
    return TREXIO_INVALID_ID;
  }

  // allocate space for the dimensions to be read
  hsize_t ddims[1] = {0};

  // get the rank and dimensions of the dataset
  int rrank = H5Sget_simple_extent_dims(fspace, ddims, NULL);
  if (rrank != h5_rank) {
    H5Sclose(fspace);
    H5Dclose(dset_id);
    return TREXIO_FAILURE;
  }

  ddims[0] += chunk_dims[0];

  // extend the dset size
  herr_t status  = H5Dset_extent(dset_id, ddims);
  if (status < 0) {
    H5Sclose(fspace);
    H5Dclose(dset_id);
    return TREXIO_INVALID_ID;
  }

  // close and reopen the file dataspace to take into account the extension
  H5Sclose(fspace);
  fspace = H5Dget_space(dset_id);
  if (fspace < 0) {
    H5Dclose(dset_id);
    return TREXIO_INVALID_ID;
  }

  // select hyperslab to be written using chunk_dims and offset values
  status = H5Sselect_hyperslab(fspace, H5S_SELECT_SET, offset_file, NULL, chunk_dims, NULL);
  if (status < 0) {
    H5Sclose(fspace);
    H5Dclose(dset_id);
    return TREXIO_INVALID_ID;
  }

  // create memory dataspace to write from
  hid_t dspace = H5Screate_simple(h5_rank, chunk_dims, NULL);
  if (dspace < 0) {
    H5Sclose(fspace);
    H5Sclose(dspace);
    H5Dclose(dset_id);
    return TREXIO_INVALID_ID;
  }

  status = H5Dwrite(dset_id,
                    dtype_id,
                    dspace, fspace, H5P_DEFAULT,
                    data_sparse);
  H5Dclose(dset_id);
  H5Sclose(dspace);
  H5Sclose(fspace);
  if (status < 0) return TREXIO_FAILURE;

  return TREXIO_SUCCESS;
}


trexio_exit_code
trexio_hdf5_open_read_dset_sparse (const hid_t group_id,
                                   const char* dset_name,
                                   const uint32_t dset_rank,
                                   const hsize_t* offset_file,
                                   hsize_t* const size_read,
                                   int64_t* const eof_read_size,
                                   const int is_index,
                                   void* const data_sparse
                                   )
{
  const int h5_rank = 1;
  if (dset_rank == 0) return TREXIO_INVALID_ARG_3;

  // get the dataset handle
  hid_t dset_id = H5Dopen(group_id, dset_name, H5P_DEFAULT);
  if (dset_id <= 0) return TREXIO_INVALID_ID;

  // get the dataspace of the dataset
  hid_t fspace_id = H5Dget_space(dset_id);
  if (fspace_id < 0) {
    H5Dclose(dset_id);
    return TREXIO_INVALID_ID;
  }

  /* get dims of the dset stored in the file to check whether reading with user-provided chunk size
     will reach end of the dataset (i.e. EOF in TEXT back end)
   ,*/
  hsize_t ddims[1] = {0};
  int rrank = H5Sget_simple_extent_dims(fspace_id, ddims, NULL);
  if (rrank != h5_rank) {
    H5Sclose(fspace_id);
    H5Dclose(dset_id);
    return TREXIO_FAILURE;
  }

  hsize_t max_offset = offset_file[0] + size_read[0];

  int is_EOF = 0;
  // if max_offset exceed current dim of the dset => EOF
  if (max_offset > ddims[0]) {
    is_EOF = 1;
    // lower the value of count to reduce the number of elements which will be read
    size_read[0] -= (max_offset - ddims[0]);
    // modified the value of eof_read_size passed by address
    if (eof_read_size != NULL) *eof_read_size = size_read[0]/dset_rank;
  }

  // special case when reading int indices
  uint64_t size_ranked = (uint64_t) size_read[0];
  void* index_p = NULL;
  // read the datatype from the dataset and compare with the pre-defined values
  hid_t dtype = H5Dget_type(dset_id);
  if (is_index == 1) {
    if (H5Tequal(dtype, H5T_NATIVE_UINT8) > 0) {
      uint8_t* index = CALLOC(size_ranked, uint8_t);
      if (index == NULL) return TREXIO_ALLOCATION_FAILED;
      index_p = index;
    } else if (H5Tequal(dtype, H5T_NATIVE_UINT16) > 0) {
      uint16_t* index = CALLOC(size_ranked, uint16_t);
      if (index == NULL) return TREXIO_ALLOCATION_FAILED;
      index_p = index;
    }
  }

  herr_t status = H5Sselect_hyperslab(fspace_id, H5S_SELECT_SET, offset_file, NULL, size_read, NULL);
  if (status < 0) {
    H5Sclose(fspace_id);
    H5Dclose(dset_id);
    if (index_p != NULL) FREE(index_p);
    return TREXIO_INVALID_ID;
  }

  hid_t memspace_id = H5Screate_simple(h5_rank, size_read, NULL);
  if (memspace_id < 0) {
    H5Sclose(fspace_id);
    H5Dclose(dset_id);
    if (index_p != NULL) FREE(index_p);
    return TREXIO_INVALID_ID;
  }

  if (is_index == 1) {
    if (index_p != NULL) {
      status = H5Dread(dset_id,
                       dtype,
                       memspace_id, fspace_id, H5P_DEFAULT,
                       index_p);
    } else {
      status = H5Dread(dset_id,
                       dtype,
                       memspace_id, fspace_id, H5P_DEFAULT,
                       data_sparse);
    }
  } else {
    status = H5Dread(dset_id,
                     dtype,
                     memspace_id, fspace_id, H5P_DEFAULT,
                     data_sparse);
  }

  H5Sclose(fspace_id);
  H5Sclose(memspace_id);
  H5Dclose(dset_id);
  if (status < 0) {
    if (index_p != NULL) FREE(index_p);
    return TREXIO_FAILURE;
  }

  if (is_index == 1) {
    if (H5Tequal(dtype, H5T_NATIVE_UINT8) > 0) {
      uint8_t* index = (uint8_t*) index_p;
      for (uint64_t i=0; i<size_ranked; ++i){
        ((int32_t*)data_sparse)[i] = (int32_t) index[i];
      }
      FREE(index_p);
    } else if (H5Tequal(dtype, H5T_NATIVE_UINT16) > 0) {
      uint16_t* index = (uint16_t*) index_p;
      for (uint64_t i=0; i<size_ranked; ++i){
        ((int32_t*)data_sparse)[i] = (int32_t) index[i];
      }
      FREE(index_p);
    }
  }

  if (is_EOF == 1) return TREXIO_END;

  return TREXIO_SUCCESS;
}
  #+end_src

* Constant file suffixes (not used by the generator)               :noexport:

  #+begin_src c :tangle suffix_hdf5.h
trexio_exit_code trexio_hdf5_create_write_dset_sparse (const hid_t group_id, const char* dset_name, const hid_t dtype_id, const hsize_t* chunk_dims, const void* data_sparse);
trexio_exit_code trexio_hdf5_open_write_dset_sparse (const hid_t group_id, const char* dset_name, const hid_t dtype_id, const hsize_t* chunk_dims, const hsize_t* offset_file, const void* data_sparse);
trexio_exit_code trexio_hdf5_open_read_dset_sparse (const hid_t group_id, const char* dset_name, const uint32_t dset_rank, const hsize_t* offset_file, hsize_t* const size_read, int64_t* const eof_read_size, const int is_index, void* const data_sparse);

#endif
#endif
  #+end_src

  #+begin_src c :tangle suffix_hdf5.c :noweb yes
#endif
  #+end_src
