#+Title: TREX Input/Ouput library (TREXIO) * File prefixes :noxport: #+NAME:header #+begin_src c /* This file was generated from the trexio.org org-mode file. To generate it, open trexio.org in Emacs and execute M-x org-babel-tangle */ #+end_src #+begin_src c :tangle trexio.h :noweb yes <
> #ifndef _TREXIO_H #define _TREXIO_H #include #+end_src #+begin_src c :tangle trexio.c :noweb yes <
> #include #include #include #include #include #include "trexio.h" #include "trexio_s.h" #include "trexio_text.h" #include "trexio_hdf5.h" /* #include "trexio_json.h" ,*/ #+end_src #+begin_src c :tangle trexio_s.h :noweb yes <
> #ifndef _TREXIO_S_H #define _TREXIO_S_H #include "trexio.h" #include #include #+end_src * Coding conventions - integer types will be defined using types given in ~stdint.h~ - pointers are always initialized to ~NULL~ - when memory is freed, the pointer is set to ~NULL~ - ~assert.h~ should be used extensively - variable names are in lower case - ~#define~ constants are in upper case - structs are suffixed by ~_s~ - types are suffixed by ~_t~ * Front end All calls to TREXIO are thread-safe. ** Error handling #+begin_src c :tangle trexio.h typedef int32_t trexio_exit_code; #define TREXIO_FAILURE ( (trexio_exit_code) -1 ) #define TREXIO_SUCCESS ( (trexio_exit_code) 0 ) #define TREXIO_INVALID_ARG_1 ( (trexio_exit_code) 1 ) #define TREXIO_INVALID_ARG_2 ( (trexio_exit_code) 2 ) #define TREXIO_INVALID_ARG_3 ( (trexio_exit_code) 3 ) #define TREXIO_INVALID_ARG_4 ( (trexio_exit_code) 4 ) #define TREXIO_INVALID_ARG_5 ( (trexio_exit_code) 5 ) #define TREXIO_END ( (trexio_exit_code) 10 ) #define TREXIO_READONLY ( (trexio_exit_code) 11 ) #+end_src ** Back ends #+begin_src c :tangle trexio.h typedef int32_t back_end_t; #define TREXIO_HDF5 ( (back_end_t) 0 ) #define TREXIO_TEXT ( (back_end_t) 1 ) #define TREXIO_JSON ( (back_end_t) 2 ) #define TREXIO_INVALID_BACK_END ( (back_end_t) 3 ) #+end_src ** Read/write behavior Every time a reading function is called, the data is read from the disk. If data needs to be cached, this is left to the user of the library. Writing to TREXIO files is done with transactions (all-or-nothing effect) in a per-group fashion. File writes are attempted by calling explicitly the flush function, or when the TREXIO file is closed. If writing is impossible because the data is not valid, no data is written. The order in which the data is written is not necessarily consistent with the order in which the function calls were made. The TREXIO files are supposed to be opened by only one program at a time: if the same TREXIO file is modified simultaneously by multiple concurrent programs, the behavior is not specified. ** TREXIO file type ~trexio_s~ is the the main type for TREXIO files, visible to the users of the library. This type is kept opaque, and all modifications to the files will be necessarily done through the use of functions, taking such a type as argument. File creation and opening functions will return /TREXIO file handles/, namely pointers to ~trexio_s~ types. All functions accessing to the TREXIO files will have as a first argument the TREXIO file handle. #+begin_src c :tangle trexio.h typedef struct trexio_s trexio_t; #+end_src #+begin_src c :tangle trexio_s.h struct trexio_s { char* file_name; pthread_mutex_t thread_lock; back_end_t back_end; char mode; char padding[7]; /* Ensures the proper alignment of back-ends */ }; #+end_src ** Polymorphism of the file handle 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 ~struct trexio_s~: #+begin_src c struct trexio_back_end_s { trexio_t parent ; /* add below specific back end data */ } #+end_src ** File opening #+begin_src c :tangle trexio.h trexio_t* trexio_open(const char* file_name, const char mode, const back_end_t back_end); #+end_src #+begin_src c :tangle trexio.c trexio_t* trexio_open(const char* file_name, const char mode, const back_end_t back_end) { if (file_name == NULL) return NULL; if (file_name[0] == '\0') return NULL; if (back_end < 0) return NULL; if (back_end >= TREXIO_INVALID_BACK_END) return NULL; if (mode != 'r' && mode != 'w') return NULL; trexio_t* result = NULL; switch (back_end) { case TREXIO_TEXT: result = (trexio_t*) malloc (sizeof(trexio_text_t)); break; case TREXIO_HDF5: result = (trexio_t*) malloc (sizeof(trexio_hdf5_t)); break; /* case TREXIO_JSON: result = (trexio_t*) malloc (sizeof(trexio_json_t)); break; ,*/ } /* TODO: Error handling */ assert (result != NULL); result->file_name = (char*) calloc(strlen(file_name)+1,sizeof(char)); strcpy(result->file_name, file_name); result->back_end = back_end; result->mode = mode; int irc = pthread_mutex_init ( &(result->thread_lock), NULL); assert (irc == 0); trexio_exit_code rc = TREXIO_FAILURE; switch (back_end) { case TREXIO_TEXT: rc = trexio_text_init(result); break; case TREXIO_HDF5: rc = trexio_hdf5_init(result); break; /* case TREXIO_JSON: rc = trexio_json_init(result); break; ,*/ } if (rc != TREXIO_SUCCESS) return NULL; return result; } #+end_src ** File closing #+begin_src c :tangle trexio.h trexio_exit_code trexio_close(trexio_t* file); #+end_src #+begin_src c :tangle trexio.c trexio_exit_code trexio_close(trexio_t* file) { if (file == NULL) return TREXIO_FAILURE; trexio_exit_code rc; switch (file->back_end) { case TREXIO_TEXT: rc = trexio_text_finalize(file); break; case TREXIO_HDF5: rc = trexio_hdf5_finalize(file); break; /* case TREXIO_JSON: rc = trexio_json_finalize(file); break; ,*/ default: assert (1 == 0); /* Impossible case */ } if (rc != TREXIO_SUCCESS) { return TREXIO_FAILURE; } free(file->file_name); file->file_name = NULL; int irc = pthread_mutex_destroy( &(file->thread_lock) ); assert (irc == 0); free(file); return TREXIO_SUCCESS; } #+end_src ** Reading/writing data *** nucleus **** num #+begin_src c :tangle trexio.h trexio_exit_code trexio_read_nucleus_num(trexio_t* file, int64_t* num); trexio_exit_code trexio_write_nucleus_num(trexio_t* file, const int64_t num); #+end_src #+begin_src c :tangle trexio.c trexio_exit_code trexio_read_nucleus_num(trexio_t* file, int64_t* num) { if (file == NULL) return TREXIO_INVALID_ARG_1; uint64_t u_num = 0; trexio_exit_code rc = TREXIO_FAILURE; switch (file->back_end) { case TREXIO_TEXT: rc = trexio_text_read_nucleus_num(file, &u_num); break; case TREXIO_HDF5: rc = trexio_hdf5_read_nucleus_num(file, &u_num); break; /* case TREXIO_JSON: rc =trexio_json_read_nucleus_num(file, &u_num); break; ,*/ } if (rc != TREXIO_SUCCESS) return rc; /**/ *num = (int64_t) u_num; return TREXIO_SUCCESS; } trexio_exit_code trexio_write_nucleus_num(trexio_t* file, const int64_t num) { if (file == NULL) return TREXIO_INVALID_ARG_1; if (num < 0 ) return TREXIO_INVALID_ARG_2; trexio_exit_code rc = TREXIO_FAILURE; switch (file->back_end) { case TREXIO_TEXT: rc = trexio_text_write_nucleus_num(file, (uint64_t) num); break; case TREXIO_HDF5: rc = trexio_hdf5_write_nucleus_num(file, (uint64_t) num); break; /* case TREXIO_JSON: rc = trexio_json_write_nucleus_num(file, (uint64_t) num); break; ,*/ } if (rc != TREXIO_SUCCESS) return rc; return TREXIO_SUCCESS; } #+end_src **** coord #+begin_src c :tangle trexio.h trexio_exit_code trexio_read_nucleus_coord(trexio_t* file, double* coord); trexio_exit_code trexio_write_nucleus_coord(trexio_t* file, const double* coord); #+end_src #+begin_src c :tangle trexio.c trexio_exit_code trexio_read_nucleus_coord(trexio_t* file, double* coord) { if (file == NULL) return TREXIO_INVALID_ARG_1; if (coord == NULL) return TREXIO_INVALID_ARG_2; int64_t nucleus_num = -1; trexio_exit_code rc = trexio_read_nucleus_num(file, &nucleus_num); if (rc != TREXIO_SUCCESS) return rc; int64_t dim_coord = nucleus_num*3; if (dim_coord < 0) return TREXIO_FAILURE; switch (file->back_end) { case TREXIO_TEXT: return trexio_text_read_nucleus_coord(file, coord, (uint64_t) dim_coord); break; case TREXIO_HDF5: return trexio_hdf5_read_nucleus_coord(file, coord); break; /* case TREXIO_JSON: return trexio_json_read_nucleus_coord(file, coord); break; ,*/ default: return TREXIO_FAILURE; /* Impossible case */ } } trexio_exit_code trexio_write_nucleus_coord(trexio_t* file, const double* coord) { if (file == NULL) return TREXIO_INVALID_ARG_1; if (coord == NULL) return TREXIO_INVALID_ARG_2; int64_t nucleus_num = -1; trexio_exit_code rc = trexio_read_nucleus_num(file, &nucleus_num); if (rc != TREXIO_SUCCESS) return rc; int64_t dim_coord = nucleus_num*3; if (dim_coord < 0) return TREXIO_FAILURE; switch (file->back_end) { case TREXIO_TEXT: return trexio_text_write_nucleus_coord(file, coord, (uint64_t) dim_coord); break; case TREXIO_HDF5: return trexio_hdf5_write_nucleus_coord(file, coord); break; /* case TREXIO_JSON: return trexio_json_write_nucleus_coord(file, coord); break; ,*/ default: return TREXIO_FAILURE; /* Impossible case */ } } #+end_src **** charge #+begin_src c :tangle trexio.h trexio_exit_code trexio_read_nucleus_charge(trexio_t* file, double* charge); trexio_exit_code trexio_write_nucleus_charge(trexio_t* file, const double* charge); #+end_src #+begin_src c :tangle trexio.c trexio_exit_code trexio_read_nucleus_charge(trexio_t* file, double* charge) { if (file == NULL) return TREXIO_INVALID_ARG_1; if (charge == NULL) return TREXIO_INVALID_ARG_2; int64_t nucleus_num = -1; trexio_exit_code rc = trexio_read_nucleus_num(file, &nucleus_num); if (rc != TREXIO_SUCCESS) return rc; int64_t dim_charge = nucleus_num; if (dim_charge < 0) return TREXIO_FAILURE; switch (file->back_end) { case TREXIO_TEXT: return trexio_text_read_nucleus_charge(file, charge, (uint64_t) dim_charge); break; /* case TREXIO_HDF5: return trexio_hdf5_read_nucleus_charge(file, charge); break; case TREXIO_JSON: return trexio_json_read_nucleus_charge(file, charge); break; ,*/ default: return TREXIO_FAILURE; /* Impossible case */ } } trexio_exit_code trexio_write_nucleus_charge(trexio_t* file, const double* charge) { if (file == NULL) return TREXIO_INVALID_ARG_1; if (charge == NULL) return TREXIO_INVALID_ARG_2; int64_t nucleus_num = -1; trexio_exit_code rc = trexio_read_nucleus_num(file, &nucleus_num); if (rc != TREXIO_SUCCESS) return rc; int64_t dim_charge = nucleus_num; if (dim_charge < 0) return TREXIO_FAILURE; switch (file->back_end) { case TREXIO_TEXT: return trexio_text_write_nucleus_charge(file, charge, (uint64_t) dim_charge); break; /* case TREXIO_HDF5: return trexio_hdf5_write_nucleus_charge(file, charge); break; case TREXIO_JSON: return trexio_json_write_nucleus_charge(file, charge); break; ,*/ default: return TREXIO_FAILURE; /* Impossible case */ } } #+end_src *** rdm **** one_e #+begin_src c :tangle trexio.h trexio_exit_code trexio_read_rdm_one_e(trexio_t* file, double* one_e); trexio_exit_code trexio_write_rdm_one_e(trexio_t* file, const double* one_e); #+end_src #+begin_src c :tangle trexio.c trexio_exit_code trexio_read_rdm_one_e(trexio_t* file, double* one_e) { if (file == NULL) return TREXIO_INVALID_ARG_1; if (one_e == NULL) return TREXIO_INVALID_ARG_2; int64_t dim_one_e = -1; trexio_exit_code rc = trexio_read_nucleus_num(file, &dim_one_e); /* This dimension is wrong. Should be mo_num */ if (rc != TREXIO_SUCCESS) return rc; if (dim_one_e < 0) return TREXIO_FAILURE; switch (file->back_end) { case TREXIO_TEXT: return trexio_text_read_rdm_one_e(file, one_e, (uint64_t) dim_one_e); break; /* case TREXIO_HDF5: return trexio_hdf5_read_rdm_one_e(file, one_e); break; case TREXIO_JSON: return trexio_json_read_rdm_one_e(file, one_e); break; ,*/ default: return TREXIO_FAILURE; /* Impossible case */ } } trexio_exit_code trexio_write_rdm_one_e(trexio_t* file, const double* one_e) { if (file == NULL) return TREXIO_INVALID_ARG_1; if (one_e == NULL) return TREXIO_INVALID_ARG_2; int64_t nucleus_num = -1; trexio_exit_code rc = trexio_read_nucleus_num(file, &nucleus_num); if (rc != TREXIO_SUCCESS) return rc; int64_t dim_one_e = nucleus_num * nucleus_num; /* This dimension is wrong. Should be mo_num */ if (dim_one_e < 0) return TREXIO_FAILURE; switch (file->back_end) { case TREXIO_TEXT: return trexio_text_write_rdm_one_e(file, one_e, (uint64_t) dim_one_e); break; /* case TREXIO_HDF5: return trexio_hdf5_write_rdm_one_e(file, one_e); break; case TREXIO_JSON: return trexio_json_write_rdm_one_e(file, one_e); break; ,*/ default: return TREXIO_FAILURE; /* Impossible case */ } } #+end_src **** two_e ~buffered_read~ functions return ~TREXIO_SUCCESS~ if the complete buffer was read or written. If the read data is smaller than the buffer because the end is reached, the function returns ~TREXIO_END~. #+begin_src c :tangle trexio.h trexio_exit_code trexio_buffered_read_rdm_two_e(trexio_t* file, const int64_t offset, const int64_t size, int64_t* index, double* value); trexio_exit_code trexio_buffered_write_rdm_two_e(trexio_t* file, const int64_t offset, const int64_t size, const int64_t* index, const double* value); #+end_src #+begin_src c :tangle trexio.c trexio_exit_code trexio_buffered_read_rdm_two_e(trexio_t* file, const int64_t offset, const int64_t size, int64_t* index, double* value) { if (file == NULL) return TREXIO_INVALID_ARG_1; if (offset <= 0 ) return TREXIO_INVALID_ARG_2; if (size <= 0 ) return TREXIO_INVALID_ARG_3; if (index == NULL) return TREXIO_INVALID_ARG_4; if (value == NULL) return TREXIO_INVALID_ARG_5; switch (file->back_end) { case TREXIO_TEXT: return trexio_text_buffered_read_rdm_two_e(file, (uint64_t) offset, (uint64_t) size, index, value); break; /* case TREXIO_HDF5: return trexio_hdf5_buffered_read_rdm_two_e(file, size); break; case TREXIO_JSON: return trexio_json_buffered_read_rdm_two_e(file, size); break; ,*/ default: return TREXIO_FAILURE; /* Impossible case */ } } trexio_exit_code trexio_buffered_write_rdm_two_e(trexio_t* file, const int64_t offset, const int64_t size, const int64_t* index, const double* value) { if (file == NULL) return TREXIO_INVALID_ARG_1; if (offset <= 0 ) return TREXIO_INVALID_ARG_2; if (size <= 0 ) return TREXIO_INVALID_ARG_3; if (index == NULL) return TREXIO_INVALID_ARG_4; if (value == NULL) return TREXIO_INVALID_ARG_5; switch (file->back_end) { case TREXIO_TEXT: return trexio_text_buffered_write_rdm_two_e(file, (uint64_t) offset, (uint64_t) size, index, value); break; /* case TREXIO_HDF5: return trexio_hdf5_buffered_write_rdm_two_e(file, size); break; case TREXIO_JSON: return trexio_json_buffered_write_rdm_two_e(file, size); break; ,*/ default: return TREXIO_FAILURE; /* Impossible case */ } } #+end_src * Back ends TREXIO has multiple possible back ends: - HDF5: The most efficient back-end, by default - Text files: not to be used for production, but useful for debugging - JSON: for portability * File suffixes :noxport: #+begin_src c :tangle trexio.h #endif #+end_src #+begin_src c :tangle trexio_s.h #endif #+end_src * TODO Things to be done :noexport: - [ ] Thread safety - [ ] Error handling with errno - [ ] HDF5 back-end - [ ] JSON back-end - [ ] File locking with flock - [ ] Caching of the struct saving last modification date in structs