/src/netcdf-c/libnczarr/zxcache.c
| Line | Count | Source | 
| 1 |  | /* Copyright 2018, University Corporation for Atmospheric | 
| 2 |  |  * Research. See COPYRIGHT file for copying and redistribution | 
| 3 |  |  * conditions. */ | 
| 4 |  |  | 
| 5 |  | /** | 
| 6 |  |  * @file @internal The functions which control NCZ | 
| 7 |  |  * caching. These caching controls allow the user to change the cache | 
| 8 |  |  * sizes of ZARR before opening files. | 
| 9 |  |  * | 
| 10 |  |  * @author Dennis Heimbigner, Ed Hartnett | 
| 11 |  |  */ | 
| 12 |  |  | 
| 13 |  | #include "zincludes.h" | 
| 14 |  | #include "zcache.h" | 
| 15 |  | #include "ncxcache.h" | 
| 16 |  | #include "zfilter.h" | 
| 17 |  | #include <stddef.h> | 
| 18 |  |  | 
| 19 |  | #undef DEBUG | 
| 20 |  |  | 
| 21 |  | #undef FLUSH | 
| 22 |  |  | 
| 23 | 0 | #define LEAFLEN 32 | 
| 24 |  |  | 
| 25 | 0 | #define USEPARAMSIZE 0xffffffffffffffff | 
| 26 |  |  | 
| 27 |  | /* Forward */ | 
| 28 |  | static int get_chunk(NCZChunkCache* cache, NCZCacheEntry* entry); | 
| 29 |  | static int put_chunk(NCZChunkCache* cache, NCZCacheEntry*); | 
| 30 |  | static int verifycache(NCZChunkCache* cache); | 
| 31 |  | static int flushcache(NCZChunkCache* cache); | 
| 32 |  | static int constraincache(NCZChunkCache* cache, size64_t needed); | 
| 33 |  |  | 
| 34 |  | static void | 
| 35 |  | setmodified(NCZCacheEntry* e, int tf) | 
| 36 | 0 | { | 
| 37 | 0 |     e->modified = tf; | 
| 38 | 0 | } | 
| 39 |  |  | 
| 40 |  | /**************************************************/ | 
| 41 |  | /* Dispatch table per-var cache functions */ | 
| 42 |  |  | 
| 43 |  | /** | 
| 44 |  |  * @internal Set chunk cache size for a variable. This is the internal | 
| 45 |  |  * function called by nc_set_var_chunk_cache(). | 
| 46 |  |  * | 
| 47 |  |  * @param ncid File ID. | 
| 48 |  |  * @param varid Variable ID. | 
| 49 |  |  * @param size Size in bytes to set cache. | 
| 50 |  |  * @param nelems # of entries in cache | 
| 51 |  |  * @param preemption Controls cache swapping. | 
| 52 |  |  * | 
| 53 |  |  * @returns ::NC_NOERR No error. | 
| 54 |  |  * @returns ::NC_EBADID Bad ncid. | 
| 55 |  |  * @returns ::NC_ENOTVAR Invalid variable ID. | 
| 56 |  |  * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict | 
| 57 |  |  * nc3 netcdf-4 file. | 
| 58 |  |  * @returns ::NC_EINVAL Invalid input. | 
| 59 |  |  * @returns ::NC_EHDFERR HDF5 error. | 
| 60 |  |  * @author Ed Hartnett | 
| 61 |  |  */ | 
| 62 |  | int | 
| 63 |  | NCZ_set_var_chunk_cache(int ncid, int varid, size_t cachesize, size_t nelems, float preemption) | 
| 64 | 0 | { | 
| 65 | 0 |     NC_GRP_INFO_T *grp; | 
| 66 | 0 |     NC_FILE_INFO_T *h5; | 
| 67 | 0 |     NC_VAR_INFO_T *var; | 
| 68 | 0 |     NCZ_VAR_INFO_T *zvar; | 
| 69 | 0 |     int retval = NC_NOERR; | 
| 70 |  |  | 
| 71 |  |     /* Check input for validity. */ | 
| 72 | 0 |     if (preemption < 0 || preemption > 1) | 
| 73 | 0 |         {retval = NC_EINVAL; goto done;} | 
| 74 |  |  | 
| 75 |  |     /* Find info for this file and group, and set pointer to each. */ | 
| 76 | 0 |     if ((retval = nc4_find_nc_grp_h5(ncid, NULL, &grp, &h5))) | 
| 77 | 0 |         goto done; | 
| 78 | 0 |     assert(grp && h5); | 
| 79 |  |  | 
| 80 |  |     /* Find the var. */ | 
| 81 | 0 |     if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, (size_t)varid))) | 
| 82 | 0 |         {retval = NC_ENOTVAR; goto done;} | 
| 83 | 0 |     assert(var && var->hdr.id == varid); | 
| 84 |  | 
 | 
| 85 | 0 |     zvar = (NCZ_VAR_INFO_T*)var->format_var_info; | 
| 86 | 0 |     assert(zvar != NULL && zvar->cache != NULL); | 
| 87 |  |  | 
| 88 |  |     /* Set the values. */ | 
| 89 | 0 |     var->chunkcache.size = cachesize; | 
| 90 | 0 |     var->chunkcache.nelems = nelems; | 
| 91 | 0 |     var->chunkcache.preemption = preemption; | 
| 92 |  |  | 
| 93 |  |     /* Fix up cache */ | 
| 94 | 0 |     if((retval = NCZ_adjust_var_cache(var))) goto done; | 
| 95 | 0 | done: | 
| 96 | 0 |     return retval; | 
| 97 | 0 | } | 
| 98 |  |  | 
| 99 |  | /** | 
| 100 |  |  * @internal Adjust the chunk cache of a var for better | 
| 101 |  |  * performance. | 
| 102 |  |  * | 
| 103 |  |  * @note For contiguous and compact storage vars, or when parallel I/O | 
| 104 |  |  * is in use, this function will do nothing and return ::NC_NOERR; | 
| 105 |  |  * | 
| 106 |  |  * @param grp Pointer to group info struct. | 
| 107 |  |  * @param var Pointer to var info struct. | 
| 108 |  |  * | 
| 109 |  |  * @return ::NC_NOERR No error. | 
| 110 |  |  * @author Ed Hartnett | 
| 111 |  |  */ | 
| 112 |  | int | 
| 113 |  | NCZ_adjust_var_cache(NC_VAR_INFO_T *var) | 
| 114 | 0 | { | 
| 115 | 0 |     int stat = NC_NOERR; | 
| 116 | 0 |     NCZ_VAR_INFO_T* zvar = (NCZ_VAR_INFO_T*)var->format_var_info; | 
| 117 | 0 |     NCZChunkCache* zcache = NULL; | 
| 118 |  | 
 | 
| 119 | 0 |     zcache = zvar->cache; | 
| 120 | 0 |     if(zcache->valid) goto done; | 
| 121 |  |  | 
| 122 |  | #ifdef DEBUG | 
| 123 |  | fprintf(stderr,"xxx: adjusting cache for: %s\n",var->hdr.name); | 
| 124 |  | #endif | 
| 125 |  |  | 
| 126 |  |     /* completely empty the cache */ | 
| 127 | 0 |     flushcache(zcache); | 
| 128 |  |  | 
| 129 |  |     /* Reclaim any existing fill_chunk */ | 
| 130 | 0 |     if((stat = NCZ_reclaim_fill_chunk(zcache))) goto done; | 
| 131 |  |     /* Reset the parameters */ | 
| 132 | 0 |     zvar->cache->params.size = var->chunkcache.size; | 
| 133 | 0 |     zvar->cache->params.nelems = var->chunkcache.nelems; | 
| 134 | 0 |     zvar->cache->params.preemption = var->chunkcache.preemption; | 
| 135 |  | #ifdef DEBUG | 
| 136 |  |     fprintf(stderr,"%s.cache.adjust: size=%ld nelems=%ld\n", | 
| 137 |  |         var->hdr.name,(unsigned long)zvar->cache->maxsize,(unsigned long)zvar->cache->maxentries); | 
| 138 |  | #endif | 
| 139 |  |     /* One more thing, adjust the chunksize and count*/ | 
| 140 | 0 |     zcache->chunksize = zvar->chunksize; | 
| 141 | 0 |     zcache->chunkcount = 1; | 
| 142 | 0 |     if(var->ndims > 0) { | 
| 143 | 0 |   size_t i; | 
| 144 | 0 |   for(i=0;i<var->ndims;i++) { | 
| 145 | 0 |       zcache->chunkcount *= var->chunksizes[i]; | 
| 146 | 0 |         } | 
| 147 | 0 |     } | 
| 148 | 0 |     zcache->valid = 1; | 
| 149 | 0 | done: | 
| 150 | 0 |     return stat; | 
| 151 | 0 | } | 
| 152 |  |  | 
| 153 |  | /**************************************************/ | 
| 154 |  | /** | 
| 155 |  |  * Create a chunk cache object | 
| 156 |  |  * | 
| 157 |  |  * @param var containing var | 
| 158 |  |  * @param entrysize Size in bytes of an entry | 
| 159 |  |  * @param cachep return cache pointer | 
| 160 |  |  * | 
| 161 |  |  * @return ::NC_NOERR No error. | 
| 162 |  |  * @return ::NC_EINVAL Bad preemption. | 
| 163 |  |  * @author Dennis Heimbigner, Ed Hartnett | 
| 164 |  |  */ | 
| 165 |  | int | 
| 166 |  | NCZ_create_chunk_cache(NC_VAR_INFO_T* var, size64_t chunksize, char dimsep, NCZChunkCache** cachep) | 
| 167 | 0 | { | 
| 168 | 0 |     int stat = NC_NOERR; | 
| 169 | 0 |     NCZChunkCache* cache = NULL; | 
| 170 | 0 |     void* fill = NULL; | 
| 171 | 0 |     NCZ_VAR_INFO_T* zvar = NULL; | 
| 172 |  |    | 
| 173 | 0 |     if(chunksize == 0) return NC_EINVAL; | 
| 174 |  |  | 
| 175 | 0 |     zvar = (NCZ_VAR_INFO_T*)var->format_var_info; | 
| 176 | 0 |     if((cache = calloc(1,sizeof(NCZChunkCache))) == NULL) | 
| 177 | 0 |   {stat = NC_ENOMEM; goto done;} | 
| 178 | 0 |     cache->var = var; | 
| 179 | 0 |     cache->ndims = var->ndims + zvar->scalar; | 
| 180 | 0 |     cache->fillchunk = NULL; | 
| 181 | 0 |     cache->chunksize = chunksize; | 
| 182 | 0 |     cache->dimension_separator = dimsep; | 
| 183 | 0 |     zvar->cache = cache; | 
| 184 |  | 
 | 
| 185 | 0 |     cache->chunkcount = 1; | 
| 186 | 0 |     if(var->ndims > 0) { | 
| 187 | 0 |   size_t i; | 
| 188 | 0 |   for(i=0;i<var->ndims;i++) { | 
| 189 | 0 |       cache->chunkcount *= var->chunksizes[i]; | 
| 190 | 0 |         } | 
| 191 | 0 |     } | 
| 192 |  |      | 
| 193 |  |     /* Set default cache parameters */ | 
| 194 | 0 |     cache->params = NC_getglobalstate()->chunkcache; | 
| 195 |  | 
 | 
| 196 |  | #ifdef FLUSH | 
| 197 |  |     cache->maxentries = 1; | 
| 198 |  | #endif | 
| 199 |  | 
 | 
| 200 |  | #ifdef DEBUG | 
| 201 |  |     fprintf(stderr,"%s.cache: nelems=%ld size=%ld\n", | 
| 202 |  |         var->hdr.name,(unsigned long)cache->maxentries,(unsigned long)cache->maxsize); | 
| 203 |  | #endif | 
| 204 | 0 |     if((stat = ncxcachenew(LEAFLEN,&cache->xcache))) goto done; | 
| 205 | 0 |     if((cache->mru = nclistnew()) == NULL) | 
| 206 | 0 |   {stat = NC_ENOMEM; goto done;} | 
| 207 | 0 |     nclistsetalloc(cache->mru,cache->params.nelems); | 
| 208 |  | 
 | 
| 209 | 0 |     if(cachep) {*cachep = cache; cache = NULL;} | 
| 210 | 0 | done: | 
| 211 | 0 |     nullfree(fill); | 
| 212 | 0 |     NCZ_free_chunk_cache(cache); | 
| 213 | 0 |     return THROW(stat); | 
| 214 | 0 | } | 
| 215 |  |  | 
| 216 |  | static void | 
| 217 |  | free_cache_entry(NCZChunkCache* cache, NCZCacheEntry* entry) | 
| 218 | 0 | { | 
| 219 | 0 |     if(entry) { | 
| 220 | 0 |         int tid = cache->var->type_info->hdr.id; | 
| 221 | 0 |   if(tid == NC_STRING && !entry->isfixedstring) { | 
| 222 | 0 |             NC_reclaim_data(cache->var->container->nc4_info->controller,tid,entry->data,cache->chunkcount); | 
| 223 | 0 |   } | 
| 224 | 0 |   nullfree(entry->data); | 
| 225 | 0 |   nullfree(entry->key.varkey); | 
| 226 | 0 |   nullfree(entry->key.chunkkey); | 
| 227 | 0 |   nullfree(entry); | 
| 228 | 0 |     } | 
| 229 | 0 | } | 
| 230 |  |  | 
| 231 |  | void | 
| 232 |  | NCZ_free_chunk_cache(NCZChunkCache* cache) | 
| 233 | 0 | { | 
| 234 | 0 |     if(cache == NULL) return; | 
| 235 |  |  | 
| 236 | 0 |     ZTRACE(4,"cache.var=%s",cache->var->hdr.name); | 
| 237 |  |  | 
| 238 |  |     /* Iterate over the entries */ | 
| 239 | 0 |     while(nclistlength(cache->mru) > 0) { | 
| 240 | 0 |   void* ptr; | 
| 241 | 0 |         NCZCacheEntry* entry = nclistremove(cache->mru,0); | 
| 242 | 0 |   (void)ncxcacheremove(cache->xcache,entry->hashkey,&ptr); | 
| 243 | 0 |   assert(ptr == entry); | 
| 244 | 0 |         free_cache_entry(cache,entry); | 
| 245 | 0 |     } | 
| 246 |  | #ifdef DEBUG | 
| 247 |  | fprintf(stderr,"|cache.free|=%ld\n",nclistlength(cache->mru)); | 
| 248 |  | #endif | 
| 249 | 0 |     ncxcachefree(cache->xcache); | 
| 250 | 0 |     nclistfree(cache->mru); | 
| 251 | 0 |     cache->mru = NULL; | 
| 252 | 0 |     (void)NCZ_reclaim_fill_chunk(cache); | 
| 253 | 0 |     nullfree(cache); | 
| 254 | 0 |     (void)ZUNTRACE(NC_NOERR); | 
| 255 | 0 | } | 
| 256 |  |  | 
| 257 |  | size64_t | 
| 258 |  | NCZ_cache_entrysize(NCZChunkCache* cache) | 
| 259 | 0 | { | 
| 260 | 0 |     assert(cache); | 
| 261 | 0 |     return cache->chunksize; | 
| 262 | 0 | } | 
| 263 |  |  | 
| 264 |  | /* Return number of active entries in cache */ | 
| 265 |  | size64_t | 
| 266 |  | NCZ_cache_size(NCZChunkCache* cache) | 
| 267 | 0 | { | 
| 268 | 0 |     assert(cache); | 
| 269 | 0 |     return nclistlength(cache->mru); | 
| 270 | 0 | } | 
| 271 |  |  | 
| 272 |  | int | 
| 273 |  | NCZ_read_cache_chunk(NCZChunkCache* cache, const size64_t* indices, void** datap) | 
| 274 | 0 | { | 
| 275 | 0 |     int stat = NC_NOERR; | 
| 276 | 0 |     int rank = cache->ndims; | 
| 277 | 0 |     NCZCacheEntry* entry = NULL; | 
| 278 | 0 |     ncexhashkey_t hkey = 0; | 
| 279 | 0 |     int created = 0; | 
| 280 |  |  | 
| 281 |  |     /* the hash key */ | 
| 282 | 0 |     hkey = ncxcachekey(indices,sizeof(size64_t)*cache->ndims); | 
| 283 |  |     /* See if already in cache */ | 
| 284 | 0 |     stat = ncxcachelookup(cache->xcache,hkey,(void**)&entry); | 
| 285 | 0 |     switch(stat) { | 
| 286 | 0 |     case NC_NOERR: | 
| 287 |  |         /* Move to front of the lru */ | 
| 288 | 0 |         (void)ncxcachetouch(cache->xcache,hkey); | 
| 289 | 0 |         break; | 
| 290 | 0 |     case NC_ENOOBJECT: case NC_EEMPTY: | 
| 291 | 0 |         entry = NULL; /* not found; */ | 
| 292 | 0 |   break; | 
| 293 | 0 |     default: goto done; | 
| 294 | 0 |     } | 
| 295 |  |  | 
| 296 | 0 |     if(entry == NULL) { /*!found*/ | 
| 297 |  |   /* Create a new entry */ | 
| 298 | 0 |   if((entry = calloc(1,sizeof(NCZCacheEntry)))==NULL) | 
| 299 | 0 |       {stat = NC_ENOMEM; goto done;} | 
| 300 | 0 |   memcpy(entry->indices,indices,(size_t)rank*sizeof(size64_t)); | 
| 301 |  |         /* Create the key for this cache */ | 
| 302 | 0 |         if((stat = NCZ_buildchunkpath(cache,indices,&entry->key))) goto done; | 
| 303 | 0 |         entry->hashkey = hkey; | 
| 304 | 0 |   assert(entry->data == NULL && entry->size == 0); | 
| 305 |  |   /* Try to read the object from "disk"; might change size; will create if non-existent */ | 
| 306 | 0 |   if((stat=get_chunk(cache,entry))) goto done; | 
| 307 | 0 |   assert(entry->data != NULL); | 
| 308 |  |   /* Ensure cache constraints not violated; but do it before entry is added */ | 
| 309 | 0 |   if((stat=verifycache(cache))) goto done; | 
| 310 | 0 |         nclistpush(cache->mru,entry); | 
| 311 | 0 |   if((stat = ncxcacheinsert(cache->xcache,entry->hashkey,entry))) goto done; | 
| 312 | 0 |     } | 
| 313 |  |  | 
| 314 |  | #ifdef DEBUG | 
| 315 |  | fprintf(stderr,"|cache.read.lru|=%ld\n",nclistlength(cache->mru)); | 
| 316 |  | #endif | 
| 317 | 0 |     if(datap) *datap = entry->data; | 
| 318 | 0 |     entry = NULL; | 
| 319 |  |      | 
| 320 | 0 | done: | 
| 321 | 0 |     if(created && stat == NC_NOERR)  stat = NC_EEMPTY; /* tell upper layers */ | 
| 322 | 0 |     if(entry) free_cache_entry(cache,entry); | 
| 323 | 0 |     return THROW(stat); | 
| 324 | 0 | } | 
| 325 |  |  | 
| 326 |  | #if 0 | 
| 327 |  | int | 
| 328 |  | NCZ_write_cache_chunk(NCZChunkCache* cache, const size64_t* indices, void* content) | 
| 329 |  | { | 
| 330 |  |     int stat = NC_NOERR; | 
| 331 |  |     int rank = cache->ndims; | 
| 332 |  |     NCZCacheEntry* entry = NULL; | 
| 333 |  |     ncexhashkey_t hkey; | 
| 334 |  |      | 
| 335 |  |     /* create the hash key */ | 
| 336 |  |     hkey = ncxcachekey(indices,sizeof(size64_t)*cache->ndims); | 
| 337 |  |  | 
| 338 |  |     if(entry == NULL) { /*!found*/ | 
| 339 |  |   /* Create a new entry */ | 
| 340 |  |   if((entry = calloc(1,sizeof(NCZCacheEntry)))==NULL) | 
| 341 |  |       {stat = NC_ENOMEM; goto done;} | 
| 342 |  |   memcpy(entry->indices,indices,rank*sizeof(size64_t)); | 
| 343 |  |         if((stat = NCZ_buildchunkpath(cache,indices,&entry->key))) goto done; | 
| 344 |  |         entry->hashkey = hkey; | 
| 345 |  |   /* Create the local copy space */ | 
| 346 |  |   entry->size = cache->chunksize; | 
| 347 |  |   if((entry->data = calloc(1,cache->chunksize)) == NULL) | 
| 348 |  |       {stat = NC_ENOMEM; goto done;} | 
| 349 |  |   memcpy(entry->data,content,cache->chunksize); | 
| 350 |  |     } | 
| 351 |  |     setmodified(entry,1); | 
| 352 |  |     nclistpush(cache->mru,entry); /* MRU order */ | 
| 353 |  | #ifdef DEBUG | 
| 354 |  | fprintf(stderr,"|cache.write|=%ld\n",nclistlength(cache->mru)); | 
| 355 |  | #endif | 
| 356 |  |     entry = NULL; | 
| 357 |  |  | 
| 358 |  |     /* Ensure cache constraints not violated */ | 
| 359 |  |     if((stat=verifycache(cache))) goto done; | 
| 360 |  |  | 
| 361 |  | done: | 
| 362 |  |     if(entry) free_cache_entry(cache,entry); | 
| 363 |  |     return THROW(stat); | 
| 364 |  | } | 
| 365 |  | #endif | 
| 366 |  |  | 
| 367 |  | /* Constrain cache */ | 
| 368 |  | static int | 
| 369 |  | verifycache(NCZChunkCache* cache) | 
| 370 | 0 | { | 
| 371 | 0 |     int stat = NC_NOERR; | 
| 372 |  | 
 | 
| 373 |  | #if 0 | 
| 374 |  |     /* Sanity check; make sure at least one entry is always allowed */ | 
| 375 |  |     if(nclistlength(cache->mru) == 1) | 
| 376 |  |   goto done; | 
| 377 |  | #endif | 
| 378 | 0 |     if((stat = constraincache(cache,USEPARAMSIZE))) goto done; | 
| 379 | 0 | done: | 
| 380 | 0 |     return stat; | 
| 381 | 0 | } | 
| 382 |  |  | 
| 383 |  | /* Completely flush cache */ | 
| 384 |  |  | 
| 385 |  | static int | 
| 386 |  | flushcache(NCZChunkCache* cache) | 
| 387 | 0 | { | 
| 388 | 0 |     int stat = NC_NOERR; | 
| 389 |  | #if 0 | 
| 390 |  |     size_t oldsize = cache->params.size; | 
| 391 |  |     cache->params.size = 0; | 
| 392 |  |     stat = constraincache(cache,USEPARAMSIZE); | 
| 393 |  |     cache->params.size = oldsize; | 
| 394 |  | #else | 
| 395 | 0 |     stat = constraincache(cache,USEPARAMSIZE); | 
| 396 | 0 | #endif | 
| 397 | 0 |     return stat; | 
| 398 | 0 | } | 
| 399 |  |  | 
| 400 |  |  | 
| 401 |  | /* Remove entries to ensure cache is not | 
| 402 |  |    violating any of its constraints. | 
| 403 |  |    On entry, constraints might be violated. | 
| 404 |  |    Make sure that the entryinuse (NULL => no constraint) is not reclaimed. | 
| 405 |  | @param cache | 
| 406 |  | @param needed make sure there is room for this much space; USEPARAMSIZE => ensure no more than  cache params is used. | 
| 407 |  | */ | 
| 408 |  |  | 
| 409 |  | static int | 
| 410 |  | constraincache(NCZChunkCache* cache, size64_t needed) | 
| 411 | 0 | { | 
| 412 | 0 |     int stat = NC_NOERR; | 
| 413 | 0 |     size64_t final_size; | 
| 414 |  |  | 
| 415 |  |     /* If the cache is empty then do nothing */ | 
| 416 | 0 |     if(cache->used == 0) goto done; | 
| 417 |  |  | 
| 418 | 0 |     if(needed == USEPARAMSIZE) | 
| 419 | 0 |         final_size = cache->params.size; | 
| 420 | 0 |     else if(cache->used > needed) | 
| 421 | 0 |         final_size = cache->used - needed; | 
| 422 | 0 |     else | 
| 423 | 0 |         final_size = 0; | 
| 424 |  |  | 
| 425 |  |     /* Flush from LRU end if we are at capacity */ | 
| 426 | 0 |     while(nclistlength(cache->mru) > cache->params.nelems || cache->used > final_size) { | 
| 427 | 0 |   size_t i; | 
| 428 | 0 |   void* ptr; | 
| 429 | 0 |   NCZCacheEntry* e = ncxcachelast(cache->xcache); /* last entry is the least recently used */ | 
| 430 | 0 |   if(e == NULL) break; | 
| 431 | 0 |         if((stat = ncxcacheremove(cache->xcache,e->hashkey,&ptr))) goto done; | 
| 432 | 0 |     assert(e == ptr); | 
| 433 | 0 |         for(i=0;i<nclistlength(cache->mru);i++) { | 
| 434 | 0 |       e = nclistget(cache->mru,i); | 
| 435 | 0 |       if(ptr == e) break; | 
| 436 | 0 |   } | 
| 437 | 0 |     assert(e != NULL); | 
| 438 | 0 |   assert(i >= 0 && i < nclistlength(cache->mru)); | 
| 439 | 0 |   nclistremove(cache->mru,i); | 
| 440 | 0 |   assert(cache->used >= e->size); | 
| 441 |  |   /* Note that |old chunk data| may not be same as |new chunk data| because of filters */ | 
| 442 | 0 |   cache->used -= e->size; /* old size */ | 
| 443 | 0 |   if(e->modified) /* flush to file */ | 
| 444 | 0 |       stat=put_chunk(cache,e); | 
| 445 |  |   /* reclaim */ | 
| 446 | 0 |         nullfree(e->data); nullfree(e->key.varkey); nullfree(e->key.chunkkey); nullfree(e); | 
| 447 | 0 |     } | 
| 448 |  | #ifdef DEBUG | 
| 449 |  | fprintf(stderr,"|cache.makeroom|=%ld\n",nclistlength(cache->mru)); | 
| 450 |  | #endif | 
| 451 | 0 | done: | 
| 452 | 0 |     return stat; | 
| 453 | 0 | } | 
| 454 |  |  | 
| 455 |  | /** | 
| 456 |  | Push modified cache entries to disk. | 
| 457 |  | Also make sure the cache size is correct. | 
| 458 |  | @param cache | 
| 459 |  | @return NC_EXXX error | 
| 460 |  | */ | 
| 461 |  | int | 
| 462 |  | NCZ_flush_chunk_cache(NCZChunkCache* cache) | 
| 463 | 0 | { | 
| 464 | 0 |     int stat = NC_NOERR; | 
| 465 | 0 |     size_t i; | 
| 466 |  | 
 | 
| 467 | 0 |     ZTRACE(4,"cache.var=%s |cache|=%d",cache->var->hdr.name,(int)nclistlength(cache->mru)); | 
| 468 |  | 
 | 
| 469 | 0 |     if(NCZ_cache_size(cache) == 0) goto done; | 
| 470 |  |      | 
| 471 |  |     /* Iterate over the entries in hashmap */ | 
| 472 | 0 |     for(i=0;i<nclistlength(cache->mru);i++) { | 
| 473 | 0 |         NCZCacheEntry* entry = nclistget(cache->mru,i); | 
| 474 | 0 |         if(entry->modified) { | 
| 475 |  |       /* Write out this chunk in toto*/ | 
| 476 | 0 |         if((stat=put_chunk(cache,entry))) | 
| 477 | 0 |           goto done; | 
| 478 | 0 |   } | 
| 479 | 0 |         setmodified(entry,0); | 
| 480 | 0 |     } | 
| 481 |  |     /* Re-compute space used */ | 
| 482 | 0 |     cache->used = 0; | 
| 483 | 0 |     for(i=0;i<nclistlength(cache->mru);i++) { | 
| 484 | 0 |         NCZCacheEntry* entry = nclistget(cache->mru,i); | 
| 485 | 0 |         cache->used += entry->size; | 
| 486 | 0 |     } | 
| 487 |  |     /* Make sure cache size and nelems are correct */ | 
| 488 | 0 |     if((stat=verifycache(cache))) goto done; | 
| 489 |  |  | 
| 490 |  |  | 
| 491 | 0 | done: | 
| 492 | 0 |     return ZUNTRACE(stat); | 
| 493 | 0 | } | 
| 494 |  |  | 
| 495 |  | /* Ensure existence of some kind of fill chunk */ | 
| 496 |  | int | 
| 497 |  | NCZ_ensure_fill_chunk(NCZChunkCache* cache) | 
| 498 | 0 | { | 
| 499 | 0 |     int stat = NC_NOERR; | 
| 500 | 0 |     size_t i; | 
| 501 | 0 |     NC_VAR_INFO_T* var = cache->var; | 
| 502 | 0 |     nc_type typeid = var->type_info->hdr.id; | 
| 503 | 0 |     size_t typesize = var->type_info->size; | 
| 504 |  | 
 | 
| 505 | 0 |     if(cache->fillchunk) goto done; | 
| 506 |  |  | 
| 507 | 0 |     if((cache->fillchunk = malloc(cache->chunksize))==NULL) | 
| 508 | 0 |         {stat = NC_ENOMEM; goto done;} | 
| 509 | 0 |     if(var->no_fill) { | 
| 510 |  |         /* use zeros */ | 
| 511 | 0 |   memset(cache->fillchunk,0,cache->chunksize); | 
| 512 | 0 |   goto done; | 
| 513 | 0 |     } | 
| 514 | 0 |     if((stat = NCZ_ensure_fill_value(var))) goto done; | 
| 515 | 0 |     if(typeid == NC_STRING) { | 
| 516 | 0 |         char* src = *((char**)(var->fill_value)); | 
| 517 | 0 |   char** dst = (char**)(cache->fillchunk); | 
| 518 | 0 |         for(i=0;i<cache->chunkcount;i++) dst[i] = strdup(src); | 
| 519 | 0 |     } else | 
| 520 | 0 |     switch (typesize) { | 
| 521 | 0 |     case 1: { | 
| 522 | 0 |         unsigned char c = *((unsigned char*)var->fill_value); | 
| 523 | 0 |         memset(cache->fillchunk,c,cache->chunksize); | 
| 524 | 0 |         } break; | 
| 525 | 0 |     case 2: { | 
| 526 | 0 |         unsigned short fv = *((unsigned short*)var->fill_value); | 
| 527 | 0 |         unsigned short* p2 = (unsigned short*)cache->fillchunk; | 
| 528 | 0 |         for(i=0;i<cache->chunksize;i+=typesize) *p2++ = fv; | 
| 529 | 0 |         } break; | 
| 530 | 0 |     case 4: { | 
| 531 | 0 |         unsigned int fv = *((unsigned int*)var->fill_value); | 
| 532 | 0 |         unsigned int* p4 = (unsigned int*)cache->fillchunk; | 
| 533 | 0 |         for(i=0;i<cache->chunksize;i+=typesize) *p4++ = fv; | 
| 534 | 0 |         } break; | 
| 535 | 0 |     case 8: { | 
| 536 | 0 |         unsigned long long fv = *((unsigned long long*)var->fill_value); | 
| 537 | 0 |         unsigned long long* p8 = (unsigned long long*)cache->fillchunk; | 
| 538 | 0 |         for(i=0;i<cache->chunksize;i+=typesize) *p8++ = fv; | 
| 539 | 0 |         } break; | 
| 540 | 0 |     default: { | 
| 541 | 0 |         unsigned char* p; | 
| 542 | 0 |         for(p=cache->fillchunk,i=0;i<cache->chunksize;i+=typesize,p+=typesize) | 
| 543 | 0 |             memcpy(p,var->fill_value,typesize); | 
| 544 | 0 |         } break; | 
| 545 | 0 |     } | 
| 546 | 0 | done: | 
| 547 | 0 |     return NC_NOERR; | 
| 548 | 0 | } | 
| 549 |  |      | 
| 550 |  | int | 
| 551 |  | NCZ_reclaim_fill_chunk(NCZChunkCache* zcache) | 
| 552 | 0 | { | 
| 553 | 0 |     int stat = NC_NOERR; | 
| 554 | 0 |     if(zcache && zcache->fillchunk) { | 
| 555 | 0 |   NC_VAR_INFO_T* var = zcache->var; | 
| 556 | 0 |   int tid = var->type_info->hdr.id; | 
| 557 | 0 |   size_t chunkcount = zcache->chunkcount; | 
| 558 | 0 |         stat = NC_reclaim_data_all(var->container->nc4_info->controller,tid,zcache->fillchunk,chunkcount); | 
| 559 | 0 |   zcache->fillchunk = NULL; | 
| 560 | 0 |     } | 
| 561 | 0 |     return stat; | 
| 562 | 0 | } | 
| 563 |  |  | 
| 564 |  | int | 
| 565 |  | NCZ_chunk_cache_modify(NCZChunkCache* cache, const size64_t* indices) | 
| 566 | 0 | { | 
| 567 | 0 |     int stat = NC_NOERR; | 
| 568 | 0 |     ncexhashkey_t hkey = 0; | 
| 569 | 0 |     NCZCacheEntry* entry = NULL; | 
| 570 |  |  | 
| 571 |  |     /* the hash key */ | 
| 572 | 0 |     hkey = ncxcachekey(indices,sizeof(size64_t)*cache->ndims); | 
| 573 |  |  | 
| 574 |  |     /* See if already in cache */ | 
| 575 | 0 |     if((stat=ncxcachelookup(cache->xcache, hkey, (void**)&entry))) {stat = NC_EINTERNAL; goto done;} | 
| 576 | 0 |     setmodified(entry,1); | 
| 577 |  | 
 | 
| 578 | 0 | done: | 
| 579 | 0 |     return THROW(stat); | 
| 580 | 0 | } | 
| 581 |  |  | 
| 582 |  | /**************************************************/ | 
| 583 |  | /* | 
| 584 |  | From Zarr V2 Specification: | 
| 585 |  | "The compressed sequence of bytes for each chunk is stored under | 
| 586 |  | a key formed from the index of the chunk within the grid of | 
| 587 |  | chunks representing the array.  To form a string key for a | 
| 588 |  | chunk, the indices are converted to strings and concatenated | 
| 589 |  | with the dimension_separator character ('.' or '/') separating | 
| 590 |  | each index. For example, given an array with shape (10000, | 
| 591 |  | 10000) and chunk shape (1000, 1000) there will be 100 chunks | 
| 592 |  | laid out in a 10 by 10 grid. The chunk with indices (0, 0) | 
| 593 |  | provides data for rows 0-1000 and columns 0-1000 and is stored | 
| 594 |  | under the key "0.0"; the chunk with indices (2, 4) provides data | 
| 595 |  | for rows 2000-3000 and columns 4000-5000 and is stored under the | 
| 596 |  | key "2.4"; etc." | 
| 597 |  | */ | 
| 598 |  |  | 
| 599 |  | /** | 
| 600 |  |  * @param R Rank | 
| 601 |  |  * @param chunkindices The chunk indices | 
| 602 |  |  * @param dimsep the dimension separator | 
| 603 |  |  * @param keyp Return the chunk key string | 
| 604 |  |  */ | 
| 605 |  | int | 
| 606 |  | NCZ_buildchunkkey(size_t R, const size64_t* chunkindices, char dimsep, char** keyp) | 
| 607 | 0 | { | 
| 608 | 0 |     int stat = NC_NOERR; | 
| 609 | 0 |     size_t r; | 
| 610 | 0 |     NCbytes* key = ncbytesnew(); | 
| 611 |  | 
 | 
| 612 | 0 |     if(keyp) *keyp = NULL; | 
| 613 |  | 
 | 
| 614 | 0 |     assert(islegaldimsep(dimsep)); | 
| 615 |  |      | 
| 616 | 0 |     for(r=0;r<R;r++) { | 
| 617 | 0 |   char sindex[64]; | 
| 618 | 0 |         if(r > 0) ncbytesappend(key,dimsep); | 
| 619 |  |   /* Print as decimal with no leading zeros */ | 
| 620 | 0 |   snprintf(sindex,sizeof(sindex),"%lu",(unsigned long)chunkindices[r]);  | 
| 621 | 0 |   ncbytescat(key,sindex); | 
| 622 | 0 |     } | 
| 623 | 0 |     ncbytesnull(key); | 
| 624 | 0 |     if(keyp) *keyp = ncbytesextract(key); | 
| 625 |  | 
 | 
| 626 | 0 |     ncbytesfree(key); | 
| 627 | 0 |     return THROW(stat); | 
| 628 | 0 | } | 
| 629 |  |  | 
| 630 |  | /** | 
| 631 |  |  * @internal Push data to chunk of a file. | 
| 632 |  |  * If chunk does not exist, create it | 
| 633 |  |  * | 
| 634 |  |  * @param file Pointer to file info struct. | 
| 635 |  |  * @param proj Chunk projection | 
| 636 |  |  * @param datalen size of data | 
| 637 |  |  * @param data Buffer containing the chunk data to write | 
| 638 |  |  * | 
| 639 |  |  * @return ::NC_NOERR No error. | 
| 640 |  |  * @author Dennis Heimbigner | 
| 641 |  |  */ | 
| 642 |  | static int | 
| 643 |  | put_chunk(NCZChunkCache* cache, NCZCacheEntry* entry) | 
| 644 | 0 | { | 
| 645 | 0 |     int stat = NC_NOERR; | 
| 646 | 0 |     NC_FILE_INFO_T* file = NULL; | 
| 647 | 0 |     NCZ_FILE_INFO_T* zfile = NULL; | 
| 648 | 0 |     NCZMAP* map = NULL; | 
| 649 | 0 |     char* path = NULL; | 
| 650 | 0 |     nc_type tid = NC_NAT; | 
| 651 | 0 |     void* strchunk = NULL; | 
| 652 |  | 
 | 
| 653 | 0 |     ZTRACE(5,"cache.var=%s entry.key=%s",cache->var->hdr.name,entry->key); | 
| 654 | 0 |     LOG((3, "%s: var: %p", __func__, cache->var)); | 
| 655 |  | 
 | 
| 656 | 0 |     file = (cache->var->container)->nc4_info; | 
| 657 | 0 |     zfile = file->format_file_info; | 
| 658 | 0 |     map = zfile->map; | 
| 659 |  |  | 
| 660 |  |     /* Collect some info */ | 
| 661 | 0 |     tid = cache->var->type_info->hdr.id; | 
| 662 |  | 
 | 
| 663 | 0 |     if(tid == NC_STRING && !entry->isfixedstring) { | 
| 664 |  |         /* Convert from char* to char[strlen] format */ | 
| 665 | 0 |         int maxstrlen = NCZ_get_maxstrlen((NC_OBJ*)cache->var); | 
| 666 | 0 |         assert(maxstrlen > 0); | 
| 667 | 0 |         if((strchunk = malloc((size_t)cache->chunkcount * (size_t)maxstrlen))==NULL) {stat = NC_ENOMEM; goto done;} | 
| 668 |  |         /* copy char* to char[] format */ | 
| 669 | 0 |         if((stat = NCZ_char2fixed((const char**)entry->data,strchunk,cache->chunkcount,maxstrlen))) goto done; | 
| 670 |  |         /* Reclaim the old chunk */ | 
| 671 | 0 |         if((stat = NC_reclaim_data_all(file->controller,tid,entry->data,cache->chunkcount))) goto done; | 
| 672 | 0 |         entry->data = NULL; | 
| 673 | 0 |         entry->data = strchunk; strchunk = NULL; | 
| 674 | 0 |         entry->size = (cache->chunkcount * (size64_t)maxstrlen); | 
| 675 | 0 |         entry->isfixedstring = 1; | 
| 676 | 0 |     } | 
| 677 |  |  | 
| 678 |  |  | 
| 679 |  | #ifdef NETCDF_ENABLE_NCZARR_FILTERS | 
| 680 |  |     /* Make sure the entry is in filtered state */ | 
| 681 |  |     if(!entry->isfiltered) { | 
| 682 |  |         NC_VAR_INFO_T* var = cache->var; | 
| 683 |  |         void* filtered = NULL; /* pointer to the filtered data */ | 
| 684 |  |   size_t flen; /* length of filtered data */ | 
| 685 |  |   /* Get the filter chain to apply */ | 
| 686 |  |   NClist* filterchain = (NClist*)var->filters; | 
| 687 |  |   if(nclistlength(filterchain) > 0) { | 
| 688 |  |       /* Apply the filter chain to get the filtered data; will reclaim entry->data */ | 
| 689 |  |       if((stat = NCZ_applyfilterchain(file,var,filterchain,entry->size,entry->data,&flen,&filtered,ENCODING))) goto done; | 
| 690 |  |       /* Fix up the cache entry */ | 
| 691 |  |       /* Note that if filtered is different from entry->data, then entry->data will have been freed */ | 
| 692 |  |       entry->data = filtered; | 
| 693 |  |       entry->size = flen; | 
| 694 |  |             entry->isfiltered = 1; | 
| 695 |  |   } | 
| 696 |  |     } | 
| 697 |  | #endif | 
| 698 |  |  | 
| 699 | 0 |     path = NCZ_chunkpath(entry->key); | 
| 700 | 0 |     stat = nczmap_write(map,path,entry->size,entry->data); | 
| 701 | 0 |     nullfree(path); path = NULL; | 
| 702 |  | 
 | 
| 703 | 0 |     switch(stat) { | 
| 704 | 0 |     case NC_NOERR: | 
| 705 | 0 |   break; | 
| 706 | 0 |     case NC_ENOOBJECT: case NC_EEMPTY: | 
| 707 | 0 |     default: goto done; | 
| 708 | 0 |     } | 
| 709 | 0 | done: | 
| 710 | 0 |     nullfree(strchunk); | 
| 711 | 0 |     nullfree(path); | 
| 712 | 0 |     return ZUNTRACE(stat); | 
| 713 | 0 | } | 
| 714 |  |  | 
| 715 |  | /** | 
| 716 |  |  * @internal Push data from memory to file. | 
| 717 |  |  * | 
| 718 |  |  * @param cache Pointer to parent cache | 
| 719 |  |  * @param key chunk key | 
| 720 |  |  * @param entry cache entry to read into | 
| 721 |  |  * | 
| 722 |  |  * @return ::NC_NOERR No error. | 
| 723 |  |  * @author Dennis Heimbigner | 
| 724 |  |  */ | 
| 725 |  | static int | 
| 726 |  | get_chunk(NCZChunkCache* cache, NCZCacheEntry* entry) | 
| 727 | 0 | { | 
| 728 | 0 |     int stat = NC_NOERR; | 
| 729 | 0 |     NCZMAP* map = NULL; | 
| 730 | 0 |     NC_FILE_INFO_T* file = NULL; | 
| 731 | 0 |     NCZ_FILE_INFO_T* zfile = NULL; | 
| 732 | 0 |     NC_TYPE_INFO_T* xtype = NULL; | 
| 733 | 0 |     char** strchunk = NULL; | 
| 734 | 0 |     size64_t size; | 
| 735 | 0 |     int empty = 0; | 
| 736 | 0 |     char* path = NULL; | 
| 737 | 0 |     int tid; | 
| 738 |  | 
 | 
| 739 | 0 |     ZTRACE(5,"cache.var=%s entry.key=%s sep=%d",cache->var->hdr.name,entry->key,cache->dimension_separator); | 
| 740 |  |      | 
| 741 | 0 |     LOG((3, "%s: file: %p", __func__, file)); | 
| 742 |  | 
 | 
| 743 | 0 |     file = (cache->var->container)->nc4_info; | 
| 744 | 0 |     zfile = file->format_file_info; | 
| 745 | 0 |     map = zfile->map; | 
| 746 | 0 |     assert(map); | 
| 747 |  |  | 
| 748 |  |     /* Collect some info */ | 
| 749 | 0 |     xtype = cache->var->type_info; | 
| 750 | 0 |     tid = xtype->hdr.id; | 
| 751 |  |  | 
| 752 |  |     /* get size of the "raw" data on "disk" */ | 
| 753 | 0 |     path = NCZ_chunkpath(entry->key); | 
| 754 | 0 |     stat = nczmap_len(map,path,&size); | 
| 755 | 0 |     nullfree(path); path = NULL; | 
| 756 | 0 |     switch(stat) { | 
| 757 | 0 |     case NC_NOERR: entry->size = size; break; | 
| 758 | 0 |     case NC_ENOOBJECT: case NC_EEMPTY: empty = 1; stat = NC_NOERR; break; | 
| 759 | 0 |     default: goto done; | 
| 760 | 0 |     } | 
| 761 |  |  | 
| 762 |  |     /* make room in the cache */ | 
| 763 | 0 |     if((stat = constraincache(cache,size))) goto done;     | 
| 764 |  |  | 
| 765 | 0 |     if(!empty) { | 
| 766 |  |         /* Make sure we have a place to read it */ | 
| 767 | 0 |         if((entry->data = (void*)calloc(1,entry->size)) == NULL) | 
| 768 | 0 |       {stat = NC_ENOMEM; goto done;} | 
| 769 |  |   /* Read the raw data */ | 
| 770 | 0 |         path = NCZ_chunkpath(entry->key); | 
| 771 | 0 |         stat = nczmap_read(map,path,0,entry->size,(char*)entry->data); | 
| 772 | 0 |         nullfree(path); path = NULL; | 
| 773 | 0 |         switch (stat) { | 
| 774 | 0 |         case NC_NOERR: break; | 
| 775 | 0 |         case NC_ENOOBJECT: case NC_EEMPTY: empty = 1; stat = NC_NOERR;break; | 
| 776 | 0 |   default: goto done; | 
| 777 | 0 |   } | 
| 778 | 0 |         entry->isfiltered = (int)FILTERED(cache); /* Is the data being read filtered? */ | 
| 779 | 0 |   if(tid == NC_STRING) | 
| 780 | 0 |       entry->isfixedstring = 1; /* fill cache is in char[maxstrlen] format */ | 
| 781 | 0 |     } | 
| 782 | 0 |     if(empty) { | 
| 783 |  |   /* fake the chunk */ | 
| 784 | 0 |         setmodified(entry,(file->no_write?0:1)); | 
| 785 | 0 |   entry->size = cache->chunksize; | 
| 786 | 0 |   entry->data = NULL; | 
| 787 | 0 |         entry->isfixedstring = 0; | 
| 788 | 0 |         entry->isfiltered = 0; | 
| 789 |  |         /* apply fill value */ | 
| 790 | 0 |   if(cache->fillchunk == NULL) | 
| 791 | 0 |       {if((stat = NCZ_ensure_fill_chunk(cache))) goto done;} | 
| 792 | 0 |   if((entry->data = calloc(1,entry->size))==NULL) {stat = NC_ENOMEM; goto done;} | 
| 793 | 0 |   if((stat = NCZ_copy_data(file,cache->var,cache->fillchunk,cache->chunkcount,ZREADING,entry->data))) goto done; | 
| 794 | 0 |   stat = NC_NOERR; | 
| 795 | 0 |     } | 
| 796 |  | #ifdef NETCDF_ENABLE_NCZARR_FILTERS | 
| 797 |  |     /* Make sure the entry is in unfiltered state */ | 
| 798 |  |     if(!empty && entry->isfiltered) { | 
| 799 |  |         NC_VAR_INFO_T* var = cache->var; | 
| 800 |  |         void* unfiltered = NULL; /* pointer to the unfiltered data */ | 
| 801 |  |         void* filtered = NULL; /* pointer to the filtered data */ | 
| 802 |  |   size_t unflen; /* length of unfiltered data */ | 
| 803 |  |   assert(tid != NC_STRING || entry->isfixedstring); | 
| 804 |  |   /* Get the filter chain to apply */ | 
| 805 |  |   NClist* filterchain = (NClist*)var->filters; | 
| 806 |  |   if(nclistlength(filterchain) == 0) {stat = NC_EFILTER; goto done;} | 
| 807 |  |   /* Apply the filter chain to get the unfiltered data */ | 
| 808 |  |   filtered = entry->data; | 
| 809 |  |   entry->data = NULL; | 
| 810 |  |   if((stat = NCZ_applyfilterchain(file,var,filterchain,entry->size,filtered,&unflen,&unfiltered,!ENCODING))) goto done; | 
| 811 |  |   /* Fix up the cache entry */ | 
| 812 |  |   entry->data = unfiltered; | 
| 813 |  |   entry->size = unflen; | 
| 814 |  |   entry->isfiltered = 0; | 
| 815 |  |     } | 
| 816 |  | #endif | 
| 817 |  |  | 
| 818 | 0 |     if(tid == NC_STRING && entry->isfixedstring) { | 
| 819 |  |         /* Convert from char[strlen] to char* format */ | 
| 820 | 0 |   int maxstrlen = NCZ_get_maxstrlen((NC_OBJ*)cache->var); | 
| 821 | 0 |   assert(maxstrlen > 0); | 
| 822 |  |   /* copy char[] to char* format */ | 
| 823 | 0 |   if((strchunk = (char**)malloc(sizeof(char*)*cache->chunkcount))==NULL) | 
| 824 | 0 |         {stat = NC_ENOMEM; goto done;} | 
| 825 | 0 |   if((stat = NCZ_fixed2char(entry->data,strchunk,cache->chunkcount,maxstrlen))) goto done; | 
| 826 |  |   /* Reclaim the old chunk */ | 
| 827 | 0 |   nullfree(entry->data); | 
| 828 | 0 |   entry->data = NULL; | 
| 829 | 0 |   entry->data = strchunk; strchunk = NULL; | 
| 830 | 0 |   entry->size = cache->chunkcount * sizeof(char*); | 
| 831 | 0 |   entry->isfixedstring = 0; | 
| 832 | 0 |     } | 
| 833 |  |  | 
| 834 |  |     /* track new chunk */ | 
| 835 | 0 |     cache->used += entry->size; | 
| 836 |  | 
 | 
| 837 | 0 | done: | 
| 838 | 0 |     nullfree(strchunk); | 
| 839 | 0 |     nullfree(path); | 
| 840 | 0 |     return ZUNTRACE(stat); | 
| 841 | 0 | } | 
| 842 |  |  | 
| 843 |  | int | 
| 844 |  | NCZ_buildchunkpath(NCZChunkCache* cache, const size64_t* chunkindices, struct ChunkKey* key) | 
| 845 | 0 | { | 
| 846 | 0 |     int stat = NC_NOERR; | 
| 847 | 0 |     char* chunkname = NULL; | 
| 848 | 0 |     char* varkey = NULL; | 
| 849 |  | 
 | 
| 850 | 0 |     assert(key != NULL); | 
| 851 |  |     /* Get the chunk object name */ | 
| 852 | 0 |     if((stat = NCZ_buildchunkkey(cache->ndims, chunkindices, cache->dimension_separator, &chunkname))) goto done; | 
| 853 |  |     /* Get the var object key */ | 
| 854 | 0 |     if((stat = NCZ_varkey(cache->var,&varkey))) goto done; | 
| 855 | 0 |     key->varkey = varkey; varkey = NULL; | 
| 856 | 0 |     key->chunkkey = chunkname; chunkname = NULL;     | 
| 857 |  | 
 | 
| 858 | 0 | done: | 
| 859 | 0 |     nullfree(chunkname); | 
| 860 | 0 |     nullfree(varkey); | 
| 861 | 0 |     return THROW(stat); | 
| 862 | 0 | } | 
| 863 |  |  | 
| 864 |  | void | 
| 865 |  | NCZ_dumpxcacheentry(NCZChunkCache* cache, NCZCacheEntry* e, NCbytes* buf) | 
| 866 | 0 | { | 
| 867 | 0 |     char s[8192]; | 
| 868 | 0 |     char idx[64]; | 
| 869 | 0 |     size_t i; | 
| 870 |  | 
 | 
| 871 | 0 |     ncbytescat(buf,"{"); | 
| 872 | 0 |     snprintf(s,sizeof(s),"modified=%u isfiltered=%u indices=", | 
| 873 | 0 |   (unsigned)e->modified, | 
| 874 | 0 |   (unsigned)e->isfiltered | 
| 875 | 0 |   ); | 
| 876 | 0 |     ncbytescat(buf,s); | 
| 877 | 0 |     for(i=0;i<cache->ndims;i++) { | 
| 878 | 0 |   snprintf(idx,sizeof(idx),"%s%llu",(i==0?"":"."),e->indices[i]); | 
| 879 | 0 |   ncbytescat(buf,idx); | 
| 880 | 0 |     } | 
| 881 | 0 |     snprintf(s,sizeof(s),"size=%llu data=%p", | 
| 882 | 0 |   e->size, | 
| 883 | 0 |   e->data | 
| 884 | 0 |   ); | 
| 885 | 0 |     ncbytescat(buf,s); | 
| 886 | 0 |     ncbytescat(buf,"}"); | 
| 887 | 0 | } | 
| 888 |  |  | 
| 889 |  | void | 
| 890 |  | NCZ_printxcache(NCZChunkCache* cache) | 
| 891 | 0 | { | 
| 892 | 0 |     static char xs[20000]; | 
| 893 | 0 |     NCbytes* buf = ncbytesnew(); | 
| 894 | 0 |     char s[8192]; | 
| 895 | 0 |     size_t i; | 
| 896 |  | 
 | 
| 897 | 0 |     ncbytescat(buf,"NCZChunkCache:\n"); | 
| 898 | 0 |     snprintf(s,sizeof(s),"\tvar=%s\n\tndims=%u\n\tchunksize=%u\n\tchunkcount=%u\n\tfillchunk=%p\n", | 
| 899 | 0 |       cache->var->hdr.name, | 
| 900 | 0 |       (unsigned)cache->ndims, | 
| 901 | 0 |       (unsigned)cache->chunksize, | 
| 902 | 0 |       (unsigned)cache->chunkcount, | 
| 903 | 0 |   cache->fillchunk | 
| 904 | 0 |   ); | 
| 905 | 0 |     ncbytescat(buf,s); | 
| 906 |  | 
 | 
| 907 | 0 |     snprintf(s,sizeof(s),"\tmaxentries=%u\n\tmaxsize=%u\n\tused=%u\n\tdimsep='%c'\n", | 
| 908 | 0 |       (unsigned)cache->params.nelems, | 
| 909 | 0 |   (unsigned)cache->params.size, | 
| 910 | 0 |       (unsigned)cache->used, | 
| 911 | 0 |       cache->dimension_separator | 
| 912 | 0 |   ); | 
| 913 | 0 |     ncbytescat(buf,s); | 
| 914 |  |      | 
| 915 | 0 |     snprintf(s,sizeof(s),"\tmru: (%u)\n",(unsigned)nclistlength(cache->mru)); | 
| 916 | 0 |     ncbytescat(buf,s); | 
| 917 | 0 |     if(nclistlength(cache->mru)==0)     | 
| 918 | 0 |         ncbytescat(buf,"\t\t<empty>\n"); | 
| 919 | 0 |     for(i=0;i<nclistlength(cache->mru);i++) { | 
| 920 | 0 |   NCZCacheEntry* e = (NCZCacheEntry*)nclistget(cache->mru,i); | 
| 921 | 0 |   snprintf(s,sizeof(s),"\t\t[%zu] ", i); | 
| 922 | 0 |   ncbytescat(buf,s); | 
| 923 | 0 |   if(e == NULL) | 
| 924 | 0 |       ncbytescat(buf,"<null>"); | 
| 925 | 0 |   else | 
| 926 | 0 |       NCZ_dumpxcacheentry(cache, e, buf); | 
| 927 | 0 |   ncbytescat(buf,"\n"); | 
| 928 | 0 |     } | 
| 929 |  | 
 | 
| 930 | 0 |     xs[0] = '\0'; | 
| 931 | 0 |     strlcat(xs,ncbytescontents(buf),sizeof(xs)); | 
| 932 | 0 |     ncbytesfree(buf); | 
| 933 |  |     fprintf(stderr,"%s\n",xs); | 
| 934 | 0 | } |