star-hitran

Load line-by-line data from the HITRAN database
git clone git://git.meso-star.fr/star-hitran.git
Log | Files | Refs | README | LICENSE

shtr_isotope_metadata.c (26461B)


      1 /* Copyright (C) 2022, 2025, 2026 |Méso|Star> (contact@meso-star.com)
      2  * Copyright (C) 2025, 2026 Université de Lorraine
      3  * Copyright (C) 2022 Centre National de la Recherche Scientifique
      4  * Copyright (C) 2022 Université Paul Sabatier
      5  *
      6  * This program is free software: you can redistribute it and/or modify
      7  * it under the terms of the GNU General Public License as published by
      8  * the Free Software Foundation, either version 3 of the License, or
      9  * (at your option) any later version.
     10  *
     11  * This program is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     14  * GNU General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU General Public License
     17  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     18 
     19 #define _POSIX_C_SOURCE 200112L /* strtok_r support */
     20 
     21 #include "shtr.h"
     22 #include "shtr_c.h"
     23 #include "shtr_param.h"
     24 
     25 #include <rsys/cstr.h>
     26 #include <rsys/dynamic_array_char.h>
     27 #include <rsys/ref_count.h>
     28 #include <rsys/str.h>
     29 #include <rsys/text_reader.h>
     30 
     31 #include <ctype.h>
     32 #include <string.h>
     33 
     34 /* Version of the isotope metadata. One should increment it and perform a
     35  * version management onto serialized data when the isotope metadata structure
     36  * is updated. */
     37 static const int SHTR_ISOTOPE_METADATA_VERSION = 0;
     38 
     39 /* Generate the dynamic array of isotopes */
     40 #define DARRAY_NAME isotope
     41 #define DARRAY_DATA struct shtr_isotope
     42 #include <rsys/dynamic_array.h>
     43 
     44 struct molecule {
     45   struct str name;
     46   size_t isotopes_range[2]; /* Range of the [1st and last[ isotopes */
     47   int id; /* Unique identifier of the molecule */
     48 };
     49 #define MOLECULE_IS_VALID(Molecule) ((Molecule)->id >= 0)
     50 
     51 static INLINE void
     52 molecule_clear(struct molecule* molecule)
     53 {
     54   ASSERT(molecule);
     55   molecule->isotopes_range[0] = SIZE_MAX;
     56   molecule->isotopes_range[1] = 0;
     57   molecule->id = -1;
     58 }
     59 
     60 static INLINE void
     61 molecule_init(struct mem_allocator* allocator, struct molecule* molecule)
     62 {
     63   str_init(allocator, &molecule->name);
     64   molecule_clear(molecule);
     65 }
     66 
     67 static INLINE void
     68 molecule_release(struct molecule* molecule)
     69 {
     70   str_release(&molecule->name);
     71 }
     72 
     73 static INLINE res_T
     74 molecule_copy(struct molecule* dst, const struct molecule* src)
     75 {
     76   dst->isotopes_range[0] = src->isotopes_range[0];
     77   dst->isotopes_range[1] = src->isotopes_range[1];
     78   dst->id = src->id;
     79   return str_copy(&dst->name, &src->name);
     80 }
     81 
     82 static INLINE res_T
     83 molecule_copy_and_release(struct molecule* dst, struct molecule* src)
     84 {
     85   dst->isotopes_range[0] = src->isotopes_range[0];
     86   dst->isotopes_range[1] = src->isotopes_range[1];
     87   dst->id = src->id;
     88   return str_copy_and_release(&dst->name, &src->name);
     89 }
     90 
     91 /* Generate the dynamic array of molecules */
     92 #define DARRAY_NAME molecule
     93 #define DARRAY_DATA struct molecule
     94 #define DARRAY_FUNCTOR_INIT molecule_init
     95 #define DARRAY_FUNCTOR_RELEASE molecule_release
     96 #define DARRAY_FUNCTOR_COPY molecule_copy
     97 #define DARRAY_FUNCTOR_COPY_AND_RELEASE molecule_copy_and_release
     98 #include <rsys/dynamic_array.h>
     99 
    100 struct shtr_isotope_metadata {
    101   /* List of molecules and isotopes */
    102   struct darray_molecule molecules;
    103   struct darray_isotope isotopes;
    104 
    105   /* Map the global identifier of a molecule to its correspond local index into
    106    * the dynamic array into which it is registered */
    107   int molid2idx[SHTR_MAX_MOLECULES_COUNT];
    108 
    109   struct shtr* shtr;
    110   ref_T ref;
    111 };
    112 
    113 /*******************************************************************************
    114  * Helper functions
    115  ******************************************************************************/
    116 static res_T
    117 create_isotope_metadata
    118   (struct shtr* shtr,
    119    struct shtr_isotope_metadata** out_isotopes)
    120 {
    121   struct shtr_isotope_metadata* metadata = NULL;
    122   res_T res = RES_OK;
    123   ASSERT(shtr && out_isotopes);
    124 
    125   metadata = MEM_CALLOC(shtr->allocator, 1, sizeof(*metadata));
    126   if(!metadata) {
    127     ERROR(shtr,
    128       "Could not allocate the isotope metadata data structure.\n");
    129     res = RES_MEM_ERR;
    130     goto error;
    131   }
    132   ref_init(&metadata->ref);
    133   SHTR(ref_get(shtr));
    134   metadata->shtr = shtr;
    135   darray_molecule_init(shtr->allocator, &metadata->molecules);
    136   darray_isotope_init(shtr->allocator, &metadata->isotopes);
    137   memset(metadata->molid2idx, 0xFF, sizeof(metadata->molid2idx));
    138 
    139 exit:
    140   *out_isotopes = metadata;
    141   return res;
    142 error:
    143   if(metadata) {
    144     SHTR(isotope_metadata_ref_put(metadata));
    145     metadata = NULL;
    146   }
    147   goto exit;
    148 }
    149 
    150 static res_T
    151 flush_molecule
    152   (struct shtr_isotope_metadata* metadata,
    153    struct molecule* molecule, /* Currently parsed molecule */
    154    struct txtrdr* txtrdr)
    155 {
    156   size_t ientry = SIZE_MAX;
    157   size_t* pimolecule = NULL;
    158   res_T res = RES_OK;
    159   ASSERT(metadata && molecule && MOLECULE_IS_VALID(molecule));
    160 
    161   /* Fetch _exclusive_ upper bound */
    162   molecule->isotopes_range[1] =
    163     darray_isotope_size_get(&metadata->isotopes);
    164   if(molecule->isotopes_range[0] >= molecule->isotopes_range[1]) {
    165     WARN(metadata->shtr,
    166       "%s:%lu: the %s molecule does not have any isotope.\n",
    167       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    168       str_cget(&molecule->name));
    169   }
    170 
    171   /* Fetch the index where the molecule is going to be store */
    172   ientry = darray_molecule_size_get(&metadata->molecules);
    173   CHK(ientry < SHTR_MAX_MOLECULES_COUNT);
    174 
    175   /* Store the molecule */
    176   res = darray_molecule_push_back(&metadata->molecules, molecule);
    177   if(res != RES_OK) {
    178     ERROR(metadata->shtr,
    179       "%s: error storing the %s molecule -- %s.\n",
    180       txtrdr_get_name(txtrdr), str_cget(&molecule->name), res_to_cstr(res));
    181     goto error;
    182   }
    183 
    184   /* Register the molecule */
    185   if(metadata->molid2idx[molecule->id] >= 0) {
    186     const struct molecule* molecule2 = NULL;
    187     molecule2 = darray_molecule_cdata_get(&metadata->molecules) + *pimolecule;
    188     ERROR(metadata->shtr,
    189       "%s: cannot register the %s molecule. "
    190       "The %s molecule has the same identifier %i.\n",
    191       txtrdr_get_name(txtrdr),
    192       str_cget(&molecule->name),
    193       str_cget(&molecule2->name),
    194       molecule->id);
    195     res = RES_OK;
    196     goto error;
    197   }
    198   ASSERT((size_t)((int)ientry) == ientry);
    199   metadata->molid2idx[molecule->id] = (int)ientry;
    200   molecule_clear(molecule);
    201 
    202 exit:
    203   return res;
    204 error:
    205   if(ientry != SIZE_MAX) darray_molecule_resize(&metadata->molecules, ientry);
    206   metadata->molid2idx[molecule->id] = -1;
    207   goto exit;
    208 }
    209 
    210 static res_T
    211 parse_molecule
    212   (struct shtr_isotope_metadata* metadata,
    213    struct molecule* molecule,
    214    struct txtrdr* txtrdr)
    215 {
    216   char* name = NULL;
    217   char* id = NULL;
    218   char* tk = NULL;
    219   char* tk_ctx = NULL;
    220   size_t len;
    221   res_T res = RES_OK;
    222   ASSERT(molecule && txtrdr);
    223 
    224   name = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx);
    225   id = strtok_r(NULL, " \t", &tk_ctx);
    226 
    227   if(!name) {
    228     ERROR(metadata->shtr, "%s:%lu: molecule name is missing.\n",
    229       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
    230     res = RES_BAD_ARG;
    231     goto error;
    232   }
    233 
    234   res = str_set(&molecule->name, name);
    235   if(res != RES_OK) {
    236     ERROR(metadata->shtr,
    237       "%s:%lu: error seting the molecule name `%s' -- %s.\n",
    238       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    239       name, res_to_cstr(res));
    240     goto error;
    241   }
    242 
    243   len = strlen(id);
    244   if(!id || !len || id[0] != '(' || id[len-1] != ')') {
    245     ERROR(metadata->shtr,
    246       "%s:%lu: invalid definition of the molecule identifier.\n",
    247       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
    248     res = RES_BAD_ARG;
    249     goto error;
    250   }
    251 
    252   id[len-1] = '\0'; /* Rm trailing parenthesis */
    253   res = cstr_to_int(id+1/*Rm leading parenthesis*/, &molecule->id);
    254   if(res != RES_OK || !MOLECULE_IS_VALID(molecule)) {
    255     id[len-1] = ')'; /* Re-add the trailing parenthesis */
    256     ERROR(metadata->shtr, "%s:%lu: invalid molecule identifier `%s'.\n",
    257       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), id);
    258     res = RES_BAD_ARG;
    259     goto error;
    260   }
    261 
    262   tk = strtok_r(NULL, " \t", &tk_ctx);
    263   if(tk) {
    264     WARN(metadata->shtr, "%s:%lu: unexpected text `%s'.\n",
    265       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    266   }
    267 
    268   molecule->isotopes_range[0] = darray_isotope_size_get(&metadata->isotopes);
    269 
    270 exit:
    271   return res;
    272 error:
    273   goto exit;
    274 }
    275 
    276 static res_T
    277 parse_isotope
    278   (struct shtr_isotope_metadata* metadata,
    279    struct txtrdr* txtrdr)
    280 {
    281   struct shtr_isotope isotope = SHTR_ISOTOPE_NULL;
    282   struct param_desc param_desc = PARAM_DESC_NULL;
    283   struct shtr* shtr = NULL;
    284   char* tk = NULL;
    285   char* tk_ctx = NULL;
    286   size_t local_id = SIZE_MAX;
    287   res_T res = RES_OK;
    288   ASSERT(metadata && txtrdr);
    289 
    290   shtr = metadata->shtr;
    291   param_desc.path = txtrdr_get_name(txtrdr);
    292   param_desc.line = txtrdr_get_line_num(txtrdr);
    293 
    294   /* Fetch the index of the molecule to which the isotope belongs */
    295   isotope.molecule_id_local = darray_molecule_size_get(&metadata->molecules);
    296 
    297   tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx);
    298   param_desc.name = "isotope id";
    299   param_desc.low = 0;
    300   param_desc.upp = INT_MAX;
    301   param_desc.is_low_incl = 1;
    302   param_desc.is_upp_incl = 1;
    303   res = parse_param_int(shtr, tk, &param_desc, &isotope.id);
    304   if(res != RES_OK) goto error;
    305 
    306   tk = strtok_r(NULL, " \t", &tk_ctx);
    307   param_desc.name = "isotope abundance";
    308   param_desc.low = 0;
    309   param_desc.upp = 1;
    310   param_desc.is_low_incl = 0;
    311   param_desc.is_upp_incl = 1;
    312   res = parse_param_double(shtr, tk, &param_desc, &isotope.abundance);
    313   if(res != RES_OK) goto error;
    314 
    315   tk = strtok_r(NULL, " \t", &tk_ctx);
    316   param_desc.name = "isotope Q(296K)";
    317   param_desc.low = 0;
    318   param_desc.upp = INF;
    319   param_desc.is_low_incl = 0;
    320   param_desc.is_upp_incl = 1;
    321   res = parse_param_double(shtr, tk, &param_desc, &isotope.Q296K);
    322   if(res !=  RES_OK) goto error;
    323 
    324   tk = strtok_r(NULL, " \t", &tk_ctx);
    325   param_desc.name = "isotope state independant degeneracy factor";
    326   param_desc.low = -INT_MAX;
    327   param_desc.upp =  INT_MAX;
    328   param_desc.is_low_incl = 1;
    329   param_desc.is_upp_incl = 1;
    330   res = parse_param_int(shtr, tk, &param_desc, &isotope.gj);
    331   if(res != RES_OK) goto error;
    332 
    333   tk = strtok_r(NULL, " \t", &tk_ctx),
    334   param_desc.name = "isotope molar mass";
    335   param_desc.low = 0;
    336   param_desc.upp = INF;
    337   param_desc.is_low_incl = 0;
    338   param_desc.is_upp_incl = 1;
    339   res = parse_param_double(shtr, tk, &param_desc, &isotope.molar_mass);
    340   if(res != RES_OK) goto error;
    341 
    342   local_id = darray_isotope_size_get(&metadata->isotopes);
    343 
    344   /* Store the isotope */
    345   res = darray_isotope_push_back(&metadata->isotopes, &isotope);
    346   if(res != RES_OK) {
    347     ERROR(shtr, "%s:%lu: error storing the isotope %d -- %s.\n",
    348       param_desc.path, param_desc.line, isotope.id, res_to_cstr(res));
    349     res = RES_OK;
    350     goto error;
    351   }
    352 
    353 exit:
    354   return res;
    355 error:
    356   if(local_id != SIZE_MAX) darray_isotope_resize(&metadata->isotopes, local_id);
    357   goto exit;
    358 }
    359 
    360 static res_T
    361 parse_line
    362   (struct shtr_isotope_metadata* metadata,
    363    struct molecule* molecule, /* Currently parsed molecule */
    364    struct txtrdr* txtrdr)
    365 {
    366   const char* line = NULL;
    367   size_t i;
    368   res_T res = RES_OK;
    369   ASSERT(metadata && molecule && txtrdr);
    370 
    371   line = txtrdr_get_cline(txtrdr);
    372   ASSERT(line);
    373   i = strspn(line, " \t");
    374   ASSERT(i < strlen(line));
    375 
    376   if(isalpha(line[i])) {
    377     if(MOLECULE_IS_VALID(molecule)) {
    378       res = flush_molecule(metadata, molecule, txtrdr);
    379       if(res != RES_OK) goto error;
    380     }
    381     res = parse_molecule(metadata, molecule, txtrdr);
    382     if(res != RES_OK) goto error;
    383   } else {
    384     if(!MOLECULE_IS_VALID(molecule)) {
    385       ERROR(metadata->shtr, "%s:%lu: missing a molecule definition.\n",
    386         txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
    387       res = RES_BAD_ARG;
    388       goto error;
    389     }
    390     res = parse_isotope(metadata, txtrdr);
    391     if(res != RES_OK) goto error;
    392   }
    393 
    394 exit:
    395   return res;
    396 error:
    397   goto exit;
    398 }
    399 
    400 static res_T
    401 load_stream
    402   (struct shtr* shtr,
    403    FILE* stream,
    404    const char* name,
    405    struct shtr_isotope_metadata** out_isotopes)
    406 {
    407   struct molecule molecule; /* Current molecule */
    408   struct shtr_isotope_metadata* metadata = NULL;
    409   struct txtrdr* txtrdr = NULL;
    410   res_T res = RES_OK;
    411   ASSERT(shtr && stream && name && out_isotopes);
    412 
    413   molecule_init(shtr->allocator, &molecule);
    414 
    415   res = create_isotope_metadata(shtr, &metadata);
    416   if(res != RES_OK) goto error;
    417 
    418   res = txtrdr_stream(metadata->shtr->allocator, stream, name,
    419     0/*No comment char*/, &txtrdr);
    420   if(res != RES_OK) {
    421     ERROR(shtr, "%s: error creating the text reader -- %s.\n",
    422       name, res_to_cstr(res));
    423     goto error;
    424   }
    425 
    426   #define READ_LINE {                                                          \
    427     res = txtrdr_read_line(txtrdr);                                            \
    428     if(res != RES_OK) {                                                        \
    429       ERROR(shtr, "%s: error reading the line `%lu' -- %s.\n",               \
    430         name, (unsigned long)txtrdr_get_line_num(txtrdr), res_to_cstr(res));   \
    431       goto error;                                                              \
    432     }                                                                          \
    433   } (void)0
    434 
    435   /* Skip the 1st line that is a comment line */
    436   READ_LINE;
    437   if(!txtrdr_get_cline(txtrdr)) goto exit;
    438 
    439   for(;;) {
    440     READ_LINE;
    441 
    442     if(!txtrdr_get_cline(txtrdr)) break; /* No more parsed line */
    443     res = parse_line(metadata, &molecule, txtrdr);
    444     if(res != RES_OK) goto error;
    445   }
    446   #undef READ_LINE
    447 
    448   if(MOLECULE_IS_VALID(&molecule)) {
    449     res = flush_molecule(metadata, &molecule, txtrdr);
    450     if(res != RES_OK) goto error;
    451   }
    452 
    453 exit:
    454   if(txtrdr) txtrdr_ref_put(txtrdr);
    455   *out_isotopes = metadata;
    456   molecule_release(&molecule);
    457   return res;
    458 error:
    459   if(metadata) {
    460     SHTR(isotope_metadata_ref_put(metadata));
    461     metadata = NULL;
    462   }
    463   goto exit;
    464 }
    465 
    466 static res_T
    467 write_molecules
    468   (const struct shtr_isotope_metadata* metadata,
    469    const char* caller,
    470    FILE* stream)
    471 {
    472   const struct molecule* molecules = NULL;
    473   size_t i, nmolecules;
    474   res_T res = RES_OK;
    475   ASSERT(metadata && caller && stream);
    476 
    477   molecules = darray_molecule_cdata_get(&metadata->molecules);
    478   nmolecules = darray_molecule_size_get(&metadata->molecules);
    479 
    480   #define WRITE(Var, Nb) {                                                     \
    481     if(fwrite((Var), sizeof(*(Var)), (Nb), stream) != (Nb)) {                  \
    482       res = RES_IO_ERR;                                                        \
    483       goto error;                                                              \
    484     }                                                                          \
    485   } (void)0
    486   WRITE(&nmolecules, 1);
    487 
    488   FOR_EACH(i, 0, nmolecules) {
    489     const struct molecule* molecule = molecules + i;
    490     const size_t len = str_len(&molecule->name);
    491     WRITE(&len, 1);
    492     WRITE(str_cget(&molecule->name), len);
    493     WRITE(molecule->isotopes_range, 2);
    494     WRITE(&molecule->id, 1);
    495   }
    496   #undef WRITE
    497 
    498 exit:
    499   return res;
    500 error:
    501   ERROR(metadata->shtr, "%s: error writing molecules\n", caller);
    502   goto exit;
    503 }
    504 
    505 static res_T
    506 read_molecules
    507   (struct shtr_isotope_metadata* metadata,
    508    const char* caller,
    509    FILE* stream)
    510 {
    511   struct darray_char name;
    512   struct molecule* molecules = NULL;
    513   size_t i, nmolecules;
    514   res_T res = RES_OK;
    515   ASSERT(metadata && caller && stream);
    516 
    517   darray_char_init(metadata->shtr->allocator, &name);
    518 
    519   #define READ(Var, Nb) {                                                      \
    520     if(fread((Var), sizeof(*(Var)), (Nb), stream) != (Nb)) {                   \
    521       if(feof(stream)) {                                                       \
    522         res = RES_BAD_ARG;                                                     \
    523       } else if(ferror(stream)) {                                              \
    524         res = RES_IO_ERR;                                                      \
    525       } else {                                                                 \
    526         res = RES_UNKNOWN_ERR;                                                 \
    527       }                                                                        \
    528       goto error;                                                              \
    529     }                                                                          \
    530   } (void)0
    531 
    532   READ(&nmolecules, 1);
    533 
    534   res = darray_molecule_resize(&metadata->molecules, nmolecules);
    535   if(res != RES_OK) goto error;
    536 
    537   molecules = darray_molecule_data_get(&metadata->molecules);
    538   FOR_EACH(i, 0, nmolecules) {
    539     struct molecule* molecule = molecules + i;
    540     size_t len = 0;
    541 
    542     READ(&len, 1);
    543     res = darray_char_resize(&name, len+1);
    544     if(res != RES_OK) goto error;
    545 
    546     READ(darray_char_data_get(&name), len);
    547     darray_char_data_get(&name)[len] = '\0';
    548     res = str_set(&molecule->name, darray_char_data_get(&name));
    549     if(res != RES_OK) goto error;
    550 
    551     READ(molecule->isotopes_range, 2);
    552     READ(&molecule->id, 1);
    553   }
    554   #undef READ
    555 
    556 exit:
    557   darray_char_release(&name);
    558   return res;
    559 error:
    560   ERROR(metadata->shtr, "%s: error reading molecules -- %s.\n",
    561     caller, res_to_cstr(res));
    562   goto exit;
    563 }
    564 
    565 static res_T
    566 write_isotopes
    567   (const struct shtr_isotope_metadata* metadata,
    568    const char* caller,
    569    FILE* stream)
    570 {
    571   const struct shtr_isotope* isotopes = NULL;
    572   size_t nisotopes = 0;
    573   res_T res = RES_OK;
    574   ASSERT(metadata && caller && stream);
    575 
    576   isotopes = darray_isotope_cdata_get(&metadata->isotopes);
    577   nisotopes = darray_isotope_size_get(&metadata->isotopes);
    578 
    579   #define WRITE(Var, Nb) {                                                     \
    580     if(fwrite((Var), sizeof(*(Var)), (Nb), stream) != (Nb)) {                  \
    581       ERROR(metadata->shtr, "%s: error writing isotopes\n", caller);         \
    582       res = RES_IO_ERR;                                                        \
    583       goto error;                                                              \
    584     }                                                                          \
    585   } (void)0
    586   WRITE(&nisotopes, 1);
    587   WRITE(isotopes, nisotopes);
    588   #undef WRITE
    589 
    590 exit:
    591   return res;
    592 error:
    593   goto exit;
    594 }
    595 
    596 static res_T
    597 read_isotopes
    598   (struct shtr_isotope_metadata* metadata,
    599    const char* caller,
    600    FILE* stream)
    601 {
    602   size_t nisotopes = 0;
    603   res_T res = RES_OK;
    604   ASSERT(metadata && caller && stream);
    605 
    606   #define READ(Var, Nb) {                                                      \
    607     if(fread((Var), sizeof(*(Var)), (Nb), stream) != (Nb)) {                   \
    608       if(feof(stream)) {                                                       \
    609         res = RES_BAD_ARG;                                                     \
    610       } else if(ferror(stream)) {                                              \
    611         res = RES_IO_ERR;                                                      \
    612       } else {                                                                 \
    613         res = RES_UNKNOWN_ERR;                                                 \
    614       }                                                                        \
    615       goto error;                                                              \
    616     }                                                                          \
    617   } (void)0
    618   READ(&nisotopes, 1);
    619   res = darray_isotope_resize(&metadata->isotopes, nisotopes);
    620   if(res != RES_OK) goto error;
    621   READ(darray_isotope_data_get(&metadata->isotopes), nisotopes);
    622   #undef READ
    623 
    624 exit:
    625   return res;
    626 error:
    627   ERROR(metadata->shtr, "%s: error reading isotopes -- %s.\n",
    628     caller, res_to_cstr(res));
    629   goto exit;
    630 }
    631 
    632 static void
    633 release_isotope_metadata(ref_T* ref)
    634 {
    635   struct shtr* shtr = NULL;
    636   struct shtr_isotope_metadata* metadata = CONTAINER_OF
    637     (ref, struct shtr_isotope_metadata, ref);
    638   ASSERT(ref);
    639   shtr = metadata->shtr;
    640   darray_molecule_release(&metadata->molecules);
    641   darray_isotope_release(&metadata->isotopes);
    642   MEM_RM(shtr->allocator, metadata);
    643   SHTR(ref_put(shtr));
    644 }
    645 
    646 /*******************************************************************************
    647  * Exported functions
    648  ******************************************************************************/
    649 res_T
    650 shtr_isotope_metadata_load
    651   (struct shtr* shtr,
    652    const char* path,
    653    struct shtr_isotope_metadata** metadata)
    654 {
    655   FILE* file = NULL;
    656   res_T res = RES_OK;
    657 
    658   if(!shtr || !path || !metadata) {
    659     res = RES_BAD_ARG;
    660     goto error;
    661   }
    662 
    663   file = fopen(path, "r");
    664   if(!file) {
    665     ERROR(shtr, "%s: error opening file `%s'.\n", FUNC_NAME, path);
    666     res = RES_IO_ERR;
    667     goto error;
    668   }
    669 
    670   res = load_stream(shtr, file, path, metadata);
    671   if(res != RES_OK) goto error;
    672 
    673 exit:
    674   if(file) fclose(file);
    675   return res;
    676 error:
    677   goto exit;
    678 }
    679 
    680 res_T
    681 shtr_isotope_metadata_load_stream
    682   (struct shtr* shtr,
    683    FILE* stream,
    684    const char* stream_name,
    685    struct shtr_isotope_metadata** metadata)
    686 {
    687   if(!shtr || !stream || !metadata) return RES_BAD_ARG;
    688   return load_stream
    689     (shtr, stream, stream_name ? stream_name : "<stream>", metadata);
    690 }
    691 
    692 res_T
    693 shtr_isotope_metadata_create_from_stream
    694   (struct shtr* shtr,
    695    FILE* stream,
    696    struct shtr_isotope_metadata** out_metadata)
    697 {
    698   struct shtr_isotope_metadata* metadata = NULL;
    699   int version = 0;
    700   res_T res = RES_OK;
    701 
    702   if(!shtr || !out_metadata || !stream) {
    703     res = RES_BAD_ARG;
    704     goto error;
    705   }
    706 
    707   res = create_isotope_metadata(shtr, &metadata);
    708   if(res != RES_OK) goto error;
    709 
    710   #define READ(Var, Nb) {                                                      \
    711     if(fread((Var), sizeof(*(Var)), (Nb), stream) != (Nb)) {                   \
    712       if(feof(stream)) {                                                       \
    713         res = RES_BAD_ARG;                                                     \
    714       } else if(ferror(stream)) {                                              \
    715         res = RES_IO_ERR;                                                      \
    716       } else {                                                                 \
    717         res = RES_UNKNOWN_ERR;                                                 \
    718       }                                                                        \
    719       ERROR(shtr, "%s: error reading isotope metadata -- %s.\n",             \
    720         FUNC_NAME, res_to_cstr(res));                                          \
    721       goto error;                                                              \
    722     }                                                                          \
    723   } (void)0
    724   READ(&version, 1);
    725   if(version != SHTR_ISOTOPE_METADATA_VERSION) {
    726     ERROR(shtr,
    727       "%s: unexpected isotope metadata version %d. "
    728       "Expecting a isotope metadata in version %d.\n",
    729       FUNC_NAME, version, SHTR_ISOTOPE_METADATA_VERSION);
    730     res = RES_BAD_ARG;
    731     goto error;
    732   }
    733 
    734   res = read_molecules(metadata, FUNC_NAME, stream);
    735   if(res != RES_OK) goto error;
    736   res = read_isotopes(metadata, FUNC_NAME, stream);
    737   if(res != RES_OK) goto error;
    738 
    739   READ(metadata->molid2idx, SHTR_MAX_MOLECULES_COUNT);
    740   #undef READ
    741 
    742 exit:
    743   if(out_metadata) *out_metadata = metadata;
    744   return res;
    745 error:
    746   if(metadata) {
    747     SHTR(isotope_metadata_ref_put(metadata));
    748     metadata = NULL;
    749   }
    750   goto exit;
    751 }
    752 
    753 res_T
    754 shtr_isotope_metadata_ref_get
    755   (struct shtr_isotope_metadata* metadata)
    756 {
    757   if(!metadata) return RES_BAD_ARG;
    758   ref_get(&metadata->ref);
    759   return RES_OK;
    760 }
    761 
    762 res_T
    763 shtr_isotope_metadata_ref_put
    764   (struct shtr_isotope_metadata* metadata)
    765 {
    766   if(!metadata) return RES_BAD_ARG;
    767   ref_put(&metadata->ref, release_isotope_metadata);
    768   return RES_OK;
    769 }
    770 
    771 res_T
    772 shtr_isotope_metadata_get_molecules_count
    773   (const struct shtr_isotope_metadata* metadata,
    774    size_t* nmolecules)
    775 {
    776   if(!metadata || !nmolecules) return RES_BAD_ARG;
    777   *nmolecules = darray_molecule_size_get(&metadata->molecules);
    778   return RES_OK;
    779 }
    780 
    781 res_T
    782 shtr_isotope_metadata_get_isotopes_count
    783   (const struct shtr_isotope_metadata* metadata,
    784    size_t* nisotopes)
    785 {
    786   if(!metadata || !nisotopes) return RES_BAD_ARG;
    787   *nisotopes = darray_isotope_size_get(&metadata->isotopes);
    788   return RES_OK;
    789 }
    790 
    791 res_T
    792 shtr_isotope_metadata_get_molecule
    793   (const struct shtr_isotope_metadata* metadata,
    794    const size_t imolecule,
    795    struct shtr_molecule* out_molecule)
    796 {
    797   const struct molecule* molecule = NULL;
    798   res_T res = RES_OK;
    799 
    800   if(!metadata || !out_molecule) {
    801     res = RES_BAD_ARG;
    802     goto error;
    803   }
    804   if(imolecule >= darray_molecule_size_get(&metadata->molecules)) {
    805     ERROR(metadata->shtr, "%s: invalid molecule index %lu.\n",
    806       FUNC_NAME, (unsigned long)imolecule);
    807     res = RES_BAD_ARG;
    808     goto error;
    809   }
    810 
    811   molecule = darray_molecule_cdata_get(&metadata->molecules) + imolecule;
    812   out_molecule->name = str_cget(&molecule->name);
    813   out_molecule->id = molecule->id;
    814   out_molecule->nisotopes =
    815     molecule->isotopes_range[1] - molecule->isotopes_range[0];
    816   out_molecule->isotopes =
    817     darray_isotope_cdata_get(&metadata->isotopes) + molecule->isotopes_range[0];
    818 
    819 exit:
    820   return res;
    821 error:
    822   goto exit;
    823 }
    824 
    825 res_T
    826 shtr_isotope_metadata_find_molecule
    827   (struct shtr_isotope_metadata* metadata,
    828    const int molecule_id,
    829    struct shtr_molecule* out_molecule)
    830 {
    831   int imolecule = 0;
    832   res_T res = RES_OK;
    833 
    834   if(!metadata || !out_molecule) {
    835     res = RES_BAD_ARG;
    836     goto error;
    837   }
    838 
    839   imolecule = metadata->molid2idx[molecule_id];
    840   if(imolecule < 0) {
    841     *out_molecule = SHTR_MOLECULE_NULL;
    842   } else {
    843     res = shtr_isotope_metadata_get_molecule
    844       (metadata, (size_t)imolecule, out_molecule);
    845     if(res != RES_OK) goto error;
    846   }
    847 
    848 exit:
    849   return res;
    850 error:
    851   goto exit;
    852 }
    853 
    854 res_T
    855 shtr_isotope_metadata_write
    856   (const struct shtr_isotope_metadata* metadata,
    857    FILE* stream)
    858 {
    859   res_T res = RES_OK;
    860 
    861   if(!metadata || !stream) {
    862     res = RES_BAD_ARG;
    863     goto error;
    864   }
    865 
    866   #define WRITE(Var, Nb) {                                                     \
    867     if(fwrite((Var), sizeof(*(Var)), (Nb), stream) != (Nb)) {                  \
    868       ERROR(metadata->shtr, "%s: error writing metadata\n", FUNC_NAME);        \
    869       res = RES_IO_ERR;                                                        \
    870       goto error;                                                              \
    871     }                                                                          \
    872   } (void)0
    873   WRITE(&SHTR_ISOTOPE_METADATA_VERSION, 1);
    874 
    875   res = write_molecules(metadata, FUNC_NAME, stream);
    876   if(res != RES_OK) goto error;
    877   res = write_isotopes(metadata, FUNC_NAME, stream);
    878   if(res != RES_OK) goto error;
    879 
    880   WRITE(metadata->molid2idx, SHTR_MAX_MOLECULES_COUNT);
    881   #undef WRITE
    882 
    883 exit:
    884   return res;
    885 error:
    886   goto exit;
    887 }