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