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, ¶m, 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 }