shtr_line_list.c (24331B)
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 res_T 178 line_encode 179 (const struct shtr* shtr, /* For logging */ 180 const struct txtrdr* txtrdr, /* For logging */ 181 const struct shtr_line* line, 182 struct line* line_encoded) 183 { 184 union { float flt; int32_t i32; } ucast; 185 res_T res = RES_OK; 186 ASSERT(line && line_encoded); 187 188 /* Keep the wavenumber and the intensity as it */ 189 line_encoded->wavenumber = line->wavenumber; 190 line_encoded->intensity = line->intensity; 191 192 /* Encode the lower state energy and delta air in single precision */ 193 line_encoded->lower_state_energy = (float)line->lower_state_energy; 194 line_encoded->delta_air = (float)line->delta_air; 195 196 /* Store gamma_air as an unsigned fixed-point number (0:14), i.e., 0 bit for 197 * the integer part and 14 bits for the fractional part */ 198 if((int64_t)line->gamma_air != 0) { 199 WARN(shtr, 200 "%s:%lu: invalid gamma air %g: encoding expects a value in [0,1[\n", 201 txtrdr_get_name(txtrdr), txtrdr_get_line_num(txtrdr), line->gamma_air); 202 res = RES_BAD_OP; 203 } else { 204 ASSERT(line->gamma_air >= 0); 205 line_encoded->gair14_gself14_isoid4 = 206 ((int32_t)float2fix(line->gamma_air, 0, 14) & (BIT_I32(14)-1)) << 18; 207 } 208 209 /* Store gamma_self as an unsigned fixed-point number (3:11), i.e., 3 bit for 210 * the integer part and 11 bits for the fractional part */ 211 if((int64_t)line->gamma_self > 8) { 212 WARN(shtr, 213 "%s:%lu: invalid gamma self %g: encoding expects a value in [0,8[\n", 214 txtrdr_get_name(txtrdr), txtrdr_get_line_num(txtrdr), line->gamma_self); 215 res = RES_BAD_OP; 216 } else { 217 ASSERT(line->gamma_self >= 0); 218 line_encoded->gair14_gself14_isoid4 |= 219 ((int32_t)float2fix(line->gamma_self, 3, 11) & (BIT_I32(14)-1)) << 4; 220 } 221 222 /* Store the isotope id on 4 bits */ 223 ASSERT(line->isotope_id_local < 16); 224 line_encoded->gair14_gself14_isoid4 |= line->isotope_id_local & (BIT_I32(4)-1); 225 226 /* Encode n_air in single precision with its last 7 bits of matissa disabled 227 * in order to store the molecule identifier */ 228 ucast.flt = (float)line->n_air; 229 ucast.i32 &= ~(BIT_I32(7)-1); 230 231 /* Store the molecule id on 7 bits */ 232 ASSERT(line->molecule_id < 128); 233 ucast.i32 |= (int32_t)line->molecule_id & (BIT_I32(7)-1); 234 line_encoded->nair25_molid7 = ucast.i32; 235 236 return res; 237 } 238 239 static void 240 line_decode(const struct line* line_encoded, struct shtr_line* line) 241 { 242 union { float flt; int32_t i32; } ucast; 243 int64_t i64 = 0; 244 ASSERT(line && line_encoded); 245 246 line->wavenumber = line_encoded->wavenumber; 247 line->intensity = line_encoded->intensity; 248 line->lower_state_energy = line_encoded->lower_state_energy; 249 line->delta_air = line_encoded->delta_air; 250 251 /* Convert gamma_air and gamma_self from fixed-point numbers on 14 bits to 252 * double-precision floating-point numbers. Gamma air is encode un (0:14) and 253 * gamma self in (3:11) */ 254 i64 = (line_encoded->gair14_gself14_isoid4 >> 18) & (BIT_I32(14)-1); 255 line->gamma_air = fix2float(i64, 0, 14); 256 i64 = (line_encoded->gair14_gself14_isoid4 >> 4) & (BIT_I32(14)-1); 257 line->gamma_self = fix2float(i64, 3, 11); 258 259 /* Unpack the isotope ID */ 260 line->isotope_id_local = line_encoded->gair14_gself14_isoid4 & (BIT_I32(4)-1); 261 262 /* Unpack the molecule ID */ 263 ucast.i32 = line_encoded->nair25_molid7; 264 line->molecule_id = ucast.i32 & (BIT_I32(7)-1); 265 ucast.i32 &= ~(BIT_I32(7)-1); 266 267 /* Convert n_air from single precision to double precision */ 268 line->n_air = ucast.flt; 269 } 270 271 static res_T 272 register_line 273 (struct shtr_line_list* list, 274 const struct txtrdr* txtrdr, 275 const struct shtr_line* line) 276 { 277 struct shtr_line ln = SHTR_LINE_NULL; 278 struct line* lines = NULL; 279 struct line ln_encoded = LINE_NULL; 280 size_t iblock = 0; /* Index of the block in which the line is stored */ 281 size_t iline = 0; /* Index of the line in the block */ 282 res_T res = RES_OK; 283 284 /* Pre-conditions */ 285 ASSERT(list && txtrdr && line); 286 287 res = line_encode(list->shtr, txtrdr, line, &ln_encoded); 288 switch(res) { 289 case RES_OK: /* nothing to do */; break; 290 case RES_BAD_OP: 291 WARN(list->shtr, "%s:%lu: line discarded\n", 292 txtrdr_get_name(txtrdr), txtrdr_get_line_num(txtrdr)); 293 res = RES_OK; 294 goto exit; 295 default: goto error; 296 } 297 298 /* Check if a line has been saved. If so, ensure that the lines are sorted */ 299 if(list->nlines) { 300 const struct line* ln_encoded_prev = get_line(list, list->nlines-1); 301 if(ln_encoded_prev->wavenumber > ln_encoded.wavenumber) { 302 ERROR(list->shtr, 303 "%s:%lu: lines are not sorted in ascending order wrt their wavenumber.\n", 304 txtrdr_get_name(txtrdr), txtrdr_get_line_num(txtrdr)); 305 res = RES_BAD_ARG; 306 goto error; 307 } 308 } 309 310 iblock = list->nlines / NLINES_PER_BLOCK; 311 iline = list->nlines % NLINES_PER_BLOCK; 312 313 /* Ensure there is sufficient space to store the line */ 314 if(iline == 0) { 315 /* There is no more space in the last allocated block. Allocate a new one. */ 316 char* block = MEM_CALLOC(list->shtr->allocator, 1, BLOCK_SIZE); 317 if(!block) { res = RES_MEM_ERR; goto error; } 318 319 res = darray_charp_push_back(&list->blocks, &block); 320 if(res != RES_OK) goto error; 321 } 322 323 /* Store the encoded line */ 324 lines = (struct line*)darray_charp_data_get(&list->blocks)[iblock]; 325 lines[iline] = ln_encoded; 326 ++list->nlines; 327 328 line_decode(&ln_encoded, &ln); 329 ASSERT(ln.molecule_id == line->molecule_id); 330 ASSERT(ln.isotope_id_local == line->isotope_id_local); 331 332 #define UPDATE_INFO(Name) { \ 333 const double err = fabs(line->Name - ln.Name); \ 334 list->info.Name.range[0] = MMIN(list->info.Name.range[0], ln.Name); \ 335 list->info.Name.range[1] = MMAX(list->info.Name.range[1], ln.Name); \ 336 list->info.Name.err = MMAX(list->info.Name.err, err); \ 337 } (void) 0 338 UPDATE_INFO(wavenumber); 339 UPDATE_INFO(intensity); 340 UPDATE_INFO(gamma_air); 341 UPDATE_INFO(gamma_self); 342 UPDATE_INFO(lower_state_energy); 343 UPDATE_INFO(n_air); 344 UPDATE_INFO(delta_air); 345 #undef UPDATE_INFO 346 347 exit: 348 return res; 349 error: 350 goto exit; 351 } 352 353 static res_T 354 parse_line 355 (struct shtr_line_list* list, 356 struct txtrdr* txtrdr, 357 struct shtr_line* ln) 358 { 359 struct param_desc param = PARAM_DESC_NULL; 360 struct shtr* shtr = NULL; 361 char* line = NULL; 362 char* str = NULL; 363 char* end = NULL; 364 char backup; 365 int molecule_id; 366 int isotope_id_local; 367 res_T res = RES_OK; 368 369 ASSERT(list && txtrdr && ln); 370 371 line = txtrdr_get_line(txtrdr); 372 ASSERT(line); 373 374 shtr = list->shtr; 375 param.path = txtrdr_get_name(txtrdr); 376 param.line = txtrdr_get_line_num(txtrdr); 377 378 str = end = line; 379 backup = str[0]; 380 #define NEXT(Size) { \ 381 *end = backup; \ 382 str = end; \ 383 end = str+(Size); \ 384 backup = *end; \ 385 *end = '\0'; \ 386 } (void)0 387 #define PARSE(Var, Size, Type, Name, Low, Upp, LowIncl, UppIncl) { \ 388 NEXT(Size); \ 389 param.name = (Name); \ 390 param.low = (Low); \ 391 param.upp = (Upp); \ 392 param.is_low_incl = (LowIncl); \ 393 param.is_upp_incl = (UppIncl); \ 394 res = parse_param_##Type(shtr, str, ¶m, Var); \ 395 if(res != RES_OK) goto error; \ 396 } (void)0 397 398 PARSE(&molecule_id, 2, int, "molecule identifier", 0,99,1,1); 399 ln->molecule_id = (int32_t)molecule_id; 400 401 PARSE(&isotope_id_local, 1, int, "isotope local identifier", 0,9,1,1); 402 ln->isotope_id_local = (int32_t) 403 (isotope_id_local == 0 ? 9 : (isotope_id_local - 1)); 404 405 PARSE(&ln->wavenumber, 12, double, "central wavenumber", 0,INF,0,1); 406 PARSE(&ln->intensity, 10, double, "reference intensity", 0,INF,0,1); 407 408 NEXT(10); /* Skip the Enstein coef */ 409 410 PARSE(&ln->gamma_air, 5, double, "air broadening half-width", 0,INF,1,1); 411 PARSE(&ln->gamma_self, 5, double, "self broadening half-width", 0,INF,1,1); 412 413 /* Handle unavailable lower state energy */ 414 PARSE(&ln->lower_state_energy, 10, double, "lower state energy",-INF,INF,1,1); 415 if(ln->lower_state_energy == -1) { 416 WARN(shtr, 417 "%s:%lu: the lower state energy is unavailable for this line, so it is " 418 "ignored.\n", txtrdr_get_name(txtrdr), txtrdr_get_line_num(txtrdr)); 419 goto exit; /* Skip the line */ 420 } 421 /* Check the domain validity */ 422 if(ln->lower_state_energy < 0) { 423 ERROR(shtr, 424 "%s:%lu: invalid lower state energy %g. It must be in [0, INF].\n", 425 txtrdr_get_name(txtrdr), txtrdr_get_line_num(txtrdr), 426 ln->lower_state_energy); 427 res = RES_BAD_ARG; 428 goto error; 429 } 430 431 PARSE(&ln->n_air, 4, double, "temperature-dependent exponent",-INF,INF,1,1); 432 PARSE(&ln->delta_air, 8, double, "air-pressure wavenumber shift", -INF,INF,1,1); 433 434 /* Skip the remaining values */ 435 436 #undef NEXT 437 #undef PARSE 438 439 /* Check the size of the remaining data to ensure that there is at least the 440 * expected number of bytes wrt the HITRAN fileformat */ 441 *end = backup; 442 str = end; 443 if(strlen(str) != 93) { 444 ERROR(list->shtr, "%s:%lu: missing data after delta air.\n", 445 param.path, (unsigned long)param.line); 446 res = RES_BAD_ARG; 447 goto error; 448 } 449 450 exit: 451 return res; 452 error: 453 goto exit; 454 } 455 456 static res_T 457 load_stream 458 (struct shtr* shtr, 459 const struct shtr_line_list_load_args* args, 460 struct shtr_line_list** out_lines) 461 { 462 struct stream stream = STREAM_NULL; 463 struct shtr_line_list* list = NULL; 464 struct txtrdr* txtrdr = NULL; 465 res_T res = RES_OK; 466 467 ASSERT(shtr && args && out_lines); 468 469 res = create_line_list(shtr, &list); 470 if(res != RES_OK) goto error; 471 472 res = stream_init(shtr, FUNC_NAME, args->filename, args->file, "r", &stream); 473 if(res != RES_OK) goto error; 474 475 res = txtrdr_stream(list->shtr->allocator, stream.fp, stream.name, 476 0/*No comment char*/, &txtrdr); 477 if(res != RES_OK) { 478 ERROR(shtr, "%s: error creating the text reader -- %s.\n", 479 stream.name, res_to_cstr(res)); 480 goto error; 481 } 482 483 for(;;) { 484 struct shtr_line ln = SHTR_LINE_NULL; 485 486 res = txtrdr_read_line(txtrdr); 487 if(res != RES_OK) { 488 ERROR(shtr, "%s: error reading the line `%lu' -- %s.\n", 489 stream.name, (unsigned long)txtrdr_get_line_num(txtrdr), 490 res_to_cstr(res)); 491 goto error; 492 } 493 494 if(!txtrdr_get_cline(txtrdr)) break; /* No more parsed line */ 495 496 res = parse_line(list, txtrdr, &ln); 497 if(res != RES_OK) goto error; 498 499 res = register_line(list, txtrdr, &ln); 500 if(res != RES_OK) goto error; 501 } 502 503 exit: 504 if(txtrdr) txtrdr_ref_put(txtrdr); 505 stream_release(&stream); 506 *out_lines = list; 507 return res; 508 error: 509 if(list) { 510 SHTR(line_list_ref_put(list)); 511 list = NULL; 512 } 513 goto exit; 514 } 515 516 static void 517 hash_blocks(const struct shtr_line_list* list, struct sha256_ctx* ctx) 518 { 519 size_t i = 0; 520 size_t n = 0; 521 ASSERT(list && ctx); 522 523 n = darray_charp_size_get(&list->blocks); 524 FOR_EACH(i, 0, n) { 525 const char* block = darray_charp_cdata_get(&list->blocks)[i]; 526 const size_t nlines = MMIN(NLINES_PER_BLOCK, list->nlines-i*NLINES_PER_BLOCK); 527 528 sha256_ctx_update(ctx, block, nlines*sizeof(struct line)); 529 } 530 } 531 532 static void 533 release_lines(ref_T * ref) 534 { 535 struct shtr* shtr = NULL; 536 struct shtr_line_list* list = CONTAINER_OF(ref, struct shtr_line_list, ref); 537 char** blocks = NULL; 538 size_t i=0, n=0; 539 540 ASSERT(ref); 541 542 shtr = list->shtr; 543 544 n = darray_charp_size_get(&list->blocks); 545 blocks = darray_charp_data_get(&list->blocks); 546 FOR_EACH(i, 0, n) { if(blocks[i]) MEM_RM(shtr->allocator, blocks[i]); } 547 548 darray_charp_release(&list->blocks); 549 MEM_RM(shtr->allocator, list); 550 SHTR(ref_put(shtr)); 551 } 552 553 /******************************************************************************* 554 * Exported functions 555 ******************************************************************************/ 556 res_T 557 shtr_line_list_load 558 (struct shtr* shtr, 559 const struct shtr_line_list_load_args* args, 560 struct shtr_line_list** list) 561 { 562 res_T res = RES_OK; 563 564 if(!shtr || !list) { res = RES_BAD_ARG; goto error; } 565 res = check_shtr_line_list_load_args(args); 566 if(res != RES_OK) goto error; 567 568 res = load_stream(shtr, args, list); 569 if(res != RES_OK) goto error; 570 571 exit: 572 return res; 573 error: 574 goto exit; 575 } 576 577 res_T 578 shtr_line_list_read 579 (struct shtr* shtr, 580 const struct shtr_line_list_read_args* args, 581 struct shtr_line_list** out_list) 582 { 583 size_t line_range[2]={0,0}; /* Range of lines adapted to effective line ids */ 584 size_t nlines = 0; /* Ttotal number of lines in the original list */ 585 size_t nblocks = 0; /* Number of memory blocks needed to store read lines */ 586 587 struct shtr_line_list* list = NULL; /* The output list */ 588 char** blocks = NULL; /* Allocated list of memory blocks */ 589 590 /* Miscellaneous */ 591 struct stream stream = STREAM_NULL; 592 size_t i = 0; 593 size_t sz = 0; 594 size_t sz_to_load = 0; 595 off_t off; 596 int version = 0; 597 int err = 0; 598 res_T res = RES_OK; 599 600 if(!shtr || !out_list) { res = RES_BAD_ARG; goto error; } 601 res = check_shtr_line_list_read_args(args); 602 if(res != RES_OK) goto error; 603 604 res = create_line_list(shtr, &list); 605 if(res != RES_OK) goto error; 606 607 res = stream_init 608 (list->shtr, FUNC_NAME, args->filename, args->file, "r", &stream); 609 if(res != RES_OK) goto error; 610 611 #define READ(Var, Nb) { \ 612 if(fread((Var), sizeof(*(Var)), (Nb), stream.fp) != (Nb)) { \ 613 if(feof(stream.fp)) { \ 614 res = RES_BAD_ARG; \ 615 } else if(ferror(stream.fp)) { \ 616 res = RES_IO_ERR; \ 617 } else { \ 618 res = RES_UNKNOWN_ERR; \ 619 } \ 620 ERROR(shtr, \ 621 "%s:%s: error reading line list -- %s.\n", \ 622 FUNC_NAME, stream.name, res_to_cstr(res)); \ 623 goto error; \ 624 } \ 625 } (void)0 626 627 READ(&version, 1); 628 if(version != SHTR_LINE_LIST_VERSION) { 629 ERROR(shtr, 630 "%s:%s: unexpected line list version %d. " 631 "Expecting a line list in version %d.\n", 632 FUNC_NAME, stream.name, version, SHTR_LINE_LIST_VERSION); 633 res = RES_BAD_ARG; 634 goto error; 635 } 636 637 /* Informations on line parameters */ 638 READ(&list->info, 1); 639 640 /* Total number of lines in the list from which the lines will be read */ 641 READ(&nlines, 1); 642 643 /* Actually, there are no lines to read */ 644 if(nlines <= args->range[0]) goto exit; 645 646 /* Fit the upper limit to the effective number of lines */ 647 line_range[0] = args->range[0]; 648 line_range[1] = MMIN(args->range[1], nlines-1/*inclusive bounds*/); 649 list->nlines = line_range[1] - line_range[0] + 1/*inclusive bounds*/; 650 651 /* Calculate the number of blocks needed to store the line to be read */ 652 nblocks = (list->nlines + (NLINES_PER_BLOCK-1)/*ceil*/) / NLINES_PER_BLOCK; 653 if((res = darray_charp_resize(&list->blocks, nblocks)) != RES_OK) goto error; 654 655 /* Compute the offset toward the first line to load */ 656 sz = line_range[0] * sizeof(struct line); 657 res = size_to_off(sz, &off); 658 if(res != RES_OK) { 659 ERROR(shtr, "%s:%s: file is too large regarding the seek offset %zu\n", 660 FUNC_NAME, stream.name, sz); 661 goto error; 662 } 663 664 /* Move to the first line to load */ 665 err = fseeko(stream.fp, off, SEEK_CUR); 666 if(err) { 667 ERROR(shtr, "%s:%s: %s\n", FUNC_NAME, stream.name, strerror(errno)); 668 res = RES_IO_ERR; 669 goto error; 670 } 671 672 /* Compute the overall number of bytes to load */ 673 sz_to_load = list->nlines * sizeof(struct line); 674 675 /* Load line data and store them in memory blocks */ 676 blocks = darray_charp_data_get(&list->blocks); 677 FOR_EACH(i, 0, nblocks) { 678 blocks[i] = MEM_CALLOC(list->shtr->allocator, 1, BLOCK_SIZE); 679 if(!blocks[i]) { 680 ERROR(shtr, "%s:%s: error allocating memory block\n", 681 stream.name, FUNC_NAME); 682 res = RES_MEM_ERR; 683 goto error; 684 } 685 686 sz = MMIN(BLOCK_SIZE, sz_to_load); 687 READ(blocks[i], sz); 688 689 sz_to_load -= sz; 690 } 691 692 #undef READ 693 694 exit: 695 stream_release(&stream); 696 if(out_list) *out_list = list; 697 return res; 698 error: 699 if(list) { SHTR(line_list_ref_put(list)); list = NULL; } 700 goto exit; 701 } 702 703 res_T 704 shtr_line_list_ref_get(struct shtr_line_list* list) 705 { 706 if(!list) return RES_BAD_ARG; 707 ref_get(&list->ref); 708 return RES_OK; 709 } 710 711 res_T 712 shtr_line_list_ref_put(struct shtr_line_list* list) 713 { 714 if(!list) return RES_BAD_ARG; 715 ref_put(&list->ref, release_lines); 716 return RES_OK; 717 } 718 719 res_T 720 shtr_line_list_get_size 721 (const struct shtr_line_list* list, 722 size_t* nlines) 723 { 724 if(!list || !nlines) return RES_BAD_ARG; 725 *nlines = list->nlines; 726 return RES_OK; 727 } 728 729 res_T 730 shtr_line_list_at 731 (struct shtr_line_list* list, 732 const size_t i, 733 struct shtr_line* line) 734 { 735 const struct line* ln_encoded = NULL; 736 737 if(!list || !line || i >= list->nlines) return RES_BAD_ARG; 738 ln_encoded = get_line(list, i); 739 line_decode(ln_encoded, line); 740 return RES_OK; 741 } 742 743 res_T 744 shtr_line_list_write 745 (const struct shtr_line_list* list, 746 const struct shtr_line_list_write_args* args) 747 { 748 struct stream stream = STREAM_NULL; 749 char* const* blocks = NULL; 750 size_t i=0, n=0; 751 res_T res = RES_OK; 752 753 if(!list) { res = RES_BAD_ARG; goto error; } 754 res = check_shtr_line_list_write_args(args); 755 if(res != RES_OK) goto error; 756 757 res = stream_init 758 (list->shtr, FUNC_NAME, args->filename, args->file, "w", &stream); 759 if(res != RES_OK) goto error; 760 761 #define WRITE(Var, Nb) { \ 762 if(fwrite((Var), sizeof(*(Var)), (Nb), stream.fp) != (Nb)) { \ 763 ERROR(list->shtr, "%s:%s: error writing line list -- %s\n", \ 764 FUNC_NAME, stream.name, strerror(errno)); \ 765 res = RES_IO_ERR; \ 766 goto error; \ 767 } \ 768 } (void)0 769 770 /* Version management */ 771 WRITE(&SHTR_LINE_LIST_VERSION, 1); 772 773 /* Informations on line parameters */ 774 WRITE(&list->info, 1); 775 776 /* Number of lines in the list */ 777 WRITE(&list->nlines, 1); 778 779 /* Lines stored in memory blocks. */ 780 blocks = darray_charp_cdata_get(&list->blocks); 781 n = darray_charp_size_get(&list->blocks); 782 FOR_EACH(i, 0, n) { WRITE(blocks[i], BLOCK_SIZE); } 783 784 #undef WRITE 785 786 exit: 787 stream_release(&stream); 788 return res; 789 error: 790 goto exit; 791 } 792 793 res_T 794 shtr_line_list_get_info 795 (const struct shtr_line_list* list, 796 struct shtr_line_list_info* info) 797 { 798 if(!list || !info) return RES_BAD_ARG; 799 *info = list->info; 800 return RES_OK; 801 } 802 803 res_T 804 shtr_line_list_hash(const struct shtr_line_list* list, hash256_T hash) 805 { 806 struct sha256_ctx ctx; 807 res_T res = RES_OK; 808 809 if(!list || !hash) { res = RES_BAD_ARG; goto error; } 810 811 sha256_ctx_init(&ctx); 812 hash_blocks(list, &ctx); 813 sha256_ctx_finalize(&ctx, hash); 814 815 exit: 816 return res; 817 error: 818 goto exit; 819 }