star-aerosol

Describe the radiative properties of aerosols
git clone git://git.meso-star.fr/star-aerosol.git
Log | Files | Refs | README | LICENSE

sars.c (21677B)


      1 /* Copyright (C) 2022, 2023 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #define _POSIX_C_SOURCE 200809L /* mmap support */
     17 #define _DEFAULT_SOURCE 1 /* MAP_POPULATE support */
     18 #define _BSD_SOURCE 1 /* MAP_POPULATE for glibc < 2.19 */
     19 
     20 #include "sars.h"
     21 #include "sars_c.h"
     22 #include "sars_log.h"
     23 
     24 #include <rsys/algorithm.h>
     25 #include <rsys/cstr.h>
     26 #include <rsys/hash.h>
     27 
     28 #include <unistd.h> /* sysconf support */
     29 
     30 #include <errno.h>
     31 #include <sys/mman.h> /* mmap */
     32 #include <sys/stat.h> /* fstat */
     33 
     34 /*******************************************************************************
     35  * Helper functions
     36  ******************************************************************************/
     37 static INLINE int
     38 is_stdin(FILE* stream)
     39 {
     40   struct stat stream_buf;
     41   struct stat stdin_buf;
     42   ASSERT(stream);
     43   CHK(fstat(fileno(stream), &stream_buf) == 0);
     44   CHK(fstat(STDIN_FILENO, &stdin_buf) == 0);
     45   return stream_buf.st_dev == stdin_buf.st_dev;
     46 }
     47 
     48 static INLINE res_T
     49 check_sars_create_args(const struct sars_create_args* args)
     50 {
     51   /* Nothing to check. Only return RES_BAD_ARG if args is NULL */
     52   return args ? RES_OK : RES_BAD_ARG;
     53 }
     54 
     55 static INLINE res_T
     56 check_sars_load_args(const struct sars_load_args* args)
     57 {
     58   if(!args || !args->path) return RES_BAD_ARG;
     59   return RES_OK;
     60 }
     61 
     62 static INLINE res_T
     63 check_sars_load_stream_args
     64   (struct sars* sars,
     65    const struct sars_load_stream_args* args)
     66 {
     67   if(!args || !args->stream || !args->name) return RES_BAD_ARG;
     68   if(args->memory_mapping && is_stdin(args->stream)) {
     69     log_err(sars,
     70       "%s: unable to use memory mapping on data loaded from stdin\n",
     71       args->name);
     72     return RES_BAD_ARG;
     73   }
     74   return RES_OK;
     75 }
     76 
     77 static void
     78 reset_sars(struct sars* sars)
     79 {
     80   ASSERT(sars);
     81   sars->pagesize = 0;
     82   sars->nnodes = 0;
     83   darray_band_purge(&sars->bands);
     84 }
     85 
     86 static res_T
     87 read_band
     88   (struct sars* sars,
     89    struct band* band,
     90    FILE* stream)
     91 {
     92   size_t iband;
     93   res_T res = RES_OK;
     94   ASSERT(sars && band);
     95 
     96   band->sars = sars;
     97   iband = (size_t)(band - darray_band_cdata_get(&sars->bands));
     98 
     99   /* Read band definition */
    100   #define READ(Var,  Name) {                                                   \
    101     if(fread((Var), sizeof(*(Var)), 1, stream) != 1) {                         \
    102       log_err(sars, "%s: band %lu: could not read the %s.\n",                  \
    103         sars_get_name(sars), (unsigned long)iband, (Name));                    \
    104       res = RES_IO_ERR;                                                        \
    105       goto error;                                                              \
    106     }                                                                          \
    107   } (void)0
    108   READ(&band->low, "band lower bound");
    109   READ(&band->upp, "band upper bound");
    110   #undef READ
    111 
    112   /* Check band description */
    113   if(band->low < 0 || band->low >= band->upp) {
    114     log_err(sars,
    115       "%s: band %lu: invalid band range [%g, %g[.\n",
    116       sars_get_name(sars), (unsigned long)iband, band->low, band->upp);
    117     res = RES_BAD_ARG;
    118     goto error;
    119   }
    120 
    121 exit:
    122   return res;
    123 error:
    124   goto exit;
    125 }
    126 
    127 static res_T
    128 map_data
    129   (struct sars* sars,
    130    const int fd, /* File descriptor */
    131    const size_t filesz, /* Overall filesize */
    132    const char* data_name,
    133    const off_t offset, /* Offset of the data into file */
    134    const size_t map_len,
    135    void** out_map) /* Lenght of the data to map */
    136 {
    137   void* map = NULL;
    138   res_T res = RES_OK;
    139   ASSERT(sars && filesz && data_name && map_len && out_map);
    140   ASSERT(IS_ALIGNED((size_t)offset, (size_t)sars->pagesize));
    141 
    142   if((size_t)offset + map_len > filesz) {
    143     log_err(sars, "%s: the %s to map exceed the file size\n",
    144       sars_get_name(sars), data_name);
    145     res = RES_IO_ERR;
    146     goto error;
    147   }
    148 
    149   map = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE|MAP_POPULATE, fd, offset);
    150   if(map == MAP_FAILED) {
    151     log_err(sars, "%s: could not map the %s -- %s\n",
    152       sars_get_name(sars), data_name, strerror(errno));
    153     res = RES_IO_ERR;
    154     goto error;
    155   }
    156 
    157 exit:
    158   *out_map = map;
    159   return res;
    160 error:
    161   if(map == MAP_FAILED) map = NULL;
    162   goto exit;
    163 }
    164 
    165 static res_T
    166 map_file(struct sars* sars, FILE* stream)
    167 {
    168   size_t filesz;
    169   size_t map_len;
    170   size_t iband;
    171   size_t nbands;
    172   off_t offset;
    173   res_T res = RES_OK;
    174   ASSERT(sars && stream);
    175 
    176   /* Compute the length in bytes of the k to map for each band/quadrature point */
    177   map_len = ALIGN_SIZE(sars->nnodes * sizeof(float)*2, sars->pagesize);
    178 
    179   /* Compute the offset toward the 1st list of radiative coefficients */
    180   offset = ftell(stream);
    181   offset = (off_t)ALIGN_SIZE((uint64_t)offset, sars->pagesize);
    182 
    183   /* Retrieve the overall filesize */
    184   fseek(stream, 0, SEEK_END);
    185   filesz = (size_t)ftell(stream);
    186 
    187   nbands = sars_get_bands_count(sars);
    188   FOR_EACH(iband, 0, nbands) {
    189     struct band* band = NULL;
    190 
    191     band = darray_band_data_get(&sars->bands) + iband;
    192     band->map_len = map_len;
    193 
    194     /* Mapping per band radiative coefficients */
    195     res = map_data(sars, fileno(stream), filesz, "radiative coefficients",
    196       offset, band->map_len, (void**)&band->k_list);
    197     if(res != RES_OK) {
    198       log_err(sars,
    199         "%s: data mapping error for band %lu\n",
    200         sars_get_name(sars), (unsigned long)iband);
    201       res = RES_IO_ERR;
    202       goto error;
    203     }
    204 
    205     offset = (off_t)((size_t)offset + map_len);
    206   }
    207 
    208 exit:
    209   return res;
    210 error:
    211   goto exit;
    212 }
    213 
    214 static res_T
    215 read_padding(FILE* stream, const size_t padding)
    216 {
    217   char chunk[1024];
    218   size_t remaining_nbytes = padding;
    219 
    220   while(remaining_nbytes) {
    221     const size_t nbytes = MMIN(sizeof(chunk), remaining_nbytes);
    222     if(fread(chunk, 1, nbytes, stream) != nbytes) return RES_IO_ERR;
    223     remaining_nbytes -= nbytes;
    224   }
    225   return RES_OK;
    226 }
    227 
    228 /* Return the size in bytes of the data layout and band descriptors */
    229 static INLINE size_t
    230 compute_sizeof_header(struct sars* sars)
    231 {
    232   size_t sizeof_header = 0;
    233   ASSERT(sars);
    234 
    235   sizeof_header =
    236     sizeof(uint64_t) /* pagesize */
    237   + sizeof(uint64_t) /* #bands */
    238   + sizeof(uint64_t) /* #nodes */
    239   + sizeof(double[2]) * sars_get_bands_count(sars); /* Bands */
    240   return sizeof_header;
    241 }
    242 
    243 static res_T
    244 load_data
    245   (struct sars* sars,
    246    FILE* stream,
    247    const char* data_name,
    248    float** out_data)
    249 {
    250   float* data = NULL;
    251   res_T res = RES_OK;
    252   ASSERT(sars && stream && data_name && out_data);
    253 
    254   data = MEM_ALLOC(sars->allocator, sizeof(float[2]/*ka and ks*/)*sars->nnodes);
    255   if(!data) {
    256     res = RES_MEM_ERR;
    257     log_err(sars, "%s: could not allocate the %s -- %s\n",
    258       sars_get_name(sars), data_name, res_to_cstr(res));
    259     goto error;
    260   }
    261 
    262   if(fread(data, sizeof(float[2]), sars->nnodes, stream) != sars->nnodes) {
    263     res = RES_IO_ERR;
    264     log_err(sars, "%s: could not read the %s -- %s\n",
    265       sars_get_name(sars), data_name, res_to_cstr(res));
    266     goto error;
    267   }
    268 
    269 exit:
    270   *out_data = data;
    271   return res;
    272 error:
    273   if(data) { MEM_RM(sars->allocator, data); data = NULL; }
    274   goto exit;
    275 }
    276 
    277 static res_T
    278 load_file(struct sars* sars, FILE* stream)
    279 {
    280   size_t sizeof_header;
    281   size_t sizeof_k_list;
    282   size_t padding_bytes;
    283   size_t iband;
    284   size_t nbands;
    285   res_T res = RES_OK;
    286   ASSERT(sars && stream);
    287 
    288   sizeof_header = compute_sizeof_header(sars);
    289   sizeof_k_list = sizeof(float[2])*sars->nnodes;
    290 
    291   padding_bytes = ALIGN_SIZE(sizeof_header, sars->pagesize) - sizeof_header;
    292   if((res = read_padding(stream, padding_bytes)) != RES_OK) goto error;
    293 
    294   /* Calculate the padding between the lists of radiative coefficients. Note
    295    * that this padding is the same between each list */
    296   padding_bytes = ALIGN_SIZE(sizeof_k_list, sars->pagesize) - sizeof_k_list;
    297 
    298   nbands = sars_get_bands_count(sars);
    299   FOR_EACH(iband, 0, nbands) {
    300     struct band* band = NULL;
    301 
    302     band = darray_band_data_get(&sars->bands) + iband;
    303     ASSERT(!band->k_list && band->sars == sars);
    304 
    305     /* Loading per band scattering coefficients */
    306     res = load_data(sars, stream, "radiative coefficients", &band->k_list);
    307     if(res != RES_OK) {
    308       log_err(sars,
    309         "%s: data loading error for band %lu\n",
    310         sars_get_name(sars), (unsigned long)iband);
    311       goto error;
    312     }
    313 
    314     if((res = read_padding(stream, padding_bytes)) != RES_OK) goto error;
    315   }
    316 
    317 exit:
    318   return res;
    319 error:
    320   goto exit;
    321 }
    322 
    323 static res_T
    324 load_stream(struct sars* sars, const struct sars_load_stream_args* args)
    325 {
    326   size_t iband;
    327   uint64_t nbands;
    328   res_T res = RES_OK;
    329   ASSERT(sars && check_sars_load_stream_args(sars, args) == RES_OK);
    330 
    331   reset_sars(sars);
    332 
    333   res = str_set(&sars->name, args->name);
    334   if(res != RES_OK) {
    335     log_err(sars, "%s: unable to duplicate path to loaded data or stream name\n",
    336       args->name);
    337     goto error;
    338   }
    339 
    340   /* Read file header */
    341   #define READ(Var, Name) {                                                    \
    342     if(fread((Var), sizeof(*(Var)), 1, args->stream) != 1) {                   \
    343       log_err(sars, "%s: could not read the %s.\n", sars_get_name(sars), (Name));\
    344       res = RES_IO_ERR;                                                        \
    345       goto error;                                                              \
    346     }                                                                          \
    347   } (void)0
    348   READ(&sars->pagesize, "page size");
    349   READ(&nbands, "number of bands");
    350   READ(&sars->nnodes, "number of nodes");
    351   #undef READ
    352 
    353   /* Check band description */
    354   if(!IS_ALIGNED(sars->pagesize, sars->pagesize_os)) {
    355     log_err(sars,
    356       "%s: invalid page size %lu. The page size attribute must be aligned on "
    357       "the page size of the operating system (%lu).\n",
    358       sars_get_name(sars),
    359       (unsigned long)sars->pagesize,
    360       (unsigned long)sars->pagesize_os);
    361     res = RES_BAD_ARG;
    362     goto error;
    363   }
    364   if(!nbands) {
    365     log_err(sars, "%s: invalid number of bands %lu.\n",
    366       sars_get_name(sars), (unsigned long)nbands);
    367     res = RES_BAD_ARG;
    368     goto error;
    369   }
    370   if(!sars->nnodes) {
    371     log_err(sars, "%s: invalid number of nodes %lu.\n",
    372       sars_get_name(sars), (unsigned long)sars->nnodes);
    373     res = RES_BAD_ARG;
    374     goto error;
    375   }
    376 
    377   /* Allocate the bands */
    378   res = darray_band_resize(&sars->bands, nbands);
    379   if(res != RES_OK) {
    380     log_err(sars, "%s: could not allocate the list of bands (#bands=%lu).\n",
    381       sars_get_name(sars), (unsigned long)nbands);
    382     goto error;
    383   }
    384 
    385   /* Read the band description */
    386   FOR_EACH(iband, 0, nbands) {
    387     struct band* band = darray_band_data_get(&sars->bands) + iband;
    388     res = read_band(sars, band, args->stream);
    389     if(res != RES_OK) goto error;
    390     if(iband > 0 && band[0].low < band[-1].upp) {
    391       log_err(sars,
    392         "%s: bands must be sorted in ascending order and must not "
    393         "overlap (band %lu in [%g, %g[ nm; band %lu in [%g, %g[ nm).\n",
    394         sars_get_name(sars),
    395         (unsigned long)(iband-1), band[-1].low, band[-1].upp,
    396         (unsigned long)(iband),   band[ 0].low, band[ 0].upp);
    397       res = RES_BAD_ARG;
    398       goto error;
    399     }
    400   }
    401 
    402   if(args->memory_mapping) {
    403     res = map_file(sars, args->stream);
    404     if(res != RES_OK) goto error;
    405   } else {
    406     res = load_file(sars, args->stream);
    407     if(res != RES_OK) goto error;
    408   }
    409 
    410 exit:
    411   return res;
    412 error:
    413   reset_sars(sars);
    414   goto exit;
    415 }
    416 
    417 static INLINE int
    418 cmp_band(const void* key, const void* item)
    419 {
    420   const struct band* band = item;
    421   double wnum;
    422   ASSERT(key && item);
    423   wnum = *(double*)key;
    424 
    425   if(wnum < band->low) {
    426     return -1;
    427   } else if(wnum >= band->upp) {
    428     return +1;
    429   } else {
    430     return 0;
    431   }
    432 }
    433 
    434 static INLINE void
    435 hash_band
    436   (struct sha256_ctx* ctx,
    437    const struct sars_band* band,
    438    const size_t nnodes)
    439 {
    440   sha256_ctx_update(ctx, (const char*)&band->lower, sizeof(band->lower));
    441   sha256_ctx_update(ctx, (const char*)&band->upper, sizeof(band->upper));
    442   sha256_ctx_update(ctx, (const char*)&band->id, sizeof(band->id));
    443   sha256_ctx_update(ctx, (const char*)band->k_list, sizeof(*band->k_list)*nnodes);
    444 }
    445 
    446 static void
    447 release_sars(ref_T* ref)
    448 {
    449   struct sars* sars = NULL;
    450   ASSERT(ref);
    451   sars = CONTAINER_OF(ref, struct sars, ref);
    452   if(sars->logger == &sars->logger__) logger_release(&sars->logger__);
    453   str_release(&sars->name);
    454   darray_band_release(&sars->bands);
    455   MEM_RM(sars->allocator, sars);
    456 }
    457 
    458 /*******************************************************************************
    459  * Exported functions
    460  ******************************************************************************/
    461 res_T
    462 sars_create
    463   (const struct sars_create_args* args,
    464    struct sars** out_sars)
    465 {
    466   struct sars* sars = NULL;
    467   struct mem_allocator* allocator = NULL;
    468   res_T res = RES_OK;
    469 
    470   if(!out_sars) { res = RES_BAD_ARG; goto error; }
    471   res = check_sars_create_args(args);
    472   if(res != RES_OK) goto error;
    473 
    474   allocator = args->allocator ? args->allocator : &mem_default_allocator;
    475   sars = MEM_CALLOC(allocator, 1, sizeof(*sars));
    476   if(!sars) {
    477     if(args->verbose) {
    478       #define ERR_STR "Could not allocate the Star-Aerosol device.\n"
    479       if(args->logger) {
    480         logger_print(args->logger, LOG_ERROR, ERR_STR);
    481       } else {
    482         fprintf(stderr, MSG_ERROR_PREFIX ERR_STR);
    483       }
    484       #undef ERR_STR
    485     }
    486     res = RES_MEM_ERR;
    487     goto error;
    488   }
    489 
    490   ref_init(&sars->ref);
    491   sars->allocator = allocator;
    492   sars->verbose = args->verbose;
    493   sars->pagesize_os = (size_t)sysconf(_SC_PAGESIZE);
    494   str_init(allocator, &sars->name);
    495   darray_band_init(allocator, &sars->bands);
    496   if(args->logger) {
    497     sars->logger = args->logger;
    498   } else {
    499     setup_log_default(sars);
    500   }
    501 
    502 exit:
    503   if(out_sars) *out_sars = sars ;
    504   return res;
    505 error:
    506   if(sars) { SARS(ref_put(sars)); sars = NULL; }
    507   goto exit;
    508 }
    509 
    510 res_T
    511 sars_ref_get(struct sars* sars)
    512 {
    513   if(!sars) return RES_BAD_ARG;
    514   ref_get(&sars->ref);
    515   return RES_OK;
    516 }
    517 
    518 res_T
    519 sars_ref_put(struct sars* sars)
    520 {
    521   if(!sars) return RES_BAD_ARG;
    522   ref_put(&sars->ref, release_sars);
    523   return RES_OK;
    524 }
    525 
    526 res_T
    527 sars_load(struct sars* sars, const struct sars_load_args* args)
    528 {
    529   struct sars_load_stream_args stream_args = SARS_LOAD_STREAM_ARGS_NULL;
    530   FILE* file = NULL;
    531   res_T res = RES_OK;
    532 
    533   if(!sars) { res = RES_BAD_ARG; goto error; }
    534   res = check_sars_load_args(args);
    535   if(res != RES_OK) goto error;
    536 
    537   file = fopen(args->path, "r");
    538   if(!file) {
    539     log_err(sars, "%s: error opening file `%s'.\n", FUNC_NAME, args->path);
    540     res = RES_IO_ERR;
    541     goto error;
    542   }
    543 
    544   stream_args.stream = file;
    545   stream_args.name = args->path;
    546   stream_args.memory_mapping = args->memory_mapping;
    547   res = load_stream(sars, &stream_args);
    548   if(res != RES_OK) goto error;
    549 
    550 exit:
    551   if(file) fclose(file);
    552   return res;
    553 error:
    554   goto exit;
    555 }
    556 
    557 res_T
    558 sars_load_stream(struct sars* sars, const struct sars_load_stream_args* args)
    559 {
    560   res_T res = RES_OK;
    561   if(!sars) return RES_BAD_ARG;
    562   res = check_sars_load_stream_args(sars, args);
    563   if(res != RES_OK) return res;
    564   return load_stream(sars, args);
    565 }
    566 
    567 res_T
    568 sars_validate(const struct sars* sars)
    569 {
    570   size_t iband;
    571   size_t nbands;
    572   if(!sars) return RES_BAD_ARG;
    573 
    574   nbands = sars_get_bands_count(sars);
    575   FOR_EACH(iband, 0, nbands) {
    576     struct sars_band band = SARS_BAND_NULL;
    577     size_t inode;
    578     size_t nnodes;
    579 
    580     SARS(get_band(sars, iband, &band));
    581 
    582     /* Check band limits */
    583     if(band.lower != band.lower /* NaN? */
    584     || band.upper != band.upper) { /* NaN? */
    585       log_err(sars,
    586         "%s: invalid limits for band %lu: [%g, %g[\n",
    587         sars_get_name(sars), (unsigned long)iband, band.lower, band.upper);
    588       return RES_BAD_ARG;
    589     }
    590 
    591     /* Check radiative coefficients */
    592     nnodes = sars_get_nodes_count(sars);
    593     FOR_EACH(inode, 0, nnodes) {
    594       const float ka = sars_band_get_ka(&band, inode);
    595       const float ks = sars_band_get_ks(&band, inode);
    596       if(ka != ka /* NaN? */ || ka < 0) {
    597         log_err(sars,
    598           "%s: invalid absorption coefficient for band %lu at node %lu: %g\n",
    599           sars_get_name(sars), (unsigned long)iband, (unsigned long)inode, ka);
    600         return RES_BAD_ARG;
    601       }
    602       if(ks != ks /* NaN? */ || ks < 0) {
    603         log_err(sars,
    604           "%s: invalid scattering coefficient for band %lu at node %lu: %g\n",
    605           sars_get_name(sars), (unsigned long)iband, (unsigned long)inode, ka);
    606         return RES_BAD_ARG;
    607       }
    608     }
    609   }
    610   return RES_OK;
    611 }
    612 
    613 size_t
    614 sars_get_bands_count(const struct sars* sars)
    615 {
    616   ASSERT(sars);
    617   return darray_band_size_get(&sars->bands);
    618 }
    619 
    620 size_t
    621 sars_get_nodes_count(const struct sars* sars)
    622 {
    623   ASSERT(sars);
    624   return sars->nnodes;
    625 }
    626 
    627 res_T
    628 sars_get_band
    629   (const struct sars* sars,
    630    const size_t iband,
    631    struct sars_band* sars_band)
    632 {
    633   const struct band* band = NULL;
    634   res_T res = RES_OK;
    635 
    636   if(!sars || !sars_band) {
    637     res = RES_BAD_ARG;
    638     goto error;
    639   }
    640 
    641   if(iband >= sars_get_bands_count(sars)) {
    642     log_err(sars, "%s: invalid band index %lu.\n",
    643       FUNC_NAME, (unsigned long)iband);
    644     res = RES_BAD_ARG;
    645     goto error;
    646   }
    647 
    648   band = darray_band_cdata_get(&sars->bands) + iband;
    649   sars_band->lower = band->low;
    650   sars_band->upper = band->upp;
    651   sars_band->id = iband;
    652   sars_band->k_list = band->k_list;
    653 
    654 exit:
    655   return res;
    656 error:
    657   goto exit;
    658 }
    659 
    660 res_T
    661 sars_find_bands
    662   (const struct sars* sars,
    663    const double range[2],
    664    size_t ibands[2])
    665 {
    666   const struct band* bands = NULL;
    667   const struct band* low = NULL;
    668   const struct band* upp = NULL;
    669   size_t nbands = 0;
    670   res_T res = RES_OK;
    671 
    672   if(!sars || !range || !ibands || range[0] > range[1]) {
    673     res = RES_BAD_ARG;
    674     goto error;
    675   }
    676 
    677   bands = darray_band_cdata_get(&sars->bands);
    678   nbands = darray_band_size_get(&sars->bands);
    679 
    680   low = search_lower_bound(range+0, bands, nbands, sizeof(*bands), cmp_band);
    681   if(low) {
    682     ibands[0] = (size_t)(low - bands);
    683   } else {
    684     /* The submitted range does not overlap any band */
    685     ibands[0] = SIZE_MAX;
    686     ibands[1] = 0;
    687     goto exit;
    688   }
    689 
    690   if(range[0] == range[1]) { /* No more to search */
    691     if(range[0] <  low->low) {
    692       /* The wavelength is not included in any band */
    693       ibands[0] = SIZE_MAX;
    694       ibands[1] = 0;
    695     } else {
    696       ASSERT(range[0] < low->upp);
    697       ibands[1] = ibands[0];
    698     }
    699     goto exit;
    700   }
    701 
    702   upp = search_lower_bound(range+1, bands, nbands, sizeof(*bands), cmp_band);
    703 
    704   /* The submitted range overlaps the remaining bands */
    705   if(!upp) {
    706     ibands[1] = nbands - 1;
    707 
    708   /* The upper band includes range[1] */
    709   } else if(upp->low <= range[1]) {
    710     ibands[1] = (size_t)(upp - bands);
    711 
    712   /* The upper band is greater than range[1] and therefre must be rejected */
    713   } else if(upp->low > range[1]) {
    714     if(upp != bands) {
    715       ibands[1] = (size_t)(upp - bands - 1);
    716     } else {
    717       ibands[0] = SIZE_MAX;
    718       ibands[1] = 0;
    719     }
    720   }
    721 
    722 exit:
    723   return res;
    724 error:
    725   goto exit;
    726 }
    727 
    728 res_T
    729 sars_band_compute_hash
    730   (const struct sars* sars,
    731    const size_t iband,
    732    hash256_T hash)
    733 {
    734   struct sha256_ctx ctx;
    735   struct sars_band band;
    736   res_T res = RES_OK;
    737 
    738   if(!sars || !hash) {
    739     res = RES_BAD_ARG;
    740     goto error;
    741   }
    742 
    743   res = sars_get_band(sars, iband, &band);
    744   if(res != RES_OK) goto error;
    745 
    746   sha256_ctx_init(&ctx);
    747   hash_band(&ctx, &band, sars->nnodes);
    748   sha256_ctx_finalize(&ctx, hash);
    749 
    750 exit:
    751   return res;
    752 error:
    753   goto exit;
    754 }
    755 
    756 res_T
    757 sars_compute_hash(const struct sars* sars, hash256_T hash)
    758 {
    759   struct sha256_ctx ctx;
    760   size_t i;
    761   res_T res = RES_OK;
    762 
    763   if(!sars || !hash) {
    764     res = RES_BAD_ARG;
    765     goto error;
    766   }
    767 
    768   sha256_ctx_init(&ctx);
    769   sha256_ctx_update(&ctx, (const char*)&sars->pagesize, sizeof(sars->pagesize));
    770   sha256_ctx_update(&ctx, (const char*)&sars->nnodes, sizeof(sars->nnodes));
    771   FOR_EACH(i, 0, darray_band_size_get(&sars->bands)) {
    772     struct sars_band band;
    773     SARS(get_band(sars, i, &band));
    774     hash_band(&ctx, &band, sars->nnodes);
    775   }
    776   sha256_ctx_finalize(&ctx, hash);
    777 
    778 exit:
    779   return res;
    780 error:
    781   goto exit;
    782 }
    783 
    784 const char*
    785 sars_get_name(const struct sars* sars)
    786 {
    787   ASSERT(sars);
    788   return str_cget(&sars->name);
    789 }
    790 
    791 /*******************************************************************************
    792  * Local functions
    793  ******************************************************************************/
    794 void
    795 band_release(struct band* band)
    796 {
    797   ASSERT(band);
    798   if(!band->k_list) return;
    799 
    800   if(!band->map_len) {
    801     MEM_RM(band->sars->allocator, band->k_list);
    802   } else if(band->k_list != MAP_FAILED) {
    803     munmap(band->k_list, band->map_len);
    804   }
    805 }
    806 
    807 res_T
    808 band_copy(struct band* dst, const struct band* src)
    809 {
    810   ASSERT(dst && src);
    811 
    812   dst->sars = src->sars;
    813   dst->low = src->low;
    814   dst->upp = dst->upp;
    815   dst->map_len = src->map_len;
    816   dst->k_list = NULL;
    817 
    818   if(src->map_len) {
    819     /* The k are mapped: copy the pointer */
    820     dst->k_list = src->k_list;
    821   } else if(src->k_list != NULL) {
    822     /* The k are loaded: duplicate thable contents */
    823     const size_t memsz = sizeof(*dst->k_list)*src->sars->nnodes*2/*ka & ks*/;
    824     dst->k_list = MEM_ALLOC(src->sars->allocator, memsz);
    825     if(!dst->k_list) return RES_MEM_ERR;
    826     memcpy(dst->k_list, src->k_list, memsz);
    827   }
    828   return RES_OK;
    829 }
    830 
    831 res_T
    832 band_copy_and_release(struct band* dst, struct band* src)
    833 {
    834   ASSERT(dst && src);
    835   dst->sars = src->sars;
    836   dst->low = src->low;
    837   dst->upp = dst->upp;
    838   dst->map_len = src->map_len;
    839   dst->k_list = src->k_list;
    840   return RES_OK;
    841 }