Coverage Report

Created: 2023-05-28 06:42

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