atrstm_cache.c (13522B)
1 /* Copyright (C) 2022, 2023 |Méso|Star> (contact@meso-star.com) 2 * Copyright (C) 2020, 2021 Centre National de la Recherche Scientifique 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 16 17 #define _POSIX_C_SOURCE 200112L /* fdopen */ 18 19 #include "atrstm_c.h" 20 #include "atrstm_cache.h" 21 #include "atrstm_log.h" 22 23 #include <astoria/atrri.h> 24 #include <astoria/atrtp.h> 25 26 #include <rsys/cstr.h> 27 #include <rsys/hash.h> 28 #include <rsys/mem_allocator.h> 29 #include <rsys/ref_count.h> 30 #include <rsys/str.h> 31 32 #include <star/suvm.h> 33 34 #include <errno.h> 35 #include <fcntl.h> /* open */ 36 #include <sys/stat.h> /* S_IRUSR & S_IWUSR */ 37 #include <unistd.h> /* close */ 38 #include <string.h> 39 40 struct cache { 41 FILE* stream; 42 struct atrstm* atrstm; 43 struct str name; 44 int empty; 45 ref_T ref; 46 }; 47 48 struct hash { 49 hash256_T therm_props; 50 hash256_T refract_ids; 51 hash256_T volume; 52 }; 53 54 /* Current version the cache memory layout. One should increment it and perform 55 * a version management onto serialized data when the cache data structure is 56 * updated. */ 57 static const int CACHE_VERSION = 1; 58 59 /******************************************************************************* 60 * Helper functions 61 ******************************************************************************/ 62 static res_T 63 hash_compute(struct atrstm* atrstm, struct hash* hash) 64 { 65 struct atrtp_desc atrtp_desc = ATRTP_DESC_NULL; 66 struct atrri_desc atrri_desc = ATRRI_DESC_NULL; 67 res_T res = RES_OK; 68 ASSERT(atrstm && hash); 69 70 res = atrtp_get_desc(atrstm->atrtp, &atrtp_desc); 71 if(res != RES_OK) goto error; 72 res = atrri_get_desc(atrstm->atrri, &atrri_desc); 73 if(res != RES_OK) goto error; 74 75 hash_sha256 76 (atrtp_desc.properties, 77 atrtp_desc.nnodes*sizeof(double[ATRTP_COUNT__]), 78 hash->therm_props); 79 80 hash_sha256 81 (atrri_desc.indices, 82 atrri_desc.nindices*sizeof(struct atrri_refractive_index), 83 hash->refract_ids); 84 85 res = suvm_volume_compute_hash 86 (atrstm->volume, SUVM_POSITIONS|SUVM_INDICES, hash->volume); 87 if(res != RES_OK) goto error; 88 89 exit: 90 return res; 91 error: 92 log_err(atrstm, "Error computing the AtrSTM hash -- %s.\n", 93 res_to_cstr(res)); 94 goto exit; 95 } 96 97 static res_T 98 hash_write(const struct hash* hash, FILE* fp) 99 { 100 res_T res = RES_OK; 101 ASSERT(hash && fp); 102 103 #define WRITE(Var, N) { \ 104 if(fwrite((Var), sizeof(*(Var)), (N), fp) != (N)) { \ 105 res = RES_IO_ERR; \ 106 goto error; \ 107 } \ 108 } (void)0 109 WRITE(hash->therm_props, sizeof(hash256_T)); 110 WRITE(hash->refract_ids, sizeof(hash256_T)); 111 WRITE(hash->volume, sizeof(hash256_T)); 112 #undef WRITE 113 114 exit: 115 return res; 116 error: 117 goto exit; 118 } 119 120 static res_T 121 hash_read(struct hash* hash, FILE* fp) 122 { 123 res_T res = RES_OK; 124 ASSERT(hash && fp); 125 126 #define READ(Var, N) { \ 127 if(fread((Var), sizeof(*(Var)), (N), fp) != (N)) { \ 128 if(feof(fp)) { \ 129 res = RES_BAD_ARG; \ 130 } else if(ferror(fp)) { \ 131 res = RES_IO_ERR; \ 132 } else { \ 133 res = RES_UNKNOWN_ERR; \ 134 } \ 135 goto error; \ 136 } \ 137 } (void)0 138 READ(hash->therm_props, sizeof(hash256_T)); 139 READ(hash->refract_ids, sizeof(hash256_T)); 140 READ(hash->volume, sizeof(hash256_T)); 141 #undef READ 142 143 exit: 144 return res; 145 error: 146 goto exit; 147 } 148 149 /* Setup the cache header, i.e. data that uniquely identify the cache regarding 150 * the input data */ 151 static res_T 152 write_cache_header(struct cache* cache) 153 { 154 struct hash hash; 155 res_T res = RES_OK; 156 ASSERT(cache); 157 158 #define WRITE(Var, N) { \ 159 if(fwrite((Var), sizeof(*(Var)), (N), cache->stream) != (N)) { \ 160 log_err(cache->atrstm, \ 161 "%s: could not write the cache header.\n", cache_get_name(cache)); \ 162 res = RES_IO_ERR; \ 163 goto error; \ 164 } \ 165 } (void)0 166 WRITE(&CACHE_VERSION, 1); 167 WRITE(&cache->atrstm->fractal_prefactor, 1); 168 WRITE(&cache->atrstm->fractal_dimension, 1); 169 WRITE(&cache->atrstm->spectral_type, 1); 170 WRITE(cache->atrstm->wlen_range, 2); 171 WRITE(cache->atrstm->grid_max_definition, 3); 172 WRITE(&cache->atrstm->optical_thickness, 1); 173 WRITE(&cache->atrstm->use_simd, 1); 174 #undef WRITE 175 176 res = hash_compute(cache->atrstm, &hash); 177 if(res != RES_OK) goto error; 178 res = hash_write(&hash, cache->stream); 179 if(res != RES_OK) goto error; 180 181 exit: 182 return res; 183 error: 184 goto exit; 185 } 186 187 static res_T 188 read_cache_header(struct cache* cache) 189 { 190 struct hash cached_hash; 191 struct hash current_hash; 192 double fractal_prefactor = 0; 193 double fractal_dimension = 0; 194 enum atrstm_spectral_type spectral_type = ATRSTM_SPECTRAL_TYPES_COUNT__; 195 double wlen_range[2] = {0,0}; 196 unsigned grid_max_definition[3] = {0,0,0}; 197 double optical_thickness = 0; 198 int use_simd = 0; 199 int cache_version = 0; 200 res_T res = RES_OK; 201 ASSERT(cache); 202 203 /* Read the cache header */ 204 #define READ(Var, N) { \ 205 if(fread((Var), sizeof(*(Var)), (N), cache->stream) != (N)) { \ 206 if(feof(cache->stream)) { \ 207 res = RES_BAD_ARG; \ 208 } else if(ferror(cache->stream)) { \ 209 res = RES_IO_ERR; \ 210 } else { \ 211 res = RES_UNKNOWN_ERR; \ 212 } \ 213 log_err(cache->atrstm, "%s: could not read the cache header -- %s.\n", \ 214 cache_get_name(cache), res_to_cstr(res)); \ 215 goto error; \ 216 } \ 217 } (void)0 218 219 READ(&cache_version, 1); 220 if(cache_version != CACHE_VERSION) { 221 log_err(cache->atrstm, 222 "%s: invalid cache in version %d. Expecting a cache in version %d.\n", 223 cache_get_name(cache), cache_version, CACHE_VERSION); 224 res = RES_BAD_ARG; 225 goto error; 226 } 227 #define CHK_VAR(CachedVal, CurrentVal, Name, Fmt) { \ 228 if((CachedVal) != (CurrentVal)) { \ 229 log_err(cache->atrstm, \ 230 "%s: invalid cache regarding the "Name". " \ 231 "Cached value: "Fmt". Current Value: "Fmt".\n", \ 232 cache_get_name(cache), (CachedVal), (CurrentVal)); \ 233 res = RES_BAD_ARG; \ 234 goto error; \ 235 } \ 236 } (void)0 237 238 READ(&fractal_prefactor, 1); 239 CHK_VAR(fractal_prefactor, cache->atrstm->fractal_prefactor, 240 "fractal prefactor", "%g"); 241 242 READ(&fractal_dimension, 1); 243 CHK_VAR(fractal_dimension, cache->atrstm->fractal_dimension, 244 "fractal dimension", "%g"); 245 246 READ(&spectral_type, 1); 247 CHK_VAR(spectral_type, cache->atrstm->spectral_type, 248 "spectral type", "%i"); 249 250 READ(wlen_range, 2); 251 CHK_VAR(wlen_range[0], cache->atrstm->wlen_range[0], 252 "spectral lower bound", "%g"); 253 CHK_VAR(wlen_range[1], cache->atrstm->wlen_range[1], 254 "spectral upper bound", "%g"); 255 256 READ(grid_max_definition, 3); 257 CHK_VAR(grid_max_definition[0], cache->atrstm->grid_max_definition[0], 258 "grid X definition", "%u"); 259 CHK_VAR(grid_max_definition[1], cache->atrstm->grid_max_definition[1], 260 "grid Y definition", "%u"); 261 CHK_VAR(grid_max_definition[2], cache->atrstm->grid_max_definition[2], 262 "grid Z definition", "%u"); 263 264 READ(&optical_thickness, 1); 265 CHK_VAR(optical_thickness, cache->atrstm->optical_thickness, 266 "optical thickness", "%g"); 267 268 READ(&use_simd, 1); 269 CHK_VAR(use_simd, cache->atrstm->use_simd, "use_simd flag", "%i"); 270 271 #undef CHK_VAR 272 273 res = hash_read(&cached_hash, cache->stream); 274 if(res != RES_OK) goto error; 275 res = hash_compute(cache->atrstm, ¤t_hash); 276 if(res != RES_OK) goto error; 277 278 #define CHK_HASH(CachedHash, CurrentHash, Name) { \ 279 if(!hash256_eq((CachedHash), (CurrentHash))) { \ 280 log_err(cache->atrstm, \ 281 "%s: invalid cache regarding the submitted "Name".\n", \ 282 cache_get_name(cache)); \ 283 res = RES_BAD_ARG; \ 284 goto error; \ 285 } \ 286 } (void)0 287 CHK_HASH(cached_hash.therm_props, current_hash.therm_props, 288 "thermodynamic properties"); 289 CHK_HASH(cached_hash.refract_ids, current_hash.refract_ids, 290 "refractive indices"); 291 CHK_HASH(cached_hash.volume, current_hash.volume, 292 "volumetric mesh"); 293 #undef CHK_HASH 294 295 exit: 296 return res; 297 error: 298 goto exit; 299 } 300 301 static void 302 release_cache(ref_T* ref) 303 { 304 struct cache* cache = NULL; 305 struct atrstm* atrstm = NULL; 306 ASSERT(ref); 307 cache = CONTAINER_OF(ref, struct cache, ref); 308 atrstm = cache->atrstm; 309 if(cache->stream) CHK(fclose(cache->stream) == 0); 310 str_release(&cache->name); 311 MEM_RM(atrstm->allocator, cache); 312 } 313 314 /******************************************************************************* 315 * Local functions 316 ******************************************************************************/ 317 res_T 318 cache_create 319 (struct atrstm* atrstm, 320 const char* filename, 321 struct cache** out_cache) 322 { 323 struct cache* cache = NULL; 324 struct stat cache_stat; 325 int fd = -1; 326 int err = 0; 327 res_T res = RES_OK; 328 ASSERT(atrstm && filename && out_cache); 329 330 cache = MEM_CALLOC(atrstm->allocator, 1, sizeof(*cache)); 331 if(!cache) { 332 res = RES_MEM_ERR; 333 goto error; 334 } 335 ref_init(&cache->ref); 336 str_init(atrstm->allocator, &cache->name); 337 cache->atrstm = atrstm; 338 339 res = str_set(&cache->name, filename); 340 if(res != RES_OK) { 341 log_err(atrstm, "Could not copy the cache filename -- %s.\n", 342 res_to_cstr(res)); 343 goto error; 344 } 345 346 fd = open(filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); 347 if(fd < 0) { 348 log_err(atrstm, "Could not open the cache file `%s' -- %s.\n", 349 filename, strerror(errno)); 350 res = RES_IO_ERR; 351 goto error; 352 } 353 354 cache->stream = fdopen(fd, "w+"); 355 if(!cache->stream) { 356 log_err(atrstm, "Could not open the cache file `%s' -- %s.\n", 357 filename, strerror(errno)); 358 res = RES_IO_ERR; 359 goto error; 360 } 361 fd = -1; 362 363 err = stat(filename, &cache_stat); 364 if(err < 0) { 365 log_err(atrstm, "Could not stat the cache file `%s' -- %s.\n", 366 filename, strerror(errno)); 367 res = RES_IO_ERR; 368 goto error; 369 } 370 371 if(cache_stat.st_size == 0) { /* Empty cache */ 372 cache->empty = 1; 373 res = write_cache_header(cache); 374 if(res != RES_OK) goto error; 375 } else { /* Cache already exists */ 376 cache->empty = 0; 377 res = read_cache_header(cache); 378 if(res != RES_OK) goto error; 379 } 380 381 exit: 382 *out_cache = cache; 383 return res; 384 error: 385 if(cache) { cache_ref_put(cache); cache = NULL; } 386 if(fd >= 0) CHK(close(fd) == 0); 387 goto exit; 388 } 389 390 void 391 cache_ref_get(struct cache* cache) 392 { 393 ASSERT(cache); 394 ref_get(&cache->ref); 395 } 396 397 void 398 cache_ref_put(struct cache* cache) 399 { 400 ASSERT(cache); 401 ref_put(&cache->ref, release_cache); 402 } 403 404 FILE* 405 cache_get_stream(struct cache* cache) 406 { 407 ASSERT(cache); 408 return cache->stream; 409 } 410 411 int 412 cache_is_empty(const struct cache* cache) 413 { 414 ASSERT(cache); 415 return cache->empty; 416 } 417 418 const char* 419 cache_get_name(const struct cache* cache) 420 { 421 ASSERT(cache); 422 return str_cget(&cache->name); 423 }