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