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_line_list.c (22663B)


      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 200809L /* fseeko */
     20 
     21 #include "shtr_c.h"
     22 #include "shtr_line_list_c.h"
     23 #include "shtr_param.h"
     24 
     25 #include <rsys/cstr.h>
     26 #include <rsys/text_reader.h>
     27 
     28 #include <stdio.h> /* fseeko */
     29 
     30 /* Maximum number of lines that can be stored in a memory block */
     31 #define NLINES_PER_BLOCK (BLOCK_SIZE/sizeof(struct line))
     32 
     33 struct stream {
     34   const char* name;
     35   FILE* fp;
     36   int intern_fp; /* Define if the stream was internally opened */
     37 };
     38 static const struct stream STREAM_NULL = {NULL, NULL, 0};
     39 
     40 /*******************************************************************************
     41  * Helper functions
     42  ******************************************************************************/
     43 static INLINE res_T
     44 check_shtr_line_list_load_args(const struct shtr_line_list_load_args* args)
     45 {
     46   if(!args) return RES_BAD_ARG;
     47 
     48   /* Source is missing */
     49   if(!args->file && !args->filename) return RES_BAD_ARG;
     50 
     51   return RES_OK;
     52 }
     53 
     54 static INLINE res_T
     55 check_shtr_line_list_read_args(const struct shtr_line_list_read_args* args)
     56 {
     57   if(!args) return RES_BAD_ARG;
     58 
     59   /* Source is missing */
     60   if(!args->file && !args->filename) return RES_BAD_ARG;
     61 
     62   /* Line range is degenerated */
     63   if(args->range[0] > args->range[1]) return RES_BAD_ARG;
     64 
     65   return RES_OK;
     66 }
     67 
     68 static INLINE res_T
     69 check_shtr_line_list_write_args(const struct shtr_line_list_write_args* args)
     70 {
     71   if(!args) return RES_BAD_ARG;
     72 
     73   /* Destination is missing */
     74   if(!args->file && !args->filename) return RES_BAD_ARG;
     75 
     76   return RES_OK;
     77 }
     78 
     79 static INLINE void
     80 stream_release(struct stream* stream)
     81 {
     82   ASSERT(stream);
     83   if(stream->intern_fp && stream->fp) CHK(fclose(stream->fp) == 0);
     84 }
     85 
     86 static res_T
     87 stream_init
     88   (struct shtr* shtr,
     89    const char* caller,
     90    const char* name, /* NULL <=> default stream name */
     91    FILE* fp, /* NULL <=> open file "name" */
     92    const char* mode, /* mode in fopen */
     93    struct stream* stream)
     94 {
     95   res_T res = RES_OK;
     96 
     97   ASSERT(shtr && caller && stream);
     98   ASSERT(fp || (name && mode));
     99 
    100   *stream = STREAM_NULL;
    101 
    102   if(fp) {
    103     stream->intern_fp = 0;
    104     stream->name = name ? name : "stream";
    105     stream->fp = fp;
    106 
    107   } else {
    108     stream->intern_fp = 1;
    109     stream->name = name;
    110     if(!(stream->fp = fopen(name, mode))) {
    111       ERROR(shtr, "%s:%s: error opening file -- %s\n",
    112         caller, name, strerror(errno));
    113       res = RES_IO_ERR;
    114       goto error;
    115     }
    116   }
    117 
    118 exit:
    119   return res;
    120 error:
    121   if(stream->intern_fp && stream->fp) CHK(fclose(stream->fp) == 0);
    122   goto exit;
    123 }
    124 
    125 static res_T
    126 size_to_off(const size_t sz, off_t* off)
    127 {
    128   ASSERT(off);
    129   if(sz != (size_t)((off_t)sz)) return RES_BAD_OP;
    130   *off = (off_t)sz;
    131   return RES_OK;
    132 }
    133 
    134 static res_T
    135 create_line_list
    136   (struct shtr* shtr,
    137    struct shtr_line_list** out_list)
    138 {
    139   struct shtr_line_list* list = NULL;
    140   res_T res = RES_OK;
    141   ASSERT(shtr && out_list);
    142 
    143   list = MEM_CALLOC(shtr->allocator, 1, sizeof(*list));
    144   if(!list) {
    145     ERROR(shtr, "Could not allocate the list of lines.\n");
    146     res = RES_MEM_ERR;
    147     goto error;
    148   }
    149   ref_init(&list->ref);
    150   SHTR(ref_get(shtr));
    151   list->shtr = shtr;
    152   darray_charp_init(shtr->allocator, &list->blocks);
    153   list->info = SHTR_LINE_LIST_INFO_NULL;
    154 
    155 exit:
    156   *out_list = list;
    157   return res;
    158 error:
    159   if(list) {
    160     SHTR(line_list_ref_put(list));
    161     list = NULL;
    162   }
    163   goto exit;
    164 }
    165 
    166 static INLINE const struct line*
    167 get_line(const struct shtr_line_list* list, const size_t i)
    168 {
    169   const size_t iblock = i / NLINES_PER_BLOCK;
    170   const size_t iline  = i % NLINES_PER_BLOCK;
    171 
    172   ASSERT(list && i < list->nlines);
    173   ASSERT(iblock < darray_charp_size_get(&list->blocks));
    174   return (struct line*)(darray_charp_cdata_get(&list->blocks))[iblock] + iline;
    175 }
    176 
    177 static void
    178 line_encode(const struct shtr_line* line, struct line* line_encoded)
    179 {
    180   union { float flt; int32_t i32; } ucast;
    181   ASSERT(line && line_encoded);
    182 
    183   /* Keep the wavenumber and the intensity as it */
    184   line_encoded->wavenumber = line->wavenumber;
    185   line_encoded->intensity = line->intensity;
    186 
    187   /* Encode the lower state energy and delta air in single precision  */
    188   line_encoded->lower_state_energy = (float)line->lower_state_energy;
    189   line_encoded->delta_air = (float)line->delta_air;
    190 
    191   /* Store gamma_air as an unsigned fixed-point number (0:14), i.e., 0 bit for
    192    * the integer part and 14 bits for the fractional part */
    193   ASSERT((int64_t)line->gamma_air == 0 && line->gamma_air >= 0);
    194   line_encoded->gair14_gself14_isoid4 =
    195     ((int32_t)float2fix(line->gamma_air, 0, 14) & (BIT_I32(14)-1)) << 18;
    196 
    197   /* Store gamma_self as an unsigned fixed-point number (0:14), i.e., 0 bit for
    198    * the integer part and 14 bits for the fractional part */
    199   ASSERT((int64_t)line->gamma_self == 0 && line->gamma_self >= 0);
    200   line_encoded->gair14_gself14_isoid4 |=
    201     ((int32_t)float2fix(line->gamma_self, 0, 14) & (BIT_I32(14)-1)) << 4;
    202 
    203   /* Store the isotope id on 4 bits */
    204   ASSERT(line->isotope_id_local < 16);
    205   line_encoded->gair14_gself14_isoid4 |= line->isotope_id_local & (BIT_I32(4)-1);
    206 
    207   /* Encode n_air in single precision with its last 7 bits of matissa disabled
    208    * in order to store the molecule identifier */
    209   ucast.flt = (float)line->n_air;
    210   ucast.i32 &= ~(BIT_I32(7)-1);
    211 
    212   /* Store the molecule id on 7 bits */
    213   ASSERT(line->molecule_id < 128);
    214   ucast.i32 |= (int32_t)line->molecule_id & (BIT_I32(7)-1);
    215   line_encoded->nair25_molid7 = ucast.i32;
    216 }
    217 
    218 static void
    219 line_decode(const struct line* line_encoded, struct shtr_line* line)
    220 {
    221   union { float flt; int32_t i32; } ucast;
    222   int64_t i64 = 0;
    223   ASSERT(line && line_encoded);
    224 
    225   line->wavenumber = line_encoded->wavenumber;
    226   line->intensity = line_encoded->intensity;
    227   line->lower_state_energy = line_encoded->lower_state_energy;
    228   line->delta_air = line_encoded->delta_air;
    229 
    230   /* Convert gamma_air and gamma_self from fixed-point numbers (0:14) to
    231    * double-precision floating-point numbers */
    232   i64 = (line_encoded->gair14_gself14_isoid4 >> 18) & (BIT_I32(14)-1);
    233   line->gamma_air = fix2float(i64, 0, 14);
    234   i64 = (line_encoded->gair14_gself14_isoid4 >> 4) & (BIT_I32(14)-1);
    235   line->gamma_self = fix2float(i64, 0, 14);
    236 
    237   /* Unpack the isotope ID */
    238   line->isotope_id_local = line_encoded->gair14_gself14_isoid4 & (BIT_I32(4)-1);
    239 
    240   /* Unpack the molecule ID */
    241   ucast.i32 = line_encoded->nair25_molid7;
    242   line->molecule_id = ucast.i32 & (BIT_I32(7)-1);
    243   ucast.i32 &= ~(BIT_I32(7)-1);
    244 
    245   /* Convert n_air from single precision to double precision */
    246   line->n_air = ucast.flt;
    247 }
    248 
    249 static res_T
    250 register_line
    251   (struct shtr_line_list* list,
    252    const struct txtrdr* txtrdr,
    253    const struct shtr_line* line)
    254 {
    255   struct shtr_line ln = SHTR_LINE_NULL;
    256   struct line* lines = NULL;
    257   struct line ln_encoded = LINE_NULL;
    258   size_t iblock = 0; /* Index of the block in which the line is stored */
    259   size_t iline  = 0; /* Index of the line in the block */
    260   res_T res = RES_OK;
    261 
    262   /* Pre-conditions */
    263   ASSERT(list && txtrdr && line);
    264 
    265   line_encode(line, &ln_encoded);
    266 
    267   /* Check if a line has been saved. If so, ensure that the lines are sorted */
    268   if(list->nlines) {
    269     const struct line* ln_encoded_prev = get_line(list, list->nlines-1);
    270     if(ln_encoded_prev->wavenumber > ln_encoded.wavenumber) {
    271       ERROR(list->shtr,
    272         "%s:%lu: lines are not sorted in ascending order wrt their wavenumber.\n",
    273         txtrdr_get_name(txtrdr), txtrdr_get_line_num(txtrdr));
    274       res = RES_BAD_ARG;
    275       goto error;
    276     }
    277   }
    278 
    279   iblock = list->nlines / NLINES_PER_BLOCK;
    280   iline  = list->nlines % NLINES_PER_BLOCK;
    281 
    282   /* Ensure there is sufficient space to store the line */
    283   if(iline == 0) {
    284     /* There is no more space in the last allocated block. Allocate a new one. */
    285     char* block = MEM_CALLOC(list->shtr->allocator, 1, BLOCK_SIZE);
    286     if(!block) { res = RES_MEM_ERR; goto error; }
    287 
    288     res = darray_charp_push_back(&list->blocks, &block);
    289     if(res != RES_OK) goto error;
    290   }
    291 
    292   /* Store the encoded line */
    293   lines = (struct line*)darray_charp_data_get(&list->blocks)[iblock];
    294   lines[iline] = ln_encoded;
    295   ++list->nlines;
    296 
    297   line_decode(&ln_encoded, &ln);
    298   ASSERT(ln.molecule_id == line->molecule_id);
    299   ASSERT(ln.isotope_id_local == line->isotope_id_local);
    300 
    301   #define UPDATE_INFO(Name) { \
    302     const double err = fabs(line->Name - ln.Name); \
    303     list->info.Name.range[0] = MMIN(list->info.Name.range[0], ln.Name); \
    304     list->info.Name.range[1] = MMAX(list->info.Name.range[1], ln.Name); \
    305     list->info.Name.err = MMAX(list->info.Name.err, err); \
    306   } (void) 0
    307   UPDATE_INFO(wavenumber);
    308   UPDATE_INFO(intensity);
    309   UPDATE_INFO(gamma_air);
    310   UPDATE_INFO(gamma_self);
    311   UPDATE_INFO(lower_state_energy);
    312   UPDATE_INFO(n_air);
    313   UPDATE_INFO(delta_air);
    314   #undef UPDATE_INFO
    315 
    316 exit:
    317   return res;
    318 error:
    319   goto exit;
    320 }
    321 
    322 static res_T
    323 parse_line
    324   (struct shtr_line_list* list,
    325    struct txtrdr* txtrdr,
    326    struct shtr_line* ln)
    327 {
    328   struct param_desc param = PARAM_DESC_NULL;
    329   struct shtr* shtr = NULL;
    330   char* line = NULL;
    331   char* str = NULL;
    332   char* end = NULL;
    333   char backup;
    334   int molecule_id;
    335   int isotope_id_local;
    336   res_T res = RES_OK;
    337 
    338   ASSERT(list && txtrdr && ln);
    339 
    340   line = txtrdr_get_line(txtrdr);
    341   ASSERT(line);
    342 
    343   shtr = list->shtr;
    344   param.path = txtrdr_get_name(txtrdr);
    345   param.line = txtrdr_get_line_num(txtrdr);
    346 
    347   str = end = line;
    348   backup = str[0];
    349   #define NEXT(Size) {                                                         \
    350     *end = backup;                                                             \
    351     str = end;                                                                 \
    352     end = str+(Size);                                                          \
    353     backup = *end;                                                             \
    354     *end = '\0';                                                               \
    355   } (void)0
    356   #define PARSE(Var, Size, Type, Name, Low, Upp, LowIncl, UppIncl) {           \
    357     NEXT(Size);                                                                \
    358     param.name = (Name);                                                       \
    359     param.low = (Low);                                                         \
    360     param.upp = (Upp);                                                         \
    361     param.is_low_incl = (LowIncl);                                             \
    362     param.is_upp_incl = (UppIncl);                                             \
    363     res = parse_param_##Type(shtr, str, &param, Var);                          \
    364     if(res != RES_OK) goto error;                                              \
    365   } (void)0
    366 
    367   PARSE(&molecule_id, 2, int, "molecule identifier", 0,99,1,1);
    368   ln->molecule_id = (int32_t)molecule_id;
    369 
    370   PARSE(&isotope_id_local, 1, int, "isotope local identifier", 0,9,1,1);
    371   ln->isotope_id_local = (int32_t)
    372     (isotope_id_local == 0 ? 9 : (isotope_id_local - 1));
    373 
    374   PARSE(&ln->wavenumber, 12, double, "central wavenumber", 0,INF,0,1);
    375   PARSE(&ln->intensity, 10, double, "reference intensity", 0,INF,0,1);
    376 
    377   NEXT(10); /* Skip the Enstein coef */
    378 
    379   PARSE(&ln->gamma_air, 5, double, "air broadening half-width", 0,INF,1,1);
    380   PARSE(&ln->gamma_self, 5, double, "self broadening half-width", 0,INF,1,1);
    381 
    382   /* Handle unavailable lower state energy */
    383   PARSE(&ln->lower_state_energy, 10, double, "lower state energy",-INF,INF,1,1);
    384   if(ln->lower_state_energy == -1) {
    385     WARN(shtr,
    386       "%s:%lu: the lower state energy is unavailable for this line, so it is "
    387       "ignored.\n", txtrdr_get_name(txtrdr), txtrdr_get_line_num(txtrdr));
    388     goto exit; /* Skip the line */
    389   }
    390   /* Check the domain validity */
    391   if(ln->lower_state_energy < 0) {
    392     ERROR(shtr,
    393       "%s:%lu: invalid lower state energy %g. It must be in [0, INF].\n",
    394       txtrdr_get_name(txtrdr), txtrdr_get_line_num(txtrdr),
    395       ln->lower_state_energy);
    396     res = RES_BAD_ARG;
    397     goto error;
    398   }
    399 
    400   PARSE(&ln->n_air, 4, double, "temperature-dependent exponent",-INF,INF,1,1);
    401   PARSE(&ln->delta_air, 8, double, "air-pressure wavenumber shift", -INF,INF,1,1);
    402 
    403   /* Skip the remaining values */
    404 
    405   #undef NEXT
    406   #undef PARSE
    407 
    408   /* Check the size of the remaining data to ensure that there is at least the
    409    * expected number of bytes wrt the HITRAN fileformat */
    410   *end = backup;
    411   str = end;
    412   if(strlen(str) != 93) {
    413     ERROR(list->shtr, "%s:%lu: missing data after delta air.\n",
    414       param.path, (unsigned long)param.line);
    415     res = RES_BAD_ARG;
    416     goto error;
    417   }
    418 
    419 exit:
    420   return res;
    421 error:
    422   goto exit;
    423 }
    424 
    425 static res_T
    426 load_stream
    427   (struct shtr* shtr,
    428    const struct shtr_line_list_load_args* args,
    429    struct shtr_line_list** out_lines)
    430 {
    431   struct stream stream = STREAM_NULL;
    432   struct shtr_line_list* list = NULL;
    433   struct txtrdr* txtrdr = NULL;
    434   res_T res = RES_OK;
    435 
    436   ASSERT(shtr && args && out_lines);
    437 
    438   res = create_line_list(shtr,  &list);
    439   if(res != RES_OK) goto error;
    440 
    441   res = stream_init(shtr, FUNC_NAME, args->filename, args->file, "r", &stream);
    442   if(res != RES_OK) goto error;
    443 
    444   res = txtrdr_stream(list->shtr->allocator, stream.fp, stream.name,
    445     0/*No comment char*/, &txtrdr);
    446   if(res != RES_OK) {
    447     ERROR(shtr, "%s: error creating the text reader -- %s.\n",
    448       stream.name, res_to_cstr(res));
    449     goto error;
    450   }
    451 
    452   for(;;) {
    453     struct shtr_line ln = SHTR_LINE_NULL;
    454 
    455     res = txtrdr_read_line(txtrdr);
    456     if(res != RES_OK) {
    457       ERROR(shtr, "%s: error reading the line `%lu' -- %s.\n",
    458         stream.name, (unsigned long)txtrdr_get_line_num(txtrdr),
    459         res_to_cstr(res));
    460       goto error;
    461     }
    462 
    463     if(!txtrdr_get_cline(txtrdr)) break; /* No more parsed line */
    464 
    465     res = parse_line(list, txtrdr, &ln);
    466     if(res != RES_OK) goto error;
    467 
    468     res = register_line(list, txtrdr, &ln);
    469     if(res != RES_OK) goto error;
    470   }
    471 
    472 exit:
    473   if(txtrdr) txtrdr_ref_put(txtrdr);
    474   stream_release(&stream);
    475   *out_lines = list;
    476   return res;
    477 error:
    478   if(list) {
    479     SHTR(line_list_ref_put(list));
    480     list = NULL;
    481   }
    482   goto exit;
    483 }
    484 
    485 static void
    486 release_lines(ref_T * ref)
    487 {
    488   struct shtr* shtr = NULL;
    489   struct shtr_line_list* list = CONTAINER_OF(ref, struct shtr_line_list, ref);
    490   char** blocks = NULL;
    491   size_t i=0, n=0;
    492 
    493   ASSERT(ref);
    494 
    495   shtr = list->shtr;
    496 
    497   n = darray_charp_size_get(&list->blocks);
    498   blocks = darray_charp_data_get(&list->blocks);
    499   FOR_EACH(i, 0, n) { if(blocks[i]) MEM_RM(shtr->allocator, blocks[i]); }
    500 
    501   darray_charp_release(&list->blocks);
    502   MEM_RM(shtr->allocator, list);
    503   SHTR(ref_put(shtr));
    504 }
    505 
    506 /*******************************************************************************
    507  * Exported functions
    508  ******************************************************************************/
    509 res_T
    510 shtr_line_list_load
    511   (struct shtr* shtr,
    512    const struct shtr_line_list_load_args* args,
    513    struct shtr_line_list** list)
    514 {
    515   res_T res = RES_OK;
    516 
    517   if(!shtr || !list) { res = RES_BAD_ARG; goto error; }
    518   res = check_shtr_line_list_load_args(args);
    519   if(res != RES_OK) goto error;
    520 
    521   res = load_stream(shtr, args, list);
    522   if(res != RES_OK) goto error;
    523 
    524 exit:
    525   return res;
    526 error:
    527   goto exit;
    528 }
    529 
    530 res_T
    531 shtr_line_list_read
    532   (struct shtr* shtr,
    533    const struct shtr_line_list_read_args* args,
    534    struct shtr_line_list** out_list)
    535 {
    536   size_t line_range[2]={0,0}; /* Range of lines adapted to effective line ids */
    537   size_t nlines = 0; /* Ttotal number of lines in the original list */
    538   size_t nblocks = 0; /* Number of memory blocks needed to store read lines */
    539 
    540   struct shtr_line_list* list = NULL; /* The output list */
    541   char** blocks = NULL; /* Allocated list of memory blocks */
    542 
    543   /* Miscellaneous */
    544   struct stream stream = STREAM_NULL;
    545   size_t i = 0;
    546   size_t sz = 0;
    547   size_t sz_to_load = 0;
    548   off_t off;
    549   int version = 0;
    550   int err = 0;
    551   res_T res = RES_OK;
    552 
    553   if(!shtr || !out_list) { res = RES_BAD_ARG; goto error; }
    554   res = check_shtr_line_list_read_args(args);
    555   if(res != RES_OK) goto error;
    556 
    557   res = create_line_list(shtr, &list);
    558   if(res != RES_OK) goto error;
    559 
    560   res = stream_init
    561     (list->shtr, FUNC_NAME, args->filename, args->file, "r", &stream);
    562   if(res != RES_OK) goto error;
    563 
    564   #define READ(Var, Nb) {                                                      \
    565     if(fread((Var), sizeof(*(Var)), (Nb), stream.fp) != (Nb)) {                \
    566       if(feof(stream.fp)) {                                                    \
    567         res = RES_BAD_ARG;                                                     \
    568       } else if(ferror(stream.fp)) {                                           \
    569         res = RES_IO_ERR;                                                      \
    570       } else {                                                                 \
    571         res = RES_UNKNOWN_ERR;                                                 \
    572       }                                                                        \
    573       ERROR(shtr,                                                              \
    574         "%s:%s: error reading line list -- %s.\n",                             \
    575         FUNC_NAME, stream.name, res_to_cstr(res));                             \
    576       goto error;                                                              \
    577     }                                                                          \
    578   } (void)0
    579 
    580   READ(&version, 1);
    581   if(version != SHTR_LINE_LIST_VERSION) {
    582     ERROR(shtr,
    583       "%s:%s: unexpected line list version %d. "
    584       "Expecting a line list in version %d.\n",
    585       FUNC_NAME, stream.name, version, SHTR_LINE_LIST_VERSION);
    586     res = RES_BAD_ARG;
    587     goto error;
    588   }
    589 
    590   /* Informations on line parameters */
    591   READ(&list->info, 1);
    592 
    593   /* Total number of lines in the list from which the lines will be read */
    594   READ(&nlines, 1);
    595 
    596   /* Actually, there are no lines to read */
    597   if(nlines <= args->range[0]) goto exit;
    598 
    599   /* Fit the upper limit to the effective number of lines */
    600   line_range[0] = args->range[0];
    601   line_range[1] = MMIN(args->range[1], nlines-1/*inclusive bounds*/);
    602   list->nlines = line_range[1] - line_range[0] + 1/*inclusive bounds*/;
    603 
    604   /* Calculate the number of blocks needed to store the line to be read */
    605   nblocks = (list->nlines + (NLINES_PER_BLOCK-1)/*ceil*/) / NLINES_PER_BLOCK;
    606   if((res = darray_charp_resize(&list->blocks, nblocks)) != RES_OK) goto error;
    607 
    608   /* Compute the offset toward the first line to load */
    609   sz = line_range[0] * sizeof(struct line);
    610   res = size_to_off(sz, &off);
    611   if(res != RES_OK) {
    612     ERROR(shtr, "%s:%s: file is too large regarding the seek offset %zu\n",
    613       FUNC_NAME, stream.name, sz);
    614     goto error;
    615   }
    616 
    617   /* Move to the first line to load */
    618   err = fseeko(stream.fp, off, SEEK_CUR);
    619   if(err) {
    620     ERROR(shtr, "%s:%s: %s\n", FUNC_NAME, stream.name, strerror(errno));
    621     res = RES_IO_ERR;
    622     goto error;
    623   }
    624 
    625   /* Compute the overall number of bytes to load */
    626   sz_to_load = list->nlines * sizeof(struct line);
    627 
    628   /* Load line data and store them in memory blocks */
    629   blocks = darray_charp_data_get(&list->blocks);
    630   FOR_EACH(i, 0, nblocks) {
    631     blocks[i] = MEM_CALLOC(list->shtr->allocator, 1, BLOCK_SIZE);
    632     if(!blocks[i]) {
    633       ERROR(shtr, "%s:%s: error allocating memory block\n",
    634         stream.name, FUNC_NAME);
    635       res = RES_MEM_ERR;
    636       goto error;
    637     }
    638 
    639     sz = MMIN(BLOCK_SIZE, sz_to_load);
    640     READ(blocks[i], sz);
    641 
    642     sz_to_load -= sz;
    643   }
    644 
    645   #undef READ
    646 
    647 exit:
    648   stream_release(&stream);
    649   if(out_list) *out_list = list;
    650   return res;
    651 error:
    652   if(list) { SHTR(line_list_ref_put(list)); list = NULL; }
    653   goto exit;
    654 }
    655 
    656 res_T
    657 shtr_line_list_ref_get(struct shtr_line_list* list)
    658 {
    659   if(!list) return RES_BAD_ARG;
    660   ref_get(&list->ref);
    661   return RES_OK;
    662 }
    663 
    664 res_T
    665 shtr_line_list_ref_put(struct shtr_line_list* list)
    666 {
    667   if(!list) return RES_BAD_ARG;
    668   ref_put(&list->ref, release_lines);
    669   return RES_OK;
    670 }
    671 
    672 res_T
    673 shtr_line_list_get_size
    674   (const struct shtr_line_list* list,
    675    size_t* nlines)
    676 {
    677   if(!list || !nlines) return RES_BAD_ARG;
    678   *nlines = list->nlines;
    679   return RES_OK;
    680 }
    681 
    682 res_T
    683 shtr_line_list_at
    684   (struct shtr_line_list* list,
    685    const size_t i,
    686    struct shtr_line* line)
    687 {
    688   const struct line* ln_encoded = NULL;
    689 
    690   if(!list || !line || i >= list->nlines) return RES_BAD_ARG;
    691   ln_encoded = get_line(list, i);
    692   line_decode(ln_encoded, line);
    693   return RES_OK;
    694 }
    695 
    696 res_T
    697 shtr_line_list_write
    698   (const struct shtr_line_list* list,
    699    const struct shtr_line_list_write_args* args)
    700 {
    701   struct stream stream = STREAM_NULL;
    702   char* const* blocks = NULL;
    703   size_t i=0, n=0;
    704   res_T res = RES_OK;
    705 
    706   if(!list) { res = RES_BAD_ARG; goto error; }
    707   res = check_shtr_line_list_write_args(args);
    708   if(res != RES_OK) goto error;
    709 
    710   res = stream_init
    711     (list->shtr, FUNC_NAME, args->filename, args->file, "w", &stream);
    712   if(res != RES_OK) goto error;
    713 
    714   #define WRITE(Var, Nb) {                                                     \
    715     if(fwrite((Var), sizeof(*(Var)), (Nb), stream.fp) != (Nb)) {               \
    716       ERROR(list->shtr, "%s:%s: error writing line list -- %s\n",              \
    717         FUNC_NAME, stream.name, strerror(errno));                              \
    718       res = RES_IO_ERR;                                                        \
    719       goto error;                                                              \
    720     }                                                                          \
    721   } (void)0
    722 
    723   /* Version management */
    724   WRITE(&SHTR_LINE_LIST_VERSION, 1);
    725 
    726   /* Informations on line parameters */
    727   WRITE(&list->info, 1);
    728 
    729   /* Number of lines in the list */
    730   WRITE(&list->nlines, 1);
    731 
    732   /* Lines stored in memory blocks. */
    733   blocks = darray_charp_cdata_get(&list->blocks);
    734   n = darray_charp_size_get(&list->blocks);
    735   FOR_EACH(i, 0, n) { WRITE(blocks[i], BLOCK_SIZE); }
    736 
    737   #undef WRITE
    738 
    739 exit:
    740   stream_release(&stream);
    741   return res;
    742 error:
    743   goto exit;
    744 }
    745 
    746 res_T
    747 shtr_line_list_get_info
    748   (const struct shtr_line_list* list,
    749    struct shtr_line_list_info* info)
    750 {
    751   if(!list || !info) return RES_BAD_ARG;
    752   *info = list->info;
    753   return RES_OK;
    754 }