Coverage Report

Created: 2022-11-18 06:58

/src/netcdf-c/libnczarr/zsync.c
Line
Count
Source (jump to first uncovered line)
1
/*********************************************************************
2
 *   Copyright 1993, UCAR/Unidata
3
 *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4
 *********************************************************************/
5
6
#include "zincludes.h"
7
#include "zfilter.h"
8
9
#ifndef nulldup
10
 #define nulldup(x) ((x)?strdup(x):(x))
11
#endif
12
13
#undef FILLONCLOSE
14
15
/*mnemonics*/
16
#define DICTOPEN '{'
17
#define DICTCLOSE '}'
18
19
/* Forward */
20
static int ncz_collect_dims(NC_FILE_INFO_T* file, NC_GRP_INFO_T* grp, NCjson** jdimsp);
21
static int ncz_sync_var(NC_FILE_INFO_T* file, NC_VAR_INFO_T* var, int isclose);
22
23
static int load_jatts(NCZMAP* map, NC_OBJ* container, int nczarrv1, NCjson** jattrsp, NClist** atypes);
24
static int zconvert(NCjson* src, nc_type typeid, size_t typelen, int* countp, NCbytes* dst);
25
static int computeattrinfo(const char* name, NClist* atypes, nc_type typehint, int purezarr, NCjson* values,
26
    nc_type* typeidp, size_t* typelenp, size_t* lenp, void** datap);
27
static int parse_group_content(NCjson* jcontent, NClist* dimdefs, NClist* varnames, NClist* subgrps);
28
static int parse_group_content_pure(NCZ_FILE_INFO_T*  zinfo, NC_GRP_INFO_T* grp, NClist* varnames, NClist* subgrps);
29
static int define_grp(NC_FILE_INFO_T* file, NC_GRP_INFO_T* grp);
30
static int define_dims(NC_FILE_INFO_T* file, NC_GRP_INFO_T* grp, NClist* diminfo);
31
static int define_vars(NC_FILE_INFO_T* file, NC_GRP_INFO_T* grp, NClist* varnames);
32
static int define_subgrps(NC_FILE_INFO_T* file, NC_GRP_INFO_T* grp, NClist* subgrpnames);
33
static int searchvars(NCZ_FILE_INFO_T*, NC_GRP_INFO_T*, NClist*);
34
static int searchsubgrps(NCZ_FILE_INFO_T*, NC_GRP_INFO_T*, NClist*);
35
static int locategroup(NC_FILE_INFO_T* file, size_t nsegs, NClist* segments, NC_GRP_INFO_T** grpp);
36
static int createdim(NC_FILE_INFO_T* file, const char* name, size64_t dimlen, NC_DIM_INFO_T** dimp);
37
static int parsedimrefs(NC_FILE_INFO_T*, NClist* dimnames,  size64_t* shape, NC_DIM_INFO_T** dims, int create);
38
static int decodeints(NCjson* jshape, size64_t* shapes);
39
static int computeattrdata(nc_type typehint, nc_type* typeidp, NCjson* values, size_t* typelenp, size_t* lenp, void** datap);
40
static int computedimrefs(NC_FILE_INFO_T* file, NC_VAR_INFO_T* var, int purezarr, int xarray, int ndims, NClist* dimnames, size64_t* shapes, NC_DIM_INFO_T** dims);
41
static int json_convention_read(NCjson* jdict, NCjson** jtextp);
42
static int jtypes2atypes(NCjson* jtypes, NClist* atypes);
43
44
/**************************************************/
45
/**************************************************/
46
/* Synchronize functions to make map and memory
47
be consistent. There are two sets of functions,
48
1) _sync_ - push memory to map (optionally create target)
49
2) _read_ - pull map data into memory
50
These functions are generally non-recursive. It is assumed
51
that the recursion occurs in the caller's code.
52
*/
53
54
/**
55
 * @internal Synchronize file metadata from memory to map.
56
 *
57
 * @param file Pointer to file info struct.
58
 *
59
 * @return ::NC_NOERR No error.
60
 * @author Dennis Heimbigner
61
 */
62
int
63
ncz_sync_file(NC_FILE_INFO_T* file, int isclose)
64
0
{
65
0
    int stat = NC_NOERR;
66
0
    NCjson* json = NULL;
67
68
0
    NC_UNUSED(isclose);
69
70
0
    LOG((3, "%s: file: %s", __func__, file->controller->path));
71
0
    ZTRACE(3,"file=%s isclose=%d",file->controller->path,isclose);
72
73
    /* Write out root group recursively */
74
0
    if((stat = ncz_sync_grp(file, file->root_grp, isclose)))
75
0
        goto done;
76
77
0
done:
78
0
    NCJreclaim(json);
79
0
    return ZUNTRACE(stat);
80
0
}
81
82
/**
83
 * @internal Synchronize dimension data from memory to map.
84
 *
85
 * @param grp Pointer to grp struct containing the dims.
86
 *
87
 * @return ::NC_NOERR No error.
88
 * @author Dennis Heimbigner
89
 */
90
static int
91
ncz_collect_dims(NC_FILE_INFO_T* file, NC_GRP_INFO_T* grp, NCjson** jdimsp)
92
0
{
93
0
    int i, stat=NC_NOERR;
94
0
    NCjson* jdims = NULL;
95
96
0
    LOG((3, "%s: ", __func__));
97
0
    ZTRACE(3,"file=%s grp=%s",file->controller->path,grp->hdr.name);
98
99
0
    NCJnew(NCJ_DICT,&jdims);
100
0
    for(i=0; i<ncindexsize(grp->dim); i++) {
101
0
  NC_DIM_INFO_T* dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
102
0
  char slen[128];
103
0
  snprintf(slen,sizeof(slen),"%llu",(unsigned long long)dim->len);
104
0
  if((stat = NCJaddstring(jdims,NCJ_STRING,dim->hdr.name))) goto done;
105
0
  if((stat = NCJaddstring(jdims,NCJ_INT,slen))) goto done;
106
0
    }
107
0
    if(jdimsp) {*jdimsp = jdims; jdims = NULL;}
108
0
done:
109
0
    NCJreclaim(jdims);
110
0
    return ZUNTRACE(THROW(stat));
111
0
}
112
113
/**
114
 * @internal Recursively synchronize group from memory to map.
115
 *
116
 * @param file Pointer to file struct
117
 * @param grp Pointer to grp struct
118
 *
119
 * @return ::NC_NOERR No error.
120
 * @author Dennis Heimbigner
121
 */
122
int
123
ncz_sync_grp(NC_FILE_INFO_T* file, NC_GRP_INFO_T* grp, int isclose)
124
0
{
125
0
    int i,stat = NC_NOERR;
126
0
    NCZ_FILE_INFO_T* zinfo = NULL;
127
0
    char version[1024];
128
0
    int purezarr = 0;
129
0
    NCZMAP* map = NULL;
130
0
    char* fullpath = NULL;
131
0
    char* key = NULL;
132
0
    NCjson* json = NULL;
133
0
    NCjson* jgroup = NULL;
134
0
    NCjson* jdims = NULL;
135
0
    NCjson* jvars = NULL;
136
0
    NCjson* jsubgrps = NULL;
137
0
    NCjson* jsuper = NULL;
138
0
    NCjson* jtmp = NULL;
139
140
0
    LOG((3, "%s: dims: %s", __func__, key));
141
0
    ZTRACE(3,"file=%s grp=%s isclose=%d",file->controller->path,grp->hdr.name,isclose);
142
143
0
    zinfo = file->format_file_info;
144
0
    map = zinfo->map;
145
146
0
    purezarr = (zinfo->controls.flags & FLAG_PUREZARR)?1:0;
147
148
    /* Construct grp key */
149
0
    if((stat = NCZ_grpkey(grp,&fullpath)))
150
0
  goto done;
151
152
0
    if(!purezarr) {
153
        /* Create dimensions dict */
154
0
        if((stat = ncz_collect_dims(file,grp,&jdims))) goto done;
155
156
        /* Create vars list */
157
0
        if((stat = NCJnew(NCJ_ARRAY,&jvars)))
158
0
      goto done;
159
0
        for(i=0; i<ncindexsize(grp->vars); i++) {
160
0
      NC_VAR_INFO_T* var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
161
0
      if((stat = NCJaddstring(jvars,NCJ_STRING,var->hdr.name))) goto done;
162
0
        }
163
164
        /* Create subgroups list */
165
0
        if((stat = NCJnew(NCJ_ARRAY,&jsubgrps)))
166
0
          goto done;
167
0
        for(i=0; i<ncindexsize(grp->children); i++) {
168
0
      NC_GRP_INFO_T* g = (NC_GRP_INFO_T*)ncindexith(grp->children,i);
169
0
      if((stat = NCJaddstring(jsubgrps,NCJ_STRING,g->hdr.name))) goto done;
170
0
        }
171
        /* Create the "_nczarr_group" dict */
172
0
        if((stat = NCJnew(NCJ_DICT,&json)))
173
0
      goto done;
174
        /* Insert the various dicts and arrays */
175
0
        if((stat = NCJinsert(json,"dims",jdims))) goto done;
176
0
        jdims = NULL; /* avoid memory problems */
177
0
        if((stat = NCJinsert(json,"vars",jvars))) goto done;
178
0
        jvars = NULL; /* avoid memory problems */
179
0
        if((stat = NCJinsert(json,"groups",jsubgrps))) goto done;
180
0
        jsubgrps = NULL; /* avoid memory problems */
181
0
    }
182
183
    /* build ZGROUP contents */
184
0
    if((stat = NCJnew(NCJ_DICT,&jgroup)))
185
0
  goto done;
186
0
    snprintf(version,sizeof(version),"%d",zinfo->zarr.zarr_version);
187
0
    if((stat = NCJaddstring(jgroup,NCJ_STRING,"zarr_format"))) goto done;
188
0
    if((stat = NCJaddstring(jgroup,NCJ_INT,version))) goto done;
189
0
    if(!purezarr && grp->parent == NULL) { /* Root group */
190
0
        snprintf(version,sizeof(version),"%lu.%lu.%lu",
191
0
     zinfo->zarr.nczarr_version.major,
192
0
     zinfo->zarr.nczarr_version.minor,
193
0
     zinfo->zarr.nczarr_version.release);
194
0
  if((stat = NCJnew(NCJ_DICT,&jsuper))) goto done;
195
0
  if((stat-NCJnewstring(NCJ_STRING,version,&jtmp))) goto done;
196
0
  if((stat = NCJinsert(jsuper,"version",jtmp))) goto done;
197
0
  jtmp = NULL;
198
0
  if((stat = NCJinsert(jgroup,NCZ_V2_SUPERBLOCK,jsuper))) goto done;
199
0
  jsuper = NULL;
200
0
    }
201
202
0
    if(!purezarr) {
203
        /* Insert the "_NCZARR_GROUP" dict */
204
0
        if((stat = NCJinsert(jgroup,NCZ_V2_GROUP,json))) goto done;
205
0
        json = NULL;
206
0
    }
207
208
    /* build ZGROUP path */
209
0
    if((stat = nczm_concat(fullpath,ZGROUP,&key)))
210
0
  goto done;
211
    /* Write to map */
212
0
    if((stat=NCZ_uploadjson(map,key,jgroup)))
213
0
  goto done;
214
0
    nullfree(key); key = NULL;
215
216
    /* Build the .zattrs object */
217
0
    assert(grp->att);
218
0
    if((stat = ncz_sync_atts(file,(NC_OBJ*)grp, grp->att, isclose)))
219
0
  goto done;
220
221
    /* Now synchronize all the variables */
222
0
    for(i=0; i<ncindexsize(grp->vars); i++) {
223
0
  NC_VAR_INFO_T* var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
224
0
  if((stat = ncz_sync_var(file,var,isclose))) goto done;
225
0
    }
226
227
    /* Now recurse to synchronize all the subgrps */
228
0
    for(i=0; i<ncindexsize(grp->children); i++) {
229
0
  NC_GRP_INFO_T* g = (NC_GRP_INFO_T*)ncindexith(grp->children,i);
230
0
  if((stat = ncz_sync_grp(file,g,isclose))) goto done;
231
0
    }
232
233
0
done:
234
0
    NCJreclaim(jtmp);
235
0
    NCJreclaim(jsuper);
236
0
    NCJreclaim(json);
237
0
    NCJreclaim(jgroup);
238
0
    NCJreclaim(jdims);
239
0
    NCJreclaim(jvars);
240
0
    NCJreclaim(jsubgrps);
241
0
    nullfree(fullpath);
242
0
    nullfree(key);
243
0
    return ZUNTRACE(THROW(stat));
244
0
}
245
246
/**
247
 * @internal Synchronize variable meta data from memory to map.
248
 *
249
 * @param file Pointer to file struct
250
 * @param var Pointer to var struct
251
 * @param isclose If this called as part of nc_close() as opposed to nc_enddef().
252
 *
253
 * @return ::NC_NOERR No error.
254
 * @author Dennis Heimbigner
255
 */
256
static int
257
ncz_sync_var_meta(NC_FILE_INFO_T* file, NC_VAR_INFO_T* var, int isclose)
258
0
{
259
0
    int i,stat = NC_NOERR;
260
0
    NCZ_FILE_INFO_T* zinfo = NULL;
261
0
    char number[1024];
262
0
    NCZMAP* map = NULL;
263
0
    char* fullpath = NULL;
264
0
    char* key = NULL;
265
0
    char* dimpath = NULL;
266
0
    NClist* dimrefs = NULL;
267
0
    NCjson* jvar = NULL;
268
0
    NCjson* jncvar = NULL;
269
0
    NCjson* jdimrefs = NULL;
270
0
    NCjson* jtmp = NULL;
271
0
    NCjson* jfill = NULL;
272
0
    char* dtypename = NULL;
273
0
    int purezarr = 0;
274
0
    size64_t shape[NC_MAX_VAR_DIMS];
275
0
    NCZ_VAR_INFO_T* zvar = var->format_var_info;
276
0
#ifdef ENABLE_NCZARR_FILTERS
277
0
    NClist* filterchain = NULL;
278
0
    NCjson* jfilter = NULL;
279
0
#endif
280
281
0
    ZTRACE(3,"file=%s var=%s isclose=%d",file->controller->path,var->hdr.name,isclose);
282
283
0
    zinfo = file->format_file_info;
284
0
    map = zinfo->map;
285
286
0
    purezarr = (zinfo->controls.flags & FLAG_PUREZARR)?1:0;
287
288
    /* Make sure that everything is established */
289
    /* ensure the fill value */
290
0
    if((stat = NCZ_ensure_fill_value(var))) goto done; /* ensure var->fill_value is set */
291
0
    assert(var->no_fill || var->fill_value != NULL);
292
    /* ensure the chunk cache */
293
0
    if((stat = NCZ_adjust_var_cache(var))) goto done;
294
    /* rebuild the fill chunk */
295
0
    if((stat = NCZ_ensure_fill_chunk(zvar->cache))) goto done;
296
0
#ifdef ENABLE_NCZARR_FILTERS
297
    /* Build the filter working parameters for any filters */
298
0
    if((stat = NCZ_filter_setup(var))) goto done;
299
0
#endif
300
301
    /* Construct var path */
302
0
    if((stat = NCZ_varkey(var,&fullpath)))
303
0
  goto done;
304
305
    /* Create the zarray json object */
306
0
    if((stat = NCJnew(NCJ_DICT,&jvar)))
307
0
  goto done;
308
309
    /* zarr_format key */
310
0
    snprintf(number,sizeof(number),"%d",zinfo->zarr.zarr_version);
311
0
    if((stat = NCJaddstring(jvar,NCJ_STRING,"zarr_format"))) goto done;
312
0
    if((stat = NCJaddstring(jvar,NCJ_INT,number))) goto done;
313
314
    /* Collect the shape vector */
315
0
    for(i=0;i<var->ndims;i++) {
316
0
  NC_DIM_INFO_T* dim = var->dim[i];
317
0
  shape[i] = dim->len;
318
0
    }
319
    /* but might be scalar */
320
0
    if(var->ndims == 0)
321
0
        shape[0] = 1;
322
323
    /* shape key */
324
    /* Integer list defining the length of each dimension of the array.*/
325
    /* Create the list */
326
0
    if((stat = NCJnew(NCJ_ARRAY,&jtmp))) goto done;
327
0
    if(zvar->scalar) {
328
0
  NCJaddstring(jtmp,NCJ_INT,"1");
329
0
    } else for(i=0;i<var->ndims;i++) {
330
0
  snprintf(number,sizeof(number),"%llu",shape[i]);
331
0
  NCJaddstring(jtmp,NCJ_INT,number);
332
0
    }
333
0
    if((stat = NCJinsert(jvar,"shape",jtmp))) goto done;
334
0
    jtmp = NULL;
335
336
    /* dtype key */
337
    /* A string or list defining a valid data type for the array. */
338
0
    if((stat = NCJaddstring(jvar,NCJ_STRING,"dtype"))) goto done;
339
0
    { /* Add the type name */
340
0
  int endianness = var->type_info->endianness;
341
0
  int atomictype = var->type_info->hdr.id;
342
0
  assert(atomictype > 0 && atomictype <= NC_MAX_ATOMIC_TYPE);
343
0
  if((stat = ncz_nctype2dtype(atomictype,endianness,purezarr,NCZ_get_maxstrlen((NC_OBJ*)var),&dtypename))) goto done;
344
0
  if((stat = NCJaddstring(jvar,NCJ_STRING,dtypename))) goto done;
345
0
  nullfree(dtypename); dtypename = NULL;
346
0
    }
347
348
    /* chunks key */
349
    /* It is not clear if the zarr format supports the concept
350
       of contiguous, so we will simulate by:
351
       1. setting a flag in _nczvar (below)
352
       2. making the chunk sizes all be same as the max dim size (here)
353
    */
354
    /* list of chunk sizes */
355
0
    if((stat = NCJaddstring(jvar,NCJ_STRING,"chunks"))) goto done;
356
    /* Create the list */
357
0
    if((stat = NCJnew(NCJ_ARRAY,&jtmp))) goto done;
358
0
    if(zvar->scalar) {
359
0
  NCJaddstring(jtmp,NCJ_INT,"1"); /* one chunk of size 1 */
360
0
    } else for(i=0;i<var->ndims;i++) {
361
0
  size64_t len = (var->storage == NC_CONTIGUOUS ? shape[i] : var->chunksizes[i]);
362
0
  snprintf(number,sizeof(number),"%lld",len);
363
0
  NCJaddstring(jtmp,NCJ_INT,number);
364
0
    }
365
0
    if((stat = NCJappend(jvar,jtmp))) goto done;
366
0
    jtmp = NULL;
367
368
    /* fill_value key */
369
0
    if(var->no_fill) {
370
0
  if((stat=NCJnew(NCJ_NULL,&jfill))) goto done;
371
0
    } else {/*!var->no_fill*/
372
0
  int atomictype = var->type_info->hdr.id;
373
0
        if(var->fill_value == NULL) {
374
0
       if((stat = NCZ_ensure_fill_value(var))) goto done;
375
0
  }
376
        /* Convert var->fill_value to a string */
377
0
  if((stat = NCZ_stringconvert(atomictype,1,var->fill_value,&jfill))) goto done;
378
0
  assert(jfill->sort != NCJ_ARRAY);
379
0
    }
380
0
    if((stat = NCJinsert(jvar,"fill_value",jfill))) goto done;
381
0
    jfill = NULL;
382
383
    /* order key */
384
0
    if((stat = NCJaddstring(jvar,NCJ_STRING,"order"))) goto done;
385
    /* "C" means row-major order, i.e., the last dimension varies fastest;
386
       "F" means column-major order, i.e., the first dimension varies fastest.*/
387
    /* Default to C for now */
388
0
    if((stat = NCJaddstring(jvar,NCJ_STRING,"C"))) goto done;
389
390
    /* Compressor and Filters */
391
    /* compressor key */
392
    /* From V2 Spec: A JSON object identifying the primary compression codec and providing
393
       configuration parameters, or ``null`` if no compressor is to be used. */
394
0
    if((stat = NCJaddstring(jvar,NCJ_STRING,"compressor"))) goto done;
395
0
#ifdef ENABLE_NCZARR_FILTERS
396
0
    filterchain = (NClist*)var->filters;
397
0
    if(nclistlength(filterchain) > 0) {
398
0
  struct NCZ_Filter* filter = (struct NCZ_Filter*)nclistget(filterchain,nclistlength(filterchain)-1);
399
        /* encode up the compressor */
400
0
        if((stat = NCZ_filter_jsonize(file,var,filter,&jtmp))) goto done;
401
0
    } else
402
0
#endif
403
0
    { /* no filters at all */
404
        /* Default to null */
405
0
        if((stat = NCJnew(NCJ_NULL,&jtmp))) goto done;
406
0
    }
407
0
    if(jtmp && (stat = NCJappend(jvar,jtmp))) goto done;
408
0
    jtmp = NULL;
409
410
    /* filters key */
411
    /* From V2 Spec: A list of JSON objects providing codec configurations,
412
       or null if no filters are to be applied. Each codec configuration
413
       object MUST contain a "id" key identifying the codec to be used. */
414
    /* A list of JSON objects providing codec configurations, or ``null``
415
       if no filters are to be applied. */
416
0
    if((stat = NCJaddstring(jvar,NCJ_STRING,"filters"))) goto done;
417
0
#ifdef ENABLE_NCZARR_FILTERS
418
0
    if(nclistlength(filterchain) > 1) {
419
0
  int k;
420
  /* jtmp holds the array of filters */
421
0
  if((stat = NCJnew(NCJ_ARRAY,&jtmp))) goto done;
422
0
  for(k=0;k<nclistlength(filterchain)-1;k++) {
423
0
      struct NCZ_Filter* filter = (struct NCZ_Filter*)nclistget(filterchain,k);
424
      /* encode up the filter as a string */
425
0
      if((stat = NCZ_filter_jsonize(file,var,filter,&jfilter))) goto done;
426
0
      if((stat = NCJappend(jtmp,jfilter))) goto done;
427
0
  }
428
0
    } else
429
0
#endif
430
0
    { /* no filters at all */
431
0
        if((stat = NCJnew(NCJ_NULL,&jtmp))) goto done;
432
0
    }
433
0
    if((stat = NCJappend(jvar,jtmp))) goto done;
434
0
    jtmp = NULL;
435
436
    /* dimension_separator key */
437
    /* Single char defining the separator in chunk keys */
438
0
    if(zvar->dimension_separator != DFALT_DIM_SEPARATOR) {
439
0
  char sep[2];
440
0
  sep[0] = zvar->dimension_separator;/* make separator a string*/
441
0
  sep[1] = '\0';
442
0
        if((stat = NCJnewstring(NCJ_STRING,sep,&jtmp))) goto done;
443
0
        if((stat = NCJinsert(jvar,"dimension_separator",jtmp))) goto done;
444
0
        jtmp = NULL;
445
0
    }
446
447
    /* Capture dimref names as FQNs */
448
0
    if(var->ndims > 0) {
449
0
        if((dimrefs = nclistnew())==NULL) {stat = NC_ENOMEM; goto done;}
450
0
  for(i=0;i<var->ndims;i++) {
451
0
      NC_DIM_INFO_T* dim = var->dim[i];
452
0
      if((stat = NCZ_dimkey(dim,&dimpath))) goto done;
453
0
      nclistpush(dimrefs,dimpath);
454
0
      dimpath = NULL;
455
0
  }
456
0
    }
457
458
    /* Build the NCZ_V2_ARRAY object */
459
0
    {
460
  /* Create the dimrefs json object */
461
0
  if((stat = NCJnew(NCJ_ARRAY,&jdimrefs)))
462
0
      goto done;
463
0
  for(i=0;i<nclistlength(dimrefs);i++) {
464
0
      const char* dim = nclistget(dimrefs,i);
465
0
      NCJaddstring(jdimrefs,NCJ_STRING,dim);
466
0
  }
467
0
  if((stat = NCJnew(NCJ_DICT,&jncvar)))
468
0
      goto done;
469
470
  /* Insert dimrefs  */
471
0
  if((stat = NCJinsert(jncvar,"dimrefs",jdimrefs)))
472
0
      goto done;
473
0
  jdimrefs = NULL; /* Avoid memory problems */
474
475
  /* Add the _Storage flag */
476
  /* Record if this is a scalar; use the storage field */
477
0
  if(var->ndims == 0) {
478
0
      if((stat = NCJnewstring(NCJ_STRING,"scalar",&jtmp)))goto done;
479
0
  } else if(var->storage == NC_CONTIGUOUS) {
480
0
       if((stat = NCJnewstring(NCJ_STRING,"contiguous",&jtmp)))goto done;
481
0
  } else if(var->storage == NC_COMPACT) {
482
0
       if((stat = NCJnewstring(NCJ_STRING,"compact",&jtmp)))goto done;
483
0
  } else {/* chunked */
484
0
       if((stat = NCJnewstring(NCJ_STRING,"chunked",&jtmp)))goto done;
485
0
  }
486
0
  if((stat = NCJinsert(jncvar,"storage",jtmp))) goto done;
487
0
  jtmp = NULL;
488
489
0
  if(!(zinfo->controls.flags & FLAG_PUREZARR)) {
490
0
      if((stat = NCJinsert(jvar,NCZ_V2_ARRAY,jncvar))) goto done;
491
0
      jncvar = NULL;
492
0
  }
493
0
    }
494
495
    /* build .zarray path */
496
0
    if((stat = nczm_concat(fullpath,ZARRAY,&key)))
497
0
  goto done;
498
499
    /* Write to map */
500
0
    if((stat=NCZ_uploadjson(map,key,jvar)))
501
0
  goto done;
502
0
    nullfree(key); key = NULL;
503
504
0
    var->created = 1;
505
506
    /* Build .zattrs object */
507
0
    assert(var->att);
508
0
    if((stat = ncz_sync_atts(file,(NC_OBJ*)var, var->att, isclose)))
509
0
  goto done;
510
511
0
done:
512
0
    nclistfreeall(dimrefs);
513
0
    nullfree(fullpath);
514
0
    nullfree(key);
515
0
    nullfree(dtypename);
516
0
    nullfree(dimpath);
517
0
    NCJreclaim(jvar);
518
0
    NCJreclaim(jncvar);
519
0
    NCJreclaim(jtmp);
520
0
    NCJreclaim(jfill);
521
0
    return ZUNTRACE(THROW(stat));
522
0
}
523
524
/**
525
 * @internal Synchronize variable meta data and data from memory to map.
526
 *
527
 * @param file Pointer to file struct
528
 * @param var Pointer to var struct
529
 * @param isclose If this called as part of nc_close() as opposed to nc_enddef().
530
 *
531
 * @return ::NC_NOERR No error.
532
 * @author Dennis Heimbigner
533
 */
534
static int
535
ncz_sync_var(NC_FILE_INFO_T* file, NC_VAR_INFO_T* var, int isclose)
536
0
{
537
0
    int stat = NC_NOERR;
538
0
    NCZ_VAR_INFO_T* zvar = var->format_var_info;
539
540
0
    ZTRACE(3,"file=%s var=%s isclose=%d",file->controller->path,var->hdr.name,isclose);
541
542
0
    if(isclose) {
543
0
  if((stat = ncz_sync_var_meta(file,var,isclose))) goto done;
544
0
    }
545
546
    /* flush only chunks that have been written */
547
0
    if(zvar->cache) {
548
0
        if((stat = NCZ_flush_chunk_cache(zvar->cache)))
549
0
      goto done;
550
0
    }
551
552
0
done:
553
0
    return ZUNTRACE(THROW(stat));
554
0
}
555
556
557
/*
558
Flush all chunks to disk. Create any that are missing
559
and fill as needed.
560
*/
561
int
562
ncz_write_var(NC_VAR_INFO_T* var)
563
0
{
564
0
    int stat = NC_NOERR;
565
0
    NCZ_VAR_INFO_T* zvar = (NCZ_VAR_INFO_T*)var->format_var_info;
566
567
0
    ZTRACE(3,"var=%s",var->hdr.name);
568
569
    /* Flush the cache */
570
0
    if(zvar->cache) {
571
0
        if((stat = NCZ_flush_chunk_cache(zvar->cache))) goto done;
572
0
    }
573
574
#ifdef FILLONCLOSE
575
    /* If fill is enabled, then create missing chunks */
576
    if(!var->no_fill) {
577
        int i;
578
    NCZOdometer* chunkodom =  NULL;
579
    NC_FILE_INFO_T* file = var->container->nc4_info;
580
    NCZ_FILE_INFO_T* zfile = (NCZ_FILE_INFO_T*)file->format_file_info;
581
    NCZMAP* map = zfile->map;
582
    size64_t start[NC_MAX_VAR_DIMS];
583
    size64_t stop[NC_MAX_VAR_DIMS];
584
    size64_t stride[NC_MAX_VAR_DIMS];
585
    char* key = NULL;
586
587
    if(var->ndims == 0) { /* scalar */
588
  start[i] = 0;
589
  stop[i] = 1;
590
        stride[i] = 1;
591
    } else {
592
        for(i=0;i<var->ndims;i++) {
593
      size64_t nchunks = ceildiv(var->dim[i]->len,var->chunksizes[i]);
594
      start[i] = 0;
595
      stop[i] = nchunks;
596
      stride[i] = 1;
597
        }
598
    }
599
600
    {
601
  if(zvar->scalar) {
602
      if((chunkodom = nczodom_new(1,start,stop,stride,stop))==NULL)
603
  } else {
604
      /* Iterate over all the chunks to create missing ones */
605
      if((chunkodom = nczodom_new(var->ndims,start,stop,stride,stop))==NULL)
606
          {stat = NC_ENOMEM; goto done;}
607
  }
608
  for(;nczodom_more(chunkodom);nczodom_next(chunkodom)) {
609
      size64_t* indices = nczodom_indices(chunkodom);
610
      /* Convert to key */
611
      if((stat = NCZ_buildchunkpath(zvar->cache,indices,&key))) goto done;
612
      switch (stat = nczmap_exists(map,key)) {
613
      case NC_NOERR: goto next; /* already exists */
614
      case NC_EEMPTY: break; /* does not exist, create it with fill */
615
      default: goto done; /* some other error */
616
      }
617
            /* If we reach here, then chunk does not exist, create it with fill */
618
      assert(zvar->cache->fillchunk != NULL);
619
      if((stat=nczmap_write(map,key,0,zvar->cache->chunksize,zvar->cache->fillchunk))) goto done;
620
next:
621
      nullfree(key);
622
      key = NULL;
623
  }
624
    }
625
    nczodom_free(chunkodom);
626
    nullfree(key);
627
    }
628
#endif /*FILLONCLOSE*/
629
630
0
done:
631
0
    return ZUNTRACE(THROW(stat));
632
0
}
633
634
/**
635
 * @internal Synchronize attribute data from memory to map.
636
 *
637
 * @param container Pointer to grp|var struct containing the attributes
638
 * @param key the name of the map entry
639
 *
640
 * @return ::NC_NOERR No error.
641
 * @author Dennis Heimbigner
642
 */
643
int
644
ncz_sync_atts(NC_FILE_INFO_T* file, NC_OBJ* container, NCindex* attlist, int isclose)
645
0
{
646
0
    int i,stat = NC_NOERR;
647
0
    NCZ_FILE_INFO_T* zinfo = NULL;
648
0
    NCjson* jatts = NULL;
649
0
    NCjson* jtypes = NULL;
650
0
    NCjson* jtype = NULL;
651
0
    NCjson* jdimrefs = NULL;
652
0
    NCjson* jdict = NULL;
653
0
    NCjson* jint = NULL;
654
0
    NCjson* jdata = NULL;
655
0
    NCZMAP* map = NULL;
656
0
    char* fullpath = NULL;
657
0
    char* key = NULL;
658
0
    char* content = NULL;
659
0
    char* dimpath = NULL;
660
0
    int isxarray = 0;
661
0
    int inrootgroup = 0;
662
0
    NC_VAR_INFO_T* var = NULL;
663
0
    NC_GRP_INFO_T* grp = NULL;
664
0
    char* tname = NULL;
665
0
    int purezarr = 0;
666
0
    int endianness = (NC_isLittleEndian()?NC_ENDIAN_LITTLE:NC_ENDIAN_BIG);
667
668
0
    LOG((3, "%s", __func__));
669
0
    ZTRACE(3,"file=%s container=%s |attlist|=%u",file->controller->path,container->name,(unsigned)ncindexsize(attlist));
670
    
671
0
    if(container->sort == NCVAR) {
672
0
        var = (NC_VAR_INFO_T*)container;
673
0
  if(var->container && var->container->parent == NULL)
674
0
      inrootgroup = 1;
675
0
    } else if(container->sort == NCGRP) {
676
0
        grp = (NC_GRP_INFO_T*)container;
677
0
    }
678
    
679
0
    zinfo = file->format_file_info;
680
0
    map = zinfo->map;
681
682
0
    purezarr = (zinfo->controls.flags & FLAG_PUREZARR)?1:0;
683
0
    if(zinfo->controls.flags & FLAG_XARRAYDIMS) isxarray = 1;
684
685
    /* Create the attribute dictionary */
686
0
    if((stat = NCJnew(NCJ_DICT,&jatts))) goto done;
687
688
0
    if(ncindexsize(attlist) > 0) {
689
        /* Create the jncattr.types object */
690
0
        if((stat = NCJnew(NCJ_DICT,&jtypes)))
691
0
      goto done;
692
        /* Walk all the attributes convert to json and collect the dtype */
693
0
        for(i=0;i<ncindexsize(attlist);i++) {
694
0
      NC_ATT_INFO_T* a = (NC_ATT_INFO_T*)ncindexith(attlist,i);
695
0
      size_t typesize = 0;
696
#if 0
697
      const NC_reservedatt* ra = NC_findreserved(a->hdr.name);
698
      /* If reserved and hidden, then ignore */
699
      if(ra && (ra->flags & HIDDENATTRFLAG)) continue;
700
#endif
701
0
      if(a->nc_typeid > NC_MAX_ATOMIC_TYPE)
702
0
          {stat = (THROW(NC_ENCZARR)); goto done;}
703
0
      if(a->nc_typeid == NC_STRING)
704
0
          typesize = NCZ_get_maxstrlen(container);
705
0
      else
706
0
          {if((stat = NC4_inq_atomic_type(a->nc_typeid,NULL,&typesize))) goto done;}
707
      /* Convert to storable json */
708
0
      if((stat = NCZ_stringconvert(a->nc_typeid,a->len,a->data,&jdata))) goto done;
709
0
      if((stat = NCJinsert(jatts,a->hdr.name,jdata))) goto done;
710
0
      jdata = NULL;
711
712
      /* Collect the corresponding dtype */
713
0
      {
714
0
          if((stat = ncz_nctype2dtype(a->nc_typeid,endianness,purezarr,typesize,&tname))) goto done;
715
0
            if((stat = NCJnewstring(NCJ_STRING,tname,&jtype))) goto done;
716
0
          nullfree(tname); tname = NULL;
717
0
          if((stat = NCJinsert(jtypes,a->hdr.name,jtype))) goto done; /* add {name: type} */
718
0
          jtype = NULL;
719
0
      }
720
0
        }
721
0
    }
722
723
    /* Construct container path */
724
0
    if(container->sort == NCGRP)
725
0
  stat = NCZ_grpkey(grp,&fullpath);
726
0
    else
727
0
  stat = NCZ_varkey(var,&fullpath);
728
0
    if(stat)
729
0
  goto done;
730
731
0
    if(container->sort == NCVAR) { 
732
0
        if(inrootgroup && isxarray) {
733
0
      int dimsinroot = 1;
734
      /* Insert the XARRAY _ARRAY_ATTRIBUTE attribute */
735
0
      if((stat = NCJnew(NCJ_ARRAY,&jdimrefs)))
736
0
          goto done;
737
      /* Fake the scalar case */
738
0
      if(var->ndims == 0) {
739
0
          NCJaddstring(jdimrefs,NCJ_STRING,XARRAYSCALAR);
740
0
      } else /* Walk the dimensions and capture the names */
741
0
      for(i=0;i<var->ndims;i++) {
742
0
          NC_DIM_INFO_T* dim = var->dim[i];
743
    /* Verify that the dimension is in the root group */
744
0
    if(dim->container && dim->container->parent != NULL) {
745
0
        dimsinroot = 0; /* dimension is not in root */
746
0
        break;
747
0
    }
748
0
      }
749
0
      if(dimsinroot) {
750
    /* Walk the dimensions and capture the names */
751
0
    for(i=0;i<var->ndims;i++) {
752
0
        char* dimname;
753
0
              NC_DIM_INFO_T* dim = var->dim[i];
754
0
        dimname = strdup(dim->hdr.name);
755
0
        if(dimname == NULL) {stat = NC_ENOMEM; goto done;}
756
0
              NCJaddstring(jdimrefs,NCJ_STRING,dimname);
757
0
                nullfree(dimname); dimname = NULL;
758
0
    }
759
          /* Add the _ARRAY_DIMENSIONS attribute */
760
0
          if((stat = NCJinsert(jatts,NC_XARRAY_DIMS,jdimrefs))) goto done;
761
0
          jdimrefs = NULL;
762
0
      }
763
0
        }
764
0
    }
765
    /* Add Quantize Attribute */
766
0
    if(container->sort == NCVAR && var && var->quantize_mode > 0) {    
767
0
  char mode[64];
768
0
  snprintf(mode,sizeof(mode),"%d",var->nsd);
769
0
        if((stat = NCJnewstring(NCJ_INT,mode,&jint)))
770
0
          goto done;
771
  /* Insert the quantize attribute */
772
0
  switch (var->quantize_mode) {
773
0
  case NC_QUANTIZE_BITGROOM:
774
0
      if((stat = NCJinsert(jatts,NC_QUANTIZE_BITGROOM_ATT_NAME,jint))) goto done; 
775
0
      jint = NULL;
776
0
      break;
777
0
  case NC_QUANTIZE_GRANULARBR:
778
0
      if((stat = NCJinsert(jatts,NC_QUANTIZE_GRANULARBR_ATT_NAME,jint))) goto done; 
779
0
      jint = NULL;
780
0
      break;
781
0
  case NC_QUANTIZE_BITROUND:
782
0
      if((stat = NCJinsert(jatts,NC_QUANTIZE_BITROUND_ATT_NAME,jint))) goto done; 
783
0
      jint = NULL;
784
0
      break;
785
0
  default: break;
786
0
  }
787
0
    }
788
789
0
    if(NCJlength(jatts) > 0) {
790
0
        if(!(zinfo->controls.flags & FLAG_PUREZARR)) {
791
      /* Insert the _NCZARR_ATTR attribute */
792
0
            if((stat = NCJnew(NCJ_DICT,&jdict)))
793
0
                goto done;
794
0
      if(jtypes != NULL)
795
0
                {if((stat = NCJinsert(jdict,"types",jtypes))) goto done;}
796
0
            jtypes = NULL;
797
0
      if(jdict != NULL)
798
0
                {if((stat = NCJinsert(jatts,NCZ_V2_ATTR,jdict))) goto done;}
799
0
            jdict = NULL;
800
0
  }
801
        /* write .zattrs path */
802
0
        if((stat = nczm_concat(fullpath,ZATTRS,&key)))
803
0
            goto done;
804
        /* Write to map */
805
0
        if((stat=NCZ_uploadjson(map,key,jatts)))
806
0
            goto done;
807
0
        nullfree(key); key = NULL;
808
0
    }
809
810
0
done:
811
0
    nullfree(fullpath);
812
0
    nullfree(key);
813
0
    nullfree(content);
814
0
    nullfree(dimpath);
815
0
    nullfree(tname);
816
0
    NCJreclaim(jatts);
817
0
    NCJreclaim(jtypes);
818
0
    NCJreclaim(jtype);
819
0
    NCJreclaim(jdimrefs);
820
0
    NCJreclaim(jdict);
821
0
    NCJreclaim(jint);
822
0
    NCJreclaim(jdata);
823
0
    return ZUNTRACE(THROW(stat));
824
0
}
825
826
827
/**************************************************/
828
829
/**
830
@internal Extract attributes from a group or var and return
831
the corresponding NCjson dict.
832
@param map - [in] the map object for storage
833
@param container - [in] the containing object
834
@param jattrsp - [out] the json for .zattrs
835
@param jtypesp - [out] the json for .ztypes
836
@return NC_NOERR
837
@author Dennis Heimbigner
838
*/
839
static int
840
load_jatts(NCZMAP* map, NC_OBJ* container, int nczarrv1, NCjson** jattrsp, NClist** atypesp)
841
0
{
842
0
    int stat = NC_NOERR;
843
0
    char* fullpath = NULL;
844
0
    char* key = NULL;
845
0
    NCjson* jnczarr = NULL;
846
0
    NCjson* jattrs = NULL;
847
0
    NCjson* jncattr = NULL;
848
0
    NClist* atypes = NULL; /* envv list */
849
850
0
    ZTRACE(3,"map=%p container=%s nczarrv1=%d",map,container->name,nczarrv1);
851
852
    /* alway return (possibly empty) list of types */
853
0
    atypes = nclistnew();
854
855
0
    if(container->sort == NCGRP) {
856
0
  NC_GRP_INFO_T* grp = (NC_GRP_INFO_T*)container;
857
  /* Get grp's fullpath name */
858
0
  if((stat = NCZ_grpkey(grp,&fullpath)))
859
0
      goto done;
860
0
    } else {
861
0
  NC_VAR_INFO_T* var = (NC_VAR_INFO_T*)container;
862
  /* Get var's fullpath name */
863
0
  if((stat = NCZ_varkey(var,&fullpath)))
864
0
      goto done;
865
0
    }
866
867
    /* Construct the path to the .zattrs object */
868
0
    if((stat = nczm_concat(fullpath,ZATTRS,&key)))
869
0
  goto done;
870
871
    /* Download the .zattrs object: may not exist if not NCZarr V1 */
872
0
    switch ((stat=NCZ_downloadjson(map,key,&jattrs))) {
873
0
    case NC_NOERR: break;
874
0
    case NC_EEMPTY: stat = NC_NOERR; break; /* did not exist */
875
0
    default: goto done; /* failure */
876
0
    }
877
0
    nullfree(key); key = NULL;
878
879
0
    if(jattrs != NULL) {
880
0
  if(nczarrv1) {
881
      /* Construct the path to the NCZATTRS object */
882
0
      if((stat = nczm_concat(fullpath,NCZATTRS,&key))) goto done;
883
      /* Download the NCZATTRS object: may not exist if pure zarr or using deprecated name */
884
0
      stat=NCZ_downloadjson(map,key,&jncattr);
885
0
      if(stat == NC_EEMPTY) {
886
          /* try deprecated name */
887
0
          nullfree(key); key = NULL;
888
0
          if((stat = nczm_concat(fullpath,NCZATTRDEP,&key))) goto done;
889
0
          stat=NCZ_downloadjson(map,key,&jncattr);
890
0
      }
891
0
  } else {/* Get _nczarr_attrs from .zattrs */
892
0
            stat = NCJdictget(jattrs,NCZ_V2_ATTR,&jncattr);
893
0
      if(!stat && jncattr == NULL)
894
0
          {stat = NCJdictget(jattrs,NCZ_V2_ATTR_UC,&jncattr);}
895
0
  }
896
0
  nullfree(key); key = NULL;
897
0
  switch (stat) {
898
0
  case NC_NOERR: break;
899
0
  case NC_EEMPTY: stat = NC_NOERR; jncattr = NULL; break;
900
0
  default: goto done; /* failure */
901
0
  }
902
0
  if(jncattr != NULL) {
903
0
      NCjson* jtypes = NULL;
904
      /* jncattr attribute should be a dict */
905
0
      if(NCJsort(jncattr) != NCJ_DICT) {stat = (THROW(NC_ENCZARR)); goto done;}
906
      /* Extract "types; may not exist if only hidden attributes are defined */
907
0
      if((stat = NCJdictget(jncattr,"types",&jtypes))) goto done;
908
0
      if(jtypes != NULL) {
909
0
          if(NCJsort(jtypes) != NCJ_DICT) {stat = (THROW(NC_ENCZARR)); goto done;}
910
          /* Convert to an envv list */
911
0
    if((stat = jtypes2atypes(jtypes,atypes))) goto done;
912
0
      }
913
0
  }
914
0
    }
915
0
    if(jattrsp) {*jattrsp = jattrs; jattrs = NULL;}
916
0
    if(atypesp) {*atypesp = atypes; atypes = NULL;}
917
918
0
done:
919
0
    if(nczarrv1)
920
0
        NCJreclaim(jncattr);
921
0
    if(stat) {
922
0
  NCJreclaim(jnczarr);
923
0
  nclistfreeall(atypes);
924
0
    }
925
0
    nullfree(fullpath);
926
0
    nullfree(key);
927
0
    return ZUNTRACE(THROW(stat));
928
0
}
929
930
/* Convert a JSON singleton or array of strings to a single string */
931
static int
932
zcharify(NCjson* src, NCbytes* buf)
933
0
{
934
0
    int i, stat = NC_NOERR;
935
0
    struct NCJconst jstr = NCJconst_empty;
936
937
0
    if(NCJsort(src) != NCJ_ARRAY) { /* singleton */
938
0
        if((stat = NCJcvt(src, NCJ_STRING, &jstr))) goto done;
939
0
        ncbytescat(buf,jstr.sval);
940
0
    } else for(i=0;i<NCJlength(src);i++) {
941
0
  NCjson* value = NCJith(src,i);
942
0
  if((stat = NCJcvt(value, NCJ_STRING, &jstr))) goto done;
943
0
  ncbytescat(buf,jstr.sval);
944
0
        nullfree(jstr.sval);jstr.sval = NULL;
945
0
    }
946
0
done:
947
0
    nullfree(jstr.sval);
948
0
    return stat;
949
0
}
950
951
/* Convert a json value to actual data values of an attribute. */
952
static int
953
zconvert(NCjson* src, nc_type typeid, size_t typelen, int* countp, NCbytes* dst)
954
0
{
955
0
    int stat = NC_NOERR;
956
0
    int i;
957
0
    int count = 0;
958
    
959
0
    ZTRACE(3,"src=%s typeid=%d typelen=%u",NCJtotext(src),typeid,typelen);
960
      
961
    /* 3 cases:
962
       (1) singleton atomic value
963
       (2) array of atomic values
964
       (3) other JSON expression
965
    */
966
0
    switch (NCJsort(src)) {
967
0
    case NCJ_INT: case NCJ_DOUBLE: case NCJ_BOOLEAN: /* case 1 */
968
0
  count = 1;
969
0
  if((stat = NCZ_convert1(src, typeid, dst)))
970
0
      goto done;
971
0
  break;
972
973
0
    case NCJ_ARRAY:
974
0
        if(typeid == NC_CHAR) {
975
0
      if((stat = zcharify(src,dst))) goto done;
976
0
      count = ncbyteslength(dst);
977
0
        } else {
978
0
      count = NCJlength(src);
979
0
      for(i=0;i<count;i++) {
980
0
          NCjson* value = NCJith(src,i);
981
0
                if((stat = NCZ_convert1(value, typeid, dst))) goto done;
982
0
      }
983
0
  }
984
0
  break;
985
0
    case NCJ_STRING:
986
0
  if(typeid == NC_CHAR) {
987
0
      if((stat = zcharify(src,dst))) goto done;
988
0
      count = ncbyteslength(dst);
989
      /* Special case for "" */
990
0
      if(count == 0) {
991
0
          ncbytesappend(dst,'\0');
992
0
          count = 1;
993
0
      }
994
0
  } else {
995
0
      if((stat = NCZ_convert1(src, typeid, dst))) goto done;
996
0
      count = 1;
997
0
  }
998
0
  break;
999
0
    default: stat = (THROW(NC_ENCZARR)); goto done;
1000
0
    }
1001
0
    if(countp) *countp = count;
1002
1003
0
done:
1004
0
    return ZUNTRACE(THROW(stat));
1005
0
}
1006
1007
/*
1008
Extract type and data for an attribute
1009
*/
1010
static int
1011
computeattrinfo(const char* name, NClist* atypes, nc_type typehint, int purezarr, NCjson* values,
1012
    nc_type* typeidp, size_t* typelenp, size_t* lenp, void** datap)
1013
0
{
1014
0
    int stat = NC_NOERR;
1015
0
    int i;
1016
0
    size_t len, typelen;
1017
0
    void* data = NULL;
1018
0
    nc_type typeid;
1019
1020
0
    ZTRACE(3,"name=%s |atypes|=%u typehint=%d purezarr=%d values=|%s|",name,nclistlength(atypes),typehint,purezarr,NCJtotext(values));
1021
1022
    /* Get type info for the given att */
1023
0
    typeid = NC_NAT;
1024
0
    for(i=0;i<nclistlength(atypes);i+=2) {
1025
0
  const char* aname = nclistget(atypes,i);
1026
0
  if(strcmp(aname,name)==0) {
1027
0
      const char* atype = nclistget(atypes,i+1);
1028
0
      if((stat = ncz_dtype2nctype(atype,typehint,purezarr,&typeid,NULL,NULL))) goto done;
1029
//    if((stat = ncz_nctypedecode(atype,&typeid))) goto done;
1030
0
      break;
1031
0
  }
1032
0
    }
1033
0
    if(typeid > NC_MAX_ATOMIC_TYPE)
1034
0
  {stat = NC_EINTERNAL; goto done;}
1035
    /* Use the hint if given one */
1036
0
    if(typeid == NC_NAT)
1037
0
        typeid = typehint;
1038
1039
0
    if((stat = computeattrdata(typehint, &typeid, values, &typelen, &len, &data))) goto done;
1040
1041
0
    if(typeidp) *typeidp = typeid;
1042
0
    if(lenp) *lenp = len;
1043
0
    if(typelenp) *typelenp = typelen;
1044
0
    if(datap) {*datap = data; data = NULL;}
1045
1046
0
done:
1047
0
    nullfree(data);
1048
0
    return ZUNTRACEX(THROW(stat),"typeid=%d typelen=%d len=%u",*typeidp,*typelenp,*lenp);
1049
0
}
1050
1051
/*
1052
Extract data for an attribute
1053
*/
1054
static int
1055
computeattrdata(nc_type typehint, nc_type* typeidp, NCjson* values, size_t* typelenp, size_t* countp, void** datap)
1056
0
{
1057
0
    int stat = NC_NOERR;
1058
0
    NCbytes* buf = ncbytesnew();
1059
0
    size_t typelen;
1060
0
    nc_type typeid = NC_NAT;
1061
0
    NCjson* jtext = NULL;
1062
0
    int reclaimvalues = 0;
1063
0
    int isjson = 0; /* 1 => attribute value is neither scalar nor array of scalars */
1064
0
    int count = 0; /* no. of attribute values */
1065
1066
0
    ZTRACE(3,"typehint=%d typeid=%d values=|%s|",typehint,*typeidp,NCJtotext(values));
1067
1068
    /* Get assumed type */
1069
0
    if(typeidp) typeid = *typeidp;
1070
0
    if(typeid == NC_NAT && !isjson) {
1071
0
        if((stat = NCZ_inferattrtype(values,typehint, &typeid))) goto done;
1072
0
    }
1073
1074
    /* See if this is a simple vector (or scalar) of atomic types */
1075
0
    isjson = NCZ_iscomplexjson(values,typeid);
1076
1077
0
    if(isjson) {
1078
  /* Apply the JSON attribute convention and convert to JSON string */
1079
0
  typeid = NC_CHAR;
1080
0
  if((stat = json_convention_read(values,&jtext))) goto done;
1081
0
  values = jtext; jtext = NULL;
1082
0
  reclaimvalues = 1;
1083
0
    } 
1084
1085
0
    if((stat = NC4_inq_atomic_type(typeid, NULL, &typelen)))
1086
0
        goto done;
1087
1088
    /* Convert the JSON attribute values to the actual netcdf attribute bytes */
1089
0
    if((stat = zconvert(values,typeid,typelen,&count,buf))) goto done;
1090
1091
0
    if(typelenp) *typelenp = typelen;
1092
0
    if(typeidp) *typeidp = typeid; /* return possibly inferred type */
1093
0
    if(countp) *countp = count;
1094
0
    if(datap) *datap = ncbytesextract(buf);
1095
1096
0
done:
1097
0
    ncbytesfree(buf);
1098
0
    if(reclaimvalues) NCJreclaim(values); /* we created it */
1099
0
    return ZUNTRACEX(THROW(stat),"typelen=%d count=%u",(typelenp?*typelenp:0),(countp?*countp:-1));
1100
0
}
1101
1102
/**
1103
 * @internal Read file data from map to memory.
1104
 *
1105
 * @param file Pointer to file info struct.
1106
 *
1107
 * @return ::NC_NOERR No error.
1108
 * @author Dennis Heimbigner
1109
 */
1110
int
1111
ncz_read_file(NC_FILE_INFO_T* file)
1112
0
{
1113
0
    int stat = NC_NOERR;
1114
0
    NCjson* json = NULL;
1115
1116
0
    LOG((3, "%s: file: %s", __func__, file->controller->path));
1117
0
    ZTRACE(3,"file=%s",file->controller->path);
1118
    
1119
    /* _nczarr should already have been read in ncz_open_dataset */
1120
1121
    /* Now load the groups starting with root */
1122
0
    if((stat = define_grp(file,file->root_grp)))
1123
0
  goto done;
1124
1125
0
done:
1126
0
    NCJreclaim(json);
1127
0
    return ZUNTRACE(THROW(stat));
1128
0
}
1129
1130
/**
1131
 * @internal Read group data from map to memory
1132
 *
1133
 * @param file Pointer to file struct
1134
 * @param grp Pointer to grp struct
1135
 *
1136
 * @return ::NC_NOERR No error.
1137
 * @author Dennis Heimbigner
1138
 */
1139
static int
1140
define_grp(NC_FILE_INFO_T* file, NC_GRP_INFO_T* grp)
1141
0
{
1142
0
    int stat = NC_NOERR;
1143
0
    NCZ_FILE_INFO_T* zinfo = NULL;
1144
0
    NCZMAP* map = NULL;
1145
0
    char* fullpath = NULL;
1146
0
    char* key = NULL;
1147
0
    NCjson* json = NULL;
1148
0
    NCjson* jgroup = NULL;
1149
0
    NCjson* jdict = NULL;
1150
0
    NClist* dimdefs = nclistnew();
1151
0
    NClist* varnames = nclistnew();
1152
0
    NClist* subgrps = nclistnew();
1153
0
    int purezarr = 0;
1154
0
    int v1 = 0;
1155
1156
0
    LOG((3, "%s: dims: %s", __func__, key));
1157
0
    ZTRACE(3,"file=%s grp=%s",file->controller->path,grp->hdr.name);
1158
    
1159
0
    zinfo = file->format_file_info;
1160
0
    map = zinfo->map;
1161
1162
    /* Construct grp path */
1163
0
    if((stat = NCZ_grpkey(grp,&fullpath)))
1164
0
  goto done;
1165
1166
0
    if(zinfo->controls.flags & FLAG_PUREZARR) {
1167
0
  if((stat = parse_group_content_pure(zinfo,grp,varnames,subgrps)))
1168
0
      goto done;
1169
0
        purezarr = 1;
1170
0
    } else { /*!purezarr*/
1171
0
  if(zinfo->controls.flags & FLAG_NCZARR_V1) {
1172
      /* build NCZGROUP path */
1173
0
      if((stat = nczm_concat(fullpath,NCZGROUP,&key)))
1174
0
          goto done;
1175
      /* Read */
1176
0
      jdict = NULL;
1177
0
      stat=NCZ_downloadjson(map,key,&jdict);
1178
0
      v1 = 1;
1179
0
  } else {
1180
        /* build ZGROUP path */
1181
0
      if((stat = nczm_concat(fullpath,ZGROUP,&key)))
1182
0
          goto done;
1183
      /* Read */
1184
0
      switch (stat=NCZ_downloadjson(map,key,&jgroup)) {
1185
0
      case NC_NOERR: /* Extract the NCZ_V2_GROUP dict */
1186
0
          if((stat = NCJdictget(jgroup,NCZ_V2_GROUP,&jdict))) goto done;
1187
0
    if(!stat && jdict == NULL)
1188
0
        {if((stat = NCJdictget(jgroup,NCZ_V2_GROUP_UC,&jdict))) goto done;}
1189
0
          break;
1190
0
      case NC_EEMPTY: /* does not exist, use search */
1191
0
          if((stat = parse_group_content_pure(zinfo,grp,varnames,subgrps)))
1192
0
        goto done;
1193
0
          purezarr = 1;
1194
0
          break;
1195
0
      default: goto done;
1196
0
      }
1197
0
  }
1198
0
  nullfree(key); key = NULL;
1199
0
  if(jdict) {
1200
            /* Pull out lists about group content */
1201
0
      if((stat = parse_group_content(jdict,dimdefs,varnames,subgrps)))
1202
0
          goto done;
1203
0
  }
1204
0
    }
1205
1206
0
    if(!purezarr) {
1207
  /* Define dimensions */
1208
0
  if((stat = define_dims(file,grp,dimdefs))) goto done;
1209
0
    }
1210
1211
    /* Define vars taking xarray into account */
1212
0
    if((stat = define_vars(file,grp,varnames))) goto done;
1213
1214
    /* Define sub-groups */
1215
0
    if((stat = define_subgrps(file,grp,subgrps))) goto done;
1216
1217
0
done:
1218
0
    if(v1) NCJreclaim(jdict);
1219
0
    NCJreclaim(json);
1220
0
    NCJreclaim(jgroup);
1221
0
    nclistfreeall(dimdefs);
1222
0
    nclistfreeall(varnames);
1223
0
    nclistfreeall(subgrps);
1224
0
    nullfree(fullpath);
1225
0
    nullfree(key);
1226
0
    return ZUNTRACE(THROW(stat));
1227
0
}
1228
1229
1230
/**
1231
@internal Read attributes from a group or var and create a list
1232
of annotated NC_ATT_INFO_T* objects. This will process
1233
_NCProperties attribute specially.
1234
@param zfile - [in] the containing file (annotation)
1235
@param container - [in] the containing object
1236
@return NC_NOERR
1237
@author Dennis Heimbigner
1238
*/
1239
int
1240
ncz_read_atts(NC_FILE_INFO_T* file, NC_OBJ* container)
1241
0
{
1242
0
    int stat = NC_NOERR;
1243
0
    int i;
1244
0
    char* fullpath = NULL;
1245
0
    char* key = NULL;
1246
0
    NCZ_FILE_INFO_T* zinfo = NULL;
1247
0
    NC_VAR_INFO_T* var = NULL;
1248
0
    NCZ_VAR_INFO_T* zvar = NULL;
1249
0
    NC_GRP_INFO_T* grp = NULL;
1250
0
    NCZMAP* map = NULL;
1251
0
    NC_ATT_INFO_T* att = NULL;
1252
0
    NCindex* attlist = NULL;
1253
0
    NCjson* jattrs = NULL;
1254
0
    NClist* atypes = NULL;
1255
0
    nc_type typeid;
1256
0
    size_t len, typelen;
1257
0
    void* data = NULL;
1258
0
    NC_ATT_INFO_T* fillvalueatt = NULL;
1259
0
    nc_type typehint = NC_NAT;
1260
0
    int purezarr;
1261
1262
0
    ZTRACE(3,"file=%s container=%s",file->controller->path,container->name);
1263
1264
0
    zinfo = file->format_file_info;
1265
0
    map = zinfo->map;
1266
1267
0
    purezarr = (zinfo->controls.flags & FLAG_PUREZARR)?1:0;
1268
 
1269
0
    if(container->sort == NCGRP) { 
1270
0
  grp = ((NC_GRP_INFO_T*)container);
1271
0
  attlist =  grp->att;
1272
0
    } else {
1273
0
  var = ((NC_VAR_INFO_T*)container);
1274
0
        zvar = (NCZ_VAR_INFO_T*)(var->format_var_info);
1275
0
  attlist =  var->att;
1276
0
    }
1277
1278
0
    switch ((stat = load_jatts(map, container, (zinfo->controls.flags & FLAG_NCZARR_V1), &jattrs, &atypes))) {
1279
0
    case NC_NOERR: break;
1280
0
    case NC_EEMPTY:  /* container has no attributes */
1281
0
        stat = NC_NOERR;
1282
0
  break;
1283
0
    default: goto done; /* true error */
1284
0
    }
1285
1286
0
    if(jattrs != NULL) {
1287
  /* Iterate over the attributes to create the in-memory attributes */
1288
  /* Watch for special cases: _FillValue and  _ARRAY_DIMENSIONS (xarray), etc. */
1289
0
  for(i=0;i<NCJlength(jattrs);i+=2) {
1290
0
      NCjson* key = NCJith(jattrs,i);
1291
0
      NCjson* value = NCJith(jattrs,i+1);
1292
0
      const NC_reservedatt* ra = NULL;
1293
0
      int isfillvalue = 0;
1294
0
          int isdfaltmaxstrlen = 0;
1295
0
            int ismaxstrlen = 0;
1296
0
      const char* aname = NCJstring(key);
1297
      /* See if this is a notable attribute */
1298
0
      if(var != NULL && strcmp(aname,NC_ATT_FILLVALUE)==0) isfillvalue = 1;
1299
0
      if(grp != NULL && grp->parent == NULL && strcmp(aname,NC_NCZARR_DEFAULT_MAXSTRLEN_ATTR)==0)
1300
0
          isdfaltmaxstrlen = 1;
1301
0
      if(var != NULL && strcmp(aname,NC_NCZARR_MAXSTRLEN_ATTR)==0)
1302
0
          ismaxstrlen = 1;
1303
1304
      /* See if this is reserved attribute */
1305
0
      ra = NC_findreserved(aname);
1306
0
      if(ra != NULL) {
1307
    /* case 1: name = _NCProperties, grp=root, varid==NC_GLOBAL */
1308
0
    if(strcmp(aname,NCPROPS)==0 && grp != NULL && file->root_grp == grp) {
1309
        /* Setup provenance */
1310
0
        if(NCJsort(value) != NCJ_STRING)
1311
0
      {stat = (THROW(NC_ENCZARR)); goto done;} /*malformed*/
1312
0
        if((stat = NCZ_read_provenance(file,aname,NCJstring(value))))
1313
0
      goto done;
1314
0
    }
1315
    /* case 2: name = _ARRAY_DIMENSIONS, sort==NCVAR, flags & HIDDENATTRFLAG */
1316
0
    if(strcmp(aname,NC_XARRAY_DIMS)==0 && var != NULL && (ra->flags & HIDDENATTRFLAG)) {
1317
                /* store for later */
1318
0
        int i;
1319
0
        assert(NCJsort(value) == NCJ_ARRAY);
1320
0
        if((zvar->xarray = nclistnew())==NULL)
1321
0
            {stat = NC_ENOMEM; goto done;}
1322
0
        for(i=0;i<NCJlength(value);i++) {
1323
0
      const NCjson* k = NCJith(value,i);
1324
0
      assert(k != NULL && NCJsort(k) == NCJ_STRING);
1325
0
      nclistpush(zvar->xarray,strdup(NCJstring(k)));
1326
0
        }
1327
0
    }
1328
    /* case other: if attribute is hidden */
1329
0
    if(ra->flags & HIDDENATTRFLAG) continue; /* ignore it */
1330
0
      }
1331
0
      typehint = NC_NAT;
1332
0
      if(isfillvalue)
1333
0
          typehint = var->type_info->hdr.id ; /* if unknown use the var's type for _FillValue */
1334
      /* Create the attribute */
1335
      /* Collect the attribute's type and value  */
1336
0
      if((stat = computeattrinfo(aname,atypes,typehint,purezarr,value,
1337
0
           &typeid,&typelen,&len,&data)))
1338
0
    goto done;
1339
0
      if((stat = ncz_makeattr(container,attlist,aname,typeid,len,data,&att)))
1340
0
    goto done;
1341
      /* No longer need this copy of the data */
1342
0
        if((stat = nc_reclaim_data_all(file->controller->ext_ncid,att->nc_typeid,data,len))) goto done;           
1343
0
      data = NULL;
1344
0
      if(isfillvalue)
1345
0
          fillvalueatt = att;
1346
0
      if(ismaxstrlen && att->nc_typeid == NC_INT)
1347
0
          zvar->maxstrlen = ((int*)att->data)[0];
1348
0
      if(isdfaltmaxstrlen && att->nc_typeid == NC_INT)
1349
0
          zinfo->default_maxstrlen = ((int*)att->data)[0];
1350
0
  }
1351
0
    }
1352
    /* If we have not read a _FillValue, then go ahead and create it */
1353
0
    if(fillvalueatt == NULL && container->sort == NCVAR) {
1354
0
  if((stat = ncz_create_fillvalue((NC_VAR_INFO_T*)container)))
1355
0
      goto done;
1356
0
    }
1357
1358
    /* Remember that we have read the atts for this var or group. */
1359
0
    if(container->sort == NCVAR)
1360
0
  ((NC_VAR_INFO_T*)container)->atts_read = 1;
1361
0
    else
1362
0
  ((NC_GRP_INFO_T*)container)->atts_read = 1;
1363
1364
0
done:
1365
0
    if(data != NULL)
1366
0
        stat = nc_reclaim_data(file->controller->ext_ncid,att->nc_typeid,data,len);
1367
0
    NCJreclaim(jattrs);
1368
0
    nclistfreeall(atypes);
1369
0
    nullfree(fullpath);
1370
0
    nullfree(key);
1371
0
    return ZUNTRACE(THROW(stat));
1372
0
}
1373
1374
/**
1375
 * @internal Materialize dimensions into memory
1376
 *
1377
 * @param file Pointer to file info struct.
1378
 * @param grp Pointer to grp info struct.
1379
 * @param diminfo List of (name,length) pairs
1380
 *
1381
 * @return ::NC_NOERR No error.
1382
 * @author Dennis Heimbigner
1383
 */
1384
static int
1385
define_dims(NC_FILE_INFO_T* file, NC_GRP_INFO_T* grp, NClist* diminfo)
1386
0
{
1387
0
    int i,stat = NC_NOERR;
1388
1389
0
    ZTRACE(3,"file=%s grp=%s |diminfo|=%u",file->controller->path,grp->hdr.name,nclistlength(diminfo));
1390
1391
    /* Reify each dim in turn */
1392
0
    for(i = 0; i < nclistlength(diminfo); i+=2) {
1393
0
  NC_DIM_INFO_T* dim = NULL;
1394
0
  size64_t len = 0;
1395
0
  const char* name = nclistget(diminfo,i);
1396
0
  const char* value = nclistget(diminfo,i+1);
1397
1398
  /* Create the NC_DIM_INFO_T object */
1399
0
  sscanf(value,"%lld",&len); /* Get length */
1400
0
  if(len <= 0)
1401
0
      {stat = NC_EDIMSIZE; goto done;}
1402
0
  if((stat = nc4_dim_list_add(grp, name, (size_t)len, -1, &dim)))
1403
0
      goto done;
1404
0
  if((dim->format_dim_info = calloc(1,sizeof(NCZ_DIM_INFO_T))) == NULL)
1405
0
      {stat = NC_ENOMEM; goto done;}
1406
0
  ((NCZ_DIM_INFO_T*)dim->format_dim_info)->common.file = file;
1407
0
    }
1408
1409
0
done:
1410
0
    return ZUNTRACE(THROW(stat));
1411
0
}
1412
1413
/**
1414
 * @internal Materialize vars into memory;
1415
 * Take xarray and purezarr into account.
1416
 *
1417
 * @param file Pointer to file info struct.
1418
 * @param grp Pointer to grp info struct.
1419
 * @param varnames List of names of variables in this group
1420
 *
1421
 * @return ::NC_NOERR No error.
1422
 * @author Dennis Heimbigner
1423
 */
1424
static int
1425
define_vars(NC_FILE_INFO_T* file, NC_GRP_INFO_T* grp, NClist* varnames)
1426
0
{
1427
0
    int stat = NC_NOERR;
1428
0
    int i,j;
1429
0
    char* varpath = NULL;
1430
0
    char* key = NULL;
1431
0
    NCZ_FILE_INFO_T* zinfo = NULL;
1432
0
    NCZ_VAR_INFO_T* zvar = NULL;
1433
0
    NCZMAP* map = NULL;
1434
0
    NCjson* jvar = NULL;
1435
0
    NCjson* jncvar = NULL;
1436
0
    NCjson* jdimrefs = NULL;
1437
0
    NCjson* jvalue = NULL;
1438
0
    int purezarr = 0;
1439
0
    int xarray = 0;
1440
0
    int formatv1 = 0;
1441
0
    nc_type vtype;
1442
0
    int vtypelen;
1443
0
    size64_t* shapes = NULL;
1444
0
    int rank = 0;
1445
0
    int zarr_rank = 1; /* Need to watch out for scalars */
1446
0
    NClist* dimnames = nclistnew();
1447
0
#ifdef ENABLE_NCZARR_FILTERS
1448
0
    NCjson* jfilter = NULL;
1449
0
    int chainindex;
1450
0
#endif
1451
1452
0
    ZTRACE(3,"file=%s grp=%s |varnames|=%u",file->controller->path,grp->hdr.name,nclistlength(varnames));
1453
1454
0
    zinfo = file->format_file_info;
1455
0
    map = zinfo->map;
1456
1457
0
    if(zinfo->controls.flags & FLAG_PUREZARR) purezarr = 1;
1458
0
    if(zinfo->controls.flags & FLAG_NCZARR_V1) formatv1 = 1;
1459
0
    if(zinfo->controls.flags & FLAG_XARRAYDIMS) {xarray = 1;}
1460
1461
    /* Load each var in turn */
1462
0
    for(i = 0; i < nclistlength(varnames); i++) {
1463
0
  NC_VAR_INFO_T* var;
1464
0
  const char* varname = nclistget(varnames,i);
1465
0
  if((stat = nc4_var_list_add2(grp, varname, &var)))
1466
0
      goto done;
1467
1468
  /* And its annotation */
1469
0
  if((zvar = calloc(1,sizeof(NCZ_VAR_INFO_T)))==NULL)
1470
0
      {stat = NC_ENOMEM; goto done;}
1471
0
  var->format_var_info = zvar;
1472
0
  zvar->common.file = file;
1473
1474
        /* pretend it was created */
1475
0
  var->created = 1;
1476
1477
  /* Indicate we do not have quantizer yet */
1478
0
  var->quantize_mode = -1;
1479
1480
  /* Set filter list */
1481
0
  assert(var->filters == NULL);
1482
0
  var->filters = (void*)nclistnew();
1483
1484
  /* Construct var path */
1485
0
  if((stat = NCZ_varkey(var,&varpath)))
1486
0
      goto done;
1487
1488
  /* Construct the path to the zarray object */
1489
0
  if((stat = nczm_concat(varpath,ZARRAY,&key)))
1490
0
      goto done;
1491
  /* Download the zarray object */
1492
0
  if((stat=NCZ_readdict(map,key,&jvar)))
1493
0
      goto done;
1494
0
  nullfree(key); key = NULL;
1495
0
  assert(NCJsort(jvar) == NCJ_DICT);
1496
1497
        /* Extract the .zarray info from jvar */
1498
1499
  /* Verify the format */
1500
0
  {
1501
0
      int version;
1502
0
      if((stat = NCJdictget(jvar,"zarr_format",&jvalue))) goto done;
1503
0
      sscanf(NCJstring(jvalue),"%d",&version);
1504
0
      if(version != zinfo->zarr.zarr_version)
1505
0
    {stat = (THROW(NC_ENCZARR)); goto done;}
1506
0
  }
1507
  /* Set the type and endianness of the variable */
1508
0
  {
1509
0
      int endianness;
1510
0
      if((stat = NCJdictget(jvar,"dtype",&jvalue))) goto done;
1511
      /* Convert dtype to nc_type + endianness */
1512
0
      if((stat = ncz_dtype2nctype(NCJstring(jvalue),NC_NAT,purezarr,&vtype,&endianness,&vtypelen)))
1513
0
    goto done;
1514
0
      if(vtype > NC_NAT && vtype <= NC_MAX_ATOMIC_TYPE) {
1515
    /* Locate the NC_TYPE_INFO_T object */
1516
0
    if((stat = ncz_gettype(file,grp,vtype,&var->type_info)))
1517
0
        goto done;
1518
0
      } else {stat = NC_EBADTYPE; goto done;}
1519
#if 0 /* leave native in place */
1520
      if(endianness == NC_ENDIAN_NATIVE)
1521
    endianness = zinfo->native_endianness;
1522
      if(endianness == NC_ENDIAN_NATIVE)
1523
          endianness = (NCZ_isLittleEndian()?NC_ENDIAN_LITTLE:NC_ENDIAN_BIG);
1524
      if(endianness == NC_ENDIAN_LITTLE || endianness == NC_ENDIAN_BIG) {
1525
    var->endianness = endianness;
1526
      } else {stat = NC_EBADTYPE; goto done;}
1527
#else
1528
0
      var->endianness = endianness;
1529
0
#endif
1530
0
      var->type_info->endianness = var->endianness; /* Propagate */
1531
0
      if(vtype == NC_STRING) {
1532
0
    zvar->maxstrlen = vtypelen;
1533
0
    vtypelen = sizeof(char*); /* in-memory len */
1534
0
    if(zvar->maxstrlen <= 0) zvar->maxstrlen = NCZ_get_maxstrlen((NC_OBJ*)var);
1535
0
      }
1536
0
  }
1537
1538
0
  if(!purezarr) {
1539
        /* Extract the _NCZARR_ARRAY values */
1540
      /* Do this first so we know about storage esp. scalar */
1541
0
      if(formatv1) {
1542
    /* Construct the path to the zarray object */
1543
0
    if((stat = nczm_concat(varpath,NCZARRAY,&key)))
1544
0
        goto done;
1545
    /* Download the nczarray object */
1546
0
    if((stat=NCZ_readdict(map,key,&jncvar)))
1547
0
        goto done;
1548
0
    nullfree(key); key = NULL;
1549
0
      } else {/* format v2 */
1550
     /* Extract the NCZ_V2_ARRAY dict */
1551
0
          if((stat = NCJdictget(jvar,NCZ_V2_ARRAY,&jncvar))) goto done;
1552
0
    if(!stat && jncvar == NULL)
1553
0
              {if((stat = NCJdictget(jvar,NCZ_V2_ARRAY_UC,&jncvar))) goto done;}
1554
0
      }
1555
0
      if(jncvar == NULL) {stat = NC_ENCZARR; goto done;}
1556
0
        assert((NCJsort(jncvar) == NCJ_DICT));
1557
      /* Extract storage flag */
1558
0
      if((stat = NCJdictget(jncvar,"storage",&jvalue)))
1559
0
    goto done;
1560
0
      if(jvalue != NULL) {
1561
0
    if(strcmp(NCJstring(jvalue),"chunked") == 0) {
1562
0
        var->storage = NC_CHUNKED;
1563
0
    } else if(strcmp(NCJstring(jvalue),"compact") == 0) {
1564
0
        var->storage = NC_COMPACT;
1565
0
    } else if(strcmp(NCJstring(jvalue),"scalar") == 0) {
1566
0
        var->storage = NC_CONTIGUOUS;
1567
0
        zvar->scalar = 1;
1568
0
    } else { /*storage = NC_CONTIGUOUS;*/
1569
0
        var->storage = NC_CONTIGUOUS;
1570
0
    }
1571
0
      }
1572
      /* Extract dimrefs list  */
1573
0
      switch ((stat = NCJdictget(jncvar,"dimrefs",&jdimrefs))) {
1574
0
      case NC_NOERR: /* Extract the dimref names */
1575
0
    assert((NCJsort(jdimrefs) == NCJ_ARRAY));
1576
0
    if(zvar->scalar) {
1577
0
          assert(NCJlength(jdimrefs) == 0);      
1578
0
    } else {
1579
0
        rank = NCJlength(jdimrefs);
1580
0
        for(j=0;j<rank;j++) {
1581
0
            const NCjson* dimpath = NCJith(jdimrefs,j);
1582
0
            assert(NCJsort(dimpath) == NCJ_STRING);
1583
0
            nclistpush(dimnames,strdup(NCJstring(dimpath)));
1584
0
        }
1585
0
    }
1586
0
    jdimrefs = NULL; /* avoid double free */
1587
0
    break;
1588
0
      case NC_EEMPTY: /* will simulate it from the shape of the variable */
1589
0
    stat = NC_NOERR;
1590
0
    break;
1591
0
      default: goto done;
1592
0
      }
1593
0
      jdimrefs = NULL;
1594
0
  }
1595
1596
  /* shape */
1597
0
  {
1598
0
      if((stat = NCJdictget(jvar,"shape",&jvalue))) goto done;
1599
0
      if(NCJsort(jvalue) != NCJ_ARRAY) {stat = (THROW(NC_ENCZARR)); goto done;}
1600
0
            if(zvar->scalar) {
1601
0
          rank = 0;
1602
0
    zarr_rank = 1; /* Zarr does not support scalars */
1603
0
      } else 
1604
0
    rank = (zarr_rank = NCJlength(jvalue));
1605
      /* Save the rank of the variable */
1606
0
      if((stat = nc4_var_set_ndims(var, rank))) goto done;
1607
      /* extract the shapes */
1608
0
      if((shapes = (size64_t*)malloc(sizeof(size64_t)*zarr_rank)) == NULL)
1609
0
          {stat = (THROW(NC_ENOMEM)); goto done;}
1610
0
      if((stat = decodeints(jvalue, shapes))) goto done;
1611
0
  }
1612
1613
  /* Capture dimension_separator (must precede chunk cache creation) */
1614
0
  {
1615
0
      NCglobalstate* ngs = NC_getglobalstate();
1616
0
      assert(ngs != NULL);
1617
0
      zvar->dimension_separator = 0;
1618
0
      if((stat = NCJdictget(jvar,"dimension_separator",&jvalue))) goto done;
1619
0
      if(jvalue != NULL) {
1620
          /* Verify its value */
1621
0
    if(NCJsort(jvalue) == NCJ_STRING && NCJstring(jvalue) != NULL && strlen(NCJstring(jvalue)) == 1)
1622
0
       zvar->dimension_separator = NCJstring(jvalue)[0];
1623
0
      }
1624
      /* If value is invalid, then use global default */
1625
0
      if(!islegaldimsep(zvar->dimension_separator))
1626
0
          zvar->dimension_separator = ngs->zarr.dimension_separator; /* use global value */
1627
0
      assert(islegaldimsep(zvar->dimension_separator)); /* we are hosed */
1628
0
  }
1629
1630
  /* fill_value; must precede calls to adjust cache */
1631
0
  {
1632
0
      if((stat = NCJdictget(jvar,"fill_value",&jvalue))) goto done;
1633
0
      if(jvalue == NULL || NCJsort(jvalue) == NCJ_NULL)
1634
0
    var->no_fill = 1;
1635
0
      else {
1636
0
    size_t fvlen;
1637
0
    nc_type atypeid = vtype;
1638
0
    var->no_fill = 0;
1639
0
    if((stat = computeattrdata(var->type_info->hdr.id, &atypeid, jvalue, NULL, &fvlen, &var->fill_value)))
1640
0
        goto done;
1641
0
    assert(atypeid == vtype);
1642
    /* Note that we do not create the _FillValue
1643
       attribute here to avoid having to read all
1644
       the attributes and thus foiling lazy read.*/
1645
0
      }
1646
0
  }
1647
1648
  /* chunks */
1649
0
  {
1650
0
      size64_t chunks[NC_MAX_VAR_DIMS];
1651
0
      if((stat = NCJdictget(jvar,"chunks",&jvalue))) goto done;
1652
0
      if(jvalue != NULL && NCJsort(jvalue) != NCJ_ARRAY)
1653
0
    {stat = (THROW(NC_ENCZARR)); goto done;}
1654
      /* Verify the rank */
1655
0
      assert (zarr_rank == NCJlength(jvalue));
1656
0
      if(zvar->scalar) {
1657
0
    if(var->ndims != 0)
1658
0
        {stat = (THROW(NC_ENCZARR)); goto done;}
1659
0
    zvar->chunkproduct = 1;
1660
0
    zvar->chunksize = zvar->chunkproduct * var->type_info->size;
1661
    /* Create the cache */
1662
0
    if((stat = NCZ_create_chunk_cache(var,var->type_info->size*zvar->chunkproduct,zvar->dimension_separator,&zvar->cache)))
1663
0
        goto done;
1664
0
      } else {/* !zvar->scalar */
1665
0
    if(zarr_rank == 0) {stat = NC_ENCZARR; goto done;}
1666
0
    var->storage = NC_CHUNKED;
1667
0
    if(var->ndims != rank)
1668
0
        {stat = (THROW(NC_ENCZARR)); goto done;}
1669
0
    if((var->chunksizes = malloc(sizeof(size_t)*zarr_rank)) == NULL)
1670
0
        {stat = NC_ENOMEM; goto done;}
1671
0
    if((stat = decodeints(jvalue, chunks))) goto done;
1672
    /* validate the chunk sizes */
1673
0
    zvar->chunkproduct = 1;
1674
0
    for(j=0;j<rank;j++) {
1675
0
        if(chunks[j] == 0 || chunks[j] > shapes[j])
1676
0
      {stat = (THROW(NC_ENCZARR)); goto done;}
1677
0
        var->chunksizes[j] = (size_t)chunks[j];
1678
0
        zvar->chunkproduct *= chunks[j];
1679
0
    }
1680
0
    zvar->chunksize = zvar->chunkproduct * var->type_info->size;
1681
    /* Create the cache */
1682
0
    if((stat = NCZ_create_chunk_cache(var,var->type_info->size*zvar->chunkproduct,zvar->dimension_separator,&zvar->cache)))
1683
0
        goto done;
1684
0
      }
1685
0
          if((stat = NCZ_adjust_var_cache(var))) goto done;
1686
0
  }
1687
  /* Capture row vs column major; currently, column major not used*/
1688
0
  {
1689
0
      if((stat = NCJdictget(jvar,"order",&jvalue))) goto done;
1690
0
      if(strcmp(NCJstring(jvalue),"C")==1)
1691
0
    ((NCZ_VAR_INFO_T*)var->format_var_info)->order = 1;
1692
0
      else ((NCZ_VAR_INFO_T*)var->format_var_info)->order = 0;
1693
0
  }
1694
        /* filters key */
1695
        /* From V2 Spec: A list of JSON objects providing codec configurations,
1696
           or null if no filters are to be applied. Each codec configuration
1697
           object MUST contain a "id" key identifying the codec to be used. */
1698
  /* Do filters key before compressor key so final filter chain is in correct order */
1699
0
  {
1700
0
      if(var->filters == NULL) var->filters = (void*)nclistnew();
1701
0
        if(zvar->incompletefilters == NULL) zvar->incompletefilters = (void*)nclistnew();
1702
0
#ifdef ENABLE_NCZARR_FILTERS
1703
0
      { int k;
1704
0
      chainindex = 0; /* track location of filter in the chain */
1705
0
      if((stat = NCZ_filter_initialize())) goto done;
1706
0
      if((stat = NCJdictget(jvar,"filters",&jvalue))) goto done;
1707
0
      if(jvalue != NULL && NCJsort(jvalue) != NCJ_NULL) {
1708
0
          if(NCJsort(jvalue) != NCJ_ARRAY) {stat = NC_EFILTER; goto done;}
1709
0
    for(k=0;;k++) {
1710
0
        jfilter = NULL;
1711
0
        jfilter = NCJith(jvalue,k);
1712
0
        if(jfilter == NULL) break; /* done */
1713
0
        if(NCJsort(jfilter) != NCJ_DICT) {stat = NC_EFILTER; goto done;}
1714
0
        if((stat = NCZ_filter_build(file,var,jfilter,chainindex++))) goto done;
1715
0
    }
1716
0
      }
1717
0
      }
1718
0
#endif
1719
0
  }
1720
1721
        /* compressor key */
1722
        /* From V2 Spec: A JSON object identifying the primary compression codec and providing
1723
           configuration parameters, or ``null`` if no compressor is to be used. */
1724
0
  {
1725
0
      if(var->filters == NULL) var->filters = (void*)nclistnew();
1726
0
#ifdef ENABLE_NCZARR_FILTERS
1727
0
      if((stat = NCZ_filter_initialize())) goto done;
1728
0
      if((stat = NCJdictget(jvar,"compressor",&jfilter))) goto done;
1729
0
      if(jfilter != NULL && NCJsort(jfilter) != NCJ_NULL) {
1730
0
          if(NCJsort(jfilter) != NCJ_DICT) {stat = NC_EFILTER; goto done;}
1731
0
    if((stat = NCZ_filter_build(file,var,jfilter,chainindex++))) goto done;
1732
0
      }
1733
0
#endif
1734
0
  }
1735
1736
0
  if((stat = computedimrefs(file, var, purezarr, xarray, rank, dimnames, shapes, var->dim)))
1737
0
      goto done;
1738
1739
0
  if(!zvar->scalar) {
1740
      /* Extract the dimids */
1741
0
      for(j=0;j<rank;j++)
1742
0
          var->dimids[j] = var->dim[j]->hdr.id;
1743
0
  }
1744
1745
0
#ifdef ENABLE_NCZARR_FILTERS
1746
  /* At this point, we can finalize the filters */
1747
0
        if((stat = NCZ_filter_setup(var))) goto done;
1748
0
#endif
1749
  /* Clean up from last cycle */
1750
0
  nclistfreeall(dimnames); dimnames = nclistnew();
1751
0
        nullfree(varpath); varpath = NULL;
1752
0
        nullfree(shapes); shapes = NULL;
1753
0
        if(formatv1) {NCJreclaim(jncvar); jncvar = NULL;}
1754
0
        NCJreclaim(jvar); jvar = NULL;
1755
0
    }
1756
1757
0
done:
1758
0
    nullfree(shapes);
1759
0
    nullfree(varpath);
1760
0
    nullfree(key);
1761
0
    nclistfreeall(dimnames);
1762
0
    NCJreclaim(jvar);
1763
0
    if(formatv1) NCJreclaim(jncvar);
1764
0
    return ZUNTRACE(THROW(stat));
1765
0
}
1766
1767
/**
1768
 * @internal Materialize subgroups into memory
1769
 *
1770
 * @param file Pointer to file info struct.
1771
 * @param grp Pointer to grp info struct.
1772
 * @param subgrpnames List of names of subgroups in this group
1773
 *
1774
 * @return ::NC_NOERR No error.
1775
 * @author Dennis Heimbigner
1776
 */
1777
static int
1778
define_subgrps(NC_FILE_INFO_T* file, NC_GRP_INFO_T* grp, NClist* subgrpnames)
1779
0
{
1780
0
    int i,stat = NC_NOERR;
1781
1782
0
    ZTRACE(3,"file=%s grp=%s |subgrpnames|=%u",file->controller->path,grp->hdr.name,nclistlength(subgrpnames));
1783
1784
    /* Load each subgroup name in turn */
1785
0
    for(i = 0; i < nclistlength(subgrpnames); i++) {
1786
0
  NC_GRP_INFO_T* g = NULL;
1787
0
  const char* gname = nclistget(subgrpnames,i);
1788
0
  char norm_name[NC_MAX_NAME];
1789
  /* Check and normalize the name. */
1790
0
  if((stat = nc4_check_name(gname, norm_name)))
1791
0
      goto done;
1792
0
  if((stat = nc4_grp_list_add(file, grp, norm_name, &g)))
1793
0
      goto done;
1794
0
  if(!(g->format_grp_info = calloc(1, sizeof(NCZ_GRP_INFO_T))))
1795
0
      {stat = NC_ENOMEM; goto done;}
1796
0
  ((NCZ_GRP_INFO_T*)g->format_grp_info)->common.file = file;
1797
0
    }
1798
1799
    /* Recurse to fill in subgroups */
1800
0
    for(i=0;i<ncindexsize(grp->children);i++) {
1801
0
  NC_GRP_INFO_T* g = (NC_GRP_INFO_T*)ncindexith(grp->children,i);
1802
0
  if((stat = define_grp(file,g)))
1803
0
      goto done;
1804
0
    }
1805
1806
0
done:
1807
0
    return ZUNTRACE(THROW(stat));
1808
0
}
1809
1810
int
1811
ncz_read_superblock(NC_FILE_INFO_T* file, char** nczarrvp, char** zarrfp)
1812
0
{
1813
0
    int stat = NC_NOERR;
1814
0
    NCjson* jnczgroup = NULL;
1815
0
    NCjson* jzgroup = NULL;
1816
0
    NCjson* jsuper = NULL;
1817
0
    NCjson* jtmp = NULL;
1818
0
    char* nczarr_version = NULL;
1819
0
    char* zarr_format = NULL;
1820
0
    NCZ_FILE_INFO_T* zinfo = (NCZ_FILE_INFO_T*)file->format_file_info;
1821
1822
0
    ZTRACE(3,"file=%s",file->controller->path);
1823
1824
    /* See if the V1 META-Root is being used */
1825
0
    switch(stat = NCZ_downloadjson(zinfo->map, NCZMETAROOT, &jnczgroup)) {
1826
0
    case NC_EEMPTY: /* not there */
1827
0
  stat = NC_NOERR;
1828
0
  break;
1829
0
    case NC_NOERR:
1830
0
  if((stat = NCJdictget(jnczgroup,"nczarr_version",&jtmp))) goto done;
1831
0
  nczarr_version = strdup(NCJstring(jtmp));
1832
0
  break;
1833
0
    default: goto done;
1834
0
    }
1835
    /* Also gett Zarr Root Group */
1836
0
    switch(stat = NCZ_downloadjson(zinfo->map, ZMETAROOT, &jzgroup)) {
1837
0
    case NC_NOERR:
1838
0
  break;
1839
0
    case NC_EEMPTY: /* not there */
1840
0
  stat = NC_NOERR;
1841
0
  assert(jzgroup == NULL);
1842
0
  break;
1843
0
    default: goto done;
1844
0
    }
1845
0
    if(jzgroup != NULL) {
1846
        /* See if this NCZarr V2 */
1847
0
        if((stat = NCJdictget(jzgroup,NCZ_V2_SUPERBLOCK,&jsuper))) goto done;
1848
0
  if(!stat && jsuper == NULL)
1849
0
            {if((stat = NCJdictget(jzgroup,NCZ_V2_SUPERBLOCK_UC,&jsuper))) goto done;}
1850
0
  if(jsuper != NULL) {
1851
      /* Extract the equivalent attribute */
1852
0
      if(jsuper->sort != NCJ_DICT)
1853
0
          {stat = NC_ENCZARR; goto done;}
1854
0
      if((stat = NCJdictget(jsuper,"version",&jtmp))) goto done;
1855
0
      nczarr_version = nulldup(NCJstring(jtmp));
1856
0
  }
1857
        /* In any case, extract the zarr format */
1858
0
        if((stat = NCJdictget(jzgroup,"zarr_format",&jtmp))) goto done;
1859
0
        zarr_format = nulldup(NCJstring(jtmp));
1860
0
    }
1861
    /* Set the controls */
1862
0
    if(jnczgroup == NULL && jsuper == NULL) {
1863
0
  zinfo->controls.flags |= FLAG_PUREZARR;
1864
0
    } else if(jnczgroup != NULL) {
1865
0
  zinfo->controls.flags |= FLAG_NCZARR_V1;
1866
  /* Also means file is read only */
1867
0
  file->no_write = 1;
1868
0
    } else if(jsuper != NULL) {
1869
       /* ! FLAG_NCZARR_V1 && ! FLAG_PUREZARR */
1870
0
    }
1871
0
    if(nczarrvp) {*nczarrvp = nczarr_version; nczarr_version = NULL;}
1872
0
    if(zarrfp) {*zarrfp = zarr_format; zarr_format = NULL;}
1873
0
done:
1874
0
    nullfree(zarr_format);
1875
0
    nullfree(nczarr_version);
1876
0
    NCJreclaim(jzgroup);
1877
0
    NCJreclaim(jnczgroup);
1878
0
    return ZUNTRACE(THROW(stat));
1879
0
}
1880
1881
/**************************************************/
1882
/* Utilities */
1883
1884
static int
1885
parse_group_content(NCjson* jcontent, NClist* dimdefs, NClist* varnames, NClist* subgrps)
1886
0
{
1887
0
    int i,stat = NC_NOERR;
1888
0
    NCjson* jvalue = NULL;
1889
1890
0
    ZTRACE(3,"jcontent=|%s| |dimdefs|=%u |varnames|=%u |subgrps|=%u",NCJtotext(jcontent),(unsigned)nclistlength(dimdefs),(unsigned)nclistlength(varnames),(unsigned)nclistlength(subgrps));
1891
1892
0
    if((stat=NCJdictget(jcontent,"dims",&jvalue))) goto done;
1893
0
    if(jvalue != NULL) {
1894
0
  if(NCJsort(jvalue) != NCJ_DICT) {stat = (THROW(NC_ENCZARR)); goto done;}
1895
  /* Extract the dimensions defined in this group */
1896
0
  for(i=0;i<NCJlength(jvalue);i+=2) {
1897
0
      NCjson* jname = NCJith(jvalue,i);
1898
0
      NCjson* jlen = NCJith(jvalue,i+1);
1899
0
      char norm_name[NC_MAX_NAME + 1];
1900
0
      size64_t len;
1901
      /* Verify name legality */
1902
0
      if((stat = nc4_check_name(NCJstring(jname), norm_name)))
1903
0
    {stat = NC_EBADNAME; goto done;}
1904
      /* check the length */
1905
0
      sscanf(NCJstring(jlen),"%lld",&len);
1906
0
      if(len < 0)
1907
0
    {stat = NC_EDIMSIZE; goto done;}
1908
0
      nclistpush(dimdefs,strdup(norm_name));
1909
0
      nclistpush(dimdefs,strdup(NCJstring(jlen)));
1910
0
  }
1911
0
    }
1912
1913
0
    if((stat=NCJdictget(jcontent,"vars",&jvalue))) goto done;
1914
0
    if(jvalue != NULL) {
1915
  /* Extract the variable names in this group */
1916
0
  for(i=0;i<NCJlength(jvalue);i++) {
1917
0
      NCjson* jname = NCJith(jvalue,i);
1918
0
      char norm_name[NC_MAX_NAME + 1];
1919
      /* Verify name legality */
1920
0
      if((stat = nc4_check_name(NCJstring(jname), norm_name)))
1921
0
    {stat = NC_EBADNAME; goto done;}
1922
0
      nclistpush(varnames,strdup(norm_name));
1923
0
  }
1924
0
    }
1925
1926
0
    if((stat=NCJdictget(jcontent,"groups",&jvalue))) goto done;
1927
0
    if(jvalue != NULL) {
1928
  /* Extract the subgroup names in this group */
1929
0
  for(i=0;i<NCJlength(jvalue);i++) {
1930
0
      NCjson* jname = NCJith(jvalue,i);
1931
0
      char norm_name[NC_MAX_NAME + 1];
1932
      /* Verify name legality */
1933
0
      if((stat = nc4_check_name(NCJstring(jname), norm_name)))
1934
0
    {stat = NC_EBADNAME; goto done;}
1935
0
      nclistpush(subgrps,strdup(norm_name));
1936
0
  }
1937
0
    }
1938
1939
0
done:
1940
0
    return ZUNTRACE(THROW(stat));
1941
0
}
1942
1943
static int
1944
parse_group_content_pure(NCZ_FILE_INFO_T*  zinfo, NC_GRP_INFO_T* grp, NClist* varnames, NClist* subgrps)
1945
0
{
1946
0
    int stat = NC_NOERR;
1947
1948
0
    ZTRACE(3,"zinfo=%s grp=%s |varnames|=%u |subgrps|=%u",zinfo->common.file->controller->path,grp->hdr.name,(unsigned)nclistlength(varnames),(unsigned)nclistlength(subgrps));
1949
1950
0
    nclistclear(varnames);
1951
0
    if((stat = searchvars(zinfo,grp,varnames))) goto done;
1952
0
    nclistclear(subgrps);
1953
0
    if((stat = searchsubgrps(zinfo,grp,subgrps))) goto done;
1954
1955
0
done:
1956
0
    return ZUNTRACE(THROW(stat));
1957
0
}
1958
1959
1960
#if 0
1961
static int
1962
parse_var_dims_pure(NCZ_FILE_INFO_T*  zinfo, NC_GRP_INFO_T* grp, NC_VAR_INFO_T* var, size64_t* shapes)
1963
{
1964
    int stat = NC_NOERR;
1965
    char* varkey = NULL;
1966
    char* zakey = NULL;
1967
    NCjson* jvar = NULL;
1968
    NCjson* jvalue = NULL;
1969
1970
    /* Construct var path */
1971
    if((stat = NCZ_varkey(var,&varkey))) goto done;
1972
    /* Construct .zarray path */
1973
    if((stat = nczm_concat(varkey,ZARRAY,&zakey))) goto done;
1974
    /* Download the zarray object */
1975
    if((stat=NCZ_readdict(zinfo->map,zakey,&jvar)))
1976
  goto done;
1977
    assert((NCJsort(jvar) == NCJ_DICT));
1978
    nullfree(varkey); varkey = NULL;
1979
    nullfree(zakey); zakey = NULL;
1980
    /* Extract the shape */
1981
    if((stat=NCJdictget(jvar,"shape",&jvalue))) goto done;
1982
    if((stat = decodeints(jvalue, shapes))) goto done;
1983
1984
done:
1985
    NCJreclaim(jvar);
1986
    NCJreclaim(jvalue);
1987
    nullfree(varkey); varkey = NULL;
1988
    nullfree(zakey); zakey = NULL;
1989
    return ZUNTRACE(THROW(stat));
1990
}
1991
#endif
1992
1993
static int
1994
searchvars(NCZ_FILE_INFO_T* zfile, NC_GRP_INFO_T* grp, NClist* varnames)
1995
0
{
1996
0
    int i,stat = NC_NOERR;
1997
0
    char* grpkey = NULL;
1998
0
    char* varkey = NULL;
1999
0
    char* zarray = NULL;
2000
0
    NClist* matches = nclistnew();
2001
2002
    /* Compute the key for the grp */
2003
0
    if((stat = NCZ_grpkey(grp,&grpkey))) goto done;
2004
    /* Get the map and search group */
2005
0
    if((stat = nczmap_search(zfile->map,grpkey,matches))) goto done;
2006
0
    for(i=0;i<nclistlength(matches);i++) {
2007
0
  const char* name = nclistget(matches,i);
2008
0
  if(name[0] == NCZM_DOT) continue; /* zarr/nczarr specific */
2009
  /* See if name/.zarray exists */
2010
0
  if((stat = nczm_concat(grpkey,name,&varkey))) goto done;
2011
0
  if((stat = nczm_concat(varkey,ZARRAY,&zarray))) goto done;
2012
0
  if((stat = nczmap_exists(zfile->map,zarray)) == NC_NOERR)
2013
0
      nclistpush(varnames,strdup(name));
2014
0
  stat = NC_NOERR;
2015
0
  nullfree(varkey); varkey = NULL;
2016
0
  nullfree(zarray); zarray = NULL;
2017
0
    }
2018
2019
0
done:
2020
0
    nullfree(grpkey);
2021
0
    nullfree(varkey);
2022
0
    nullfree(zarray);
2023
0
    nclistfreeall(matches);
2024
0
    return stat;
2025
0
}
2026
2027
static int
2028
searchsubgrps(NCZ_FILE_INFO_T* zfile, NC_GRP_INFO_T* grp, NClist* subgrpnames)
2029
0
{
2030
0
    int i,stat = NC_NOERR;
2031
0
    char* grpkey = NULL;
2032
0
    char* subkey = NULL;
2033
0
    char* zgroup = NULL;
2034
0
    NClist* matches = nclistnew();
2035
2036
    /* Compute the key for the grp */
2037
0
    if((stat = NCZ_grpkey(grp,&grpkey))) goto done;
2038
    /* Get the map and search group */
2039
0
    if((stat = nczmap_search(zfile->map,grpkey,matches))) goto done;
2040
0
    for(i=0;i<nclistlength(matches);i++) {
2041
0
  const char* name = nclistget(matches,i);
2042
0
  if(name[0] == NCZM_DOT) continue; /* zarr/nczarr specific */
2043
  /* See if name/.zgroup exists */
2044
0
  if((stat = nczm_concat(grpkey,name,&subkey))) goto done;
2045
0
  if((stat = nczm_concat(subkey,ZGROUP,&zgroup))) goto done;
2046
0
  if((stat = nczmap_exists(zfile->map,zgroup)) == NC_NOERR)
2047
0
      nclistpush(subgrpnames,strdup(name));
2048
0
  stat = NC_NOERR;
2049
0
  nullfree(subkey); subkey = NULL;
2050
0
  nullfree(zgroup); zgroup = NULL;
2051
0
    }
2052
2053
0
done:
2054
0
    nullfree(grpkey);
2055
0
    nullfree(subkey);
2056
0
    nullfree(zgroup);
2057
0
    nclistfreeall(matches);
2058
0
    return stat;
2059
0
}
2060
2061
/* Convert a list of integer strings to 64 bit dimension sizes (shapes) */
2062
static int
2063
decodeints(NCjson* jshape, size64_t* shapes)
2064
0
{
2065
0
    int i, stat = NC_NOERR;
2066
2067
0
    for(i=0;i<NCJlength(jshape);i++) {
2068
0
  struct ZCVT zcvt;
2069
0
  nc_type typeid = NC_NAT;
2070
0
  NCjson* jv = NCJith(jshape,i);
2071
0
  if((stat = NCZ_json2cvt(jv,&zcvt,&typeid))) goto done;
2072
0
  switch (typeid) {
2073
0
  case NC_INT64:
2074
0
  if(zcvt.int64v < 0) {stat = (THROW(NC_ENCZARR)); goto done;}
2075
0
      shapes[i] = (size64_t)zcvt.int64v;
2076
0
      break;
2077
0
  case NC_UINT64:
2078
0
      shapes[i] = (size64_t)zcvt.uint64v;
2079
0
      break;
2080
0
  default: {stat = (THROW(NC_ENCZARR)); goto done;}
2081
0
  }
2082
0
    }
2083
2084
0
done:
2085
0
    return THROW(stat);
2086
0
}
2087
2088
/* This code is a subset of NCZ_def_dim */
2089
static int
2090
createdim(NC_FILE_INFO_T* file, const char* name, size64_t dimlen, NC_DIM_INFO_T** dimp)
2091
0
{
2092
0
    int stat = NC_NOERR;
2093
0
    NC_GRP_INFO_T* root = file->root_grp;
2094
0
    NC_DIM_INFO_T* thed = NULL;
2095
0
    if((stat = nc4_dim_list_add(root, name, (size_t)dimlen, -1, &thed)))
2096
0
        goto done;
2097
0
    assert(thed != NULL);
2098
    /* Create struct for NCZ-specific dim info. */
2099
0
    if (!(thed->format_dim_info = calloc(1, sizeof(NCZ_DIM_INFO_T))))
2100
0
  {stat = NC_ENOMEM; goto done;}
2101
0
    ((NCZ_DIM_INFO_T*)thed->format_dim_info)->common.file = file;
2102
0
    *dimp = thed; thed = NULL;
2103
0
done:
2104
0
    return stat;
2105
0
}
2106
2107
2108
/*
2109
Given a list of segments, find corresponding group.
2110
*/
2111
static int
2112
locategroup(NC_FILE_INFO_T* file, size_t nsegs, NClist* segments, NC_GRP_INFO_T** grpp)
2113
0
{
2114
0
    int i, j, found, stat = NC_NOERR;
2115
0
    NC_GRP_INFO_T* grp = NULL;
2116
2117
0
    grp = file->root_grp;
2118
0
    for(i=0;i<nsegs;i++) {
2119
0
  const char* segment = nclistget(segments,i);
2120
0
  char norm_name[NC_MAX_NAME];
2121
0
  found = 0;
2122
0
  if((stat = nc4_check_name(segment,norm_name))) goto done;
2123
0
  for(j=0;j<ncindexsize(grp->children);j++) {
2124
0
      NC_GRP_INFO_T* subgrp = (NC_GRP_INFO_T*)ncindexith(grp->children,j);
2125
0
      if(strcmp(subgrp->hdr.name,norm_name)==0) {
2126
0
    grp = subgrp;
2127
0
    found = 1;
2128
0
    break;
2129
0
      }
2130
0
  }
2131
0
  if(!found) {stat = NC_ENOGRP; goto done;}
2132
0
    }
2133
    /* grp should be group of interest */
2134
0
    if(grpp) *grpp = grp;
2135
2136
0
done:
2137
0
    return THROW(stat);
2138
0
}
2139
2140
static int
2141
parsedimrefs(NC_FILE_INFO_T* file, NClist* dimnames, size64_t* shape, NC_DIM_INFO_T** dims, int create)
2142
0
{
2143
0
    int i, stat = NC_NOERR;
2144
0
    NClist* segments = NULL;
2145
2146
0
    for(i=0;i<nclistlength(dimnames);i++) {
2147
0
  NC_GRP_INFO_T* g = NULL;
2148
0
  NC_DIM_INFO_T* d = NULL;
2149
0
  int j;
2150
0
  const char* dimpath = nclistget(dimnames,i);
2151
0
  const char* dimname = NULL;
2152
2153
  /* Locate the corresponding NC_DIM_INFO_T* object */
2154
0
  nclistfreeall(segments);
2155
0
  segments = nclistnew();
2156
0
  if((stat = ncz_splitkey(dimpath,segments)))
2157
0
      goto done;
2158
0
  if((stat=locategroup(file,nclistlength(segments)-1,segments,&g)))
2159
0
      goto done;
2160
  /* Lookup the dimension */
2161
0
  dimname = nclistget(segments,nclistlength(segments)-1);
2162
0
  d = NULL;
2163
0
  dims[i] = NULL;
2164
0
  for(j=0;j<ncindexsize(g->dim);j++) {
2165
0
      d = (NC_DIM_INFO_T*)ncindexith(g->dim,j);
2166
0
      if(strcmp(d->hdr.name,dimname)==0) {
2167
0
    dims[i] = d;
2168
0
    break;
2169
0
      }
2170
0
  }
2171
0
  if(dims[i] == NULL && create) {
2172
      /* If not found and create then create it */
2173
0
      if((stat = createdim(file, dimname, shape[i], &dims[i])))
2174
0
          goto done;
2175
0
  } else {
2176
      /* Verify consistency */
2177
0
      if(dims[i]->len != shape[i])
2178
0
          {stat = NC_EDIMSIZE; goto done;}
2179
0
  }
2180
0
  assert(dims[i] != NULL);
2181
0
    }
2182
0
done:
2183
0
    nclistfreeall(segments);
2184
0
    return THROW(stat);
2185
0
}
2186
2187
/**
2188
 * @internal Get the metadata for a variable.
2189
 *
2190
 * @param var Pointer to var info struct.
2191
 *
2192
 * @return ::NC_NOERR No error.
2193
 * @return ::NC_EBADID Bad ncid.
2194
 * @return ::NC_ENOMEM Out of memory.
2195
 * @return ::NC_EHDFERR HDF5 returned error.
2196
 * @return ::NC_EVARMETA Error with var metadata.
2197
 * @author Ed Hartnett
2198
 */
2199
int
2200
ncz_get_var_meta(NC_FILE_INFO_T* file, NC_VAR_INFO_T* var)
2201
0
{
2202
0
    int retval = NC_NOERR;
2203
2204
0
    assert(file && var && var->format_var_info);
2205
0
    LOG((3, "%s: var %s", __func__, var->hdr.name));
2206
0
    ZTRACE(3,"file=%s var=%s",file->controller->path,var->hdr.name);
2207
    
2208
    /* Have we already read the var metadata? */
2209
0
    if (var->meta_read)
2210
0
  goto done;
2211
2212
#ifdef LOOK
2213
    /* Get the current chunk cache settings. */
2214
    if ((access_pid = H5Dget_access_plist(hdf5_var->hdf_datasetid)) < 0)
2215
  BAIL(NC_EVARMETA);
2216
2217
    /* Learn about current chunk cache settings. */
2218
    if ((H5Pget_chunk_cache(access_pid, &(var->chunk_cache_nelems),
2219
          &(var->chunk_cache_size), &rdcc_w0)) < 0)
2220
  BAIL(NC_EHDFERR);
2221
    var->chunk_cache_preemption = rdcc_w0;
2222
2223
    /* Get the dataset creation properties. */
2224
    if ((propid = H5Dget_create_plist(hdf5_var->hdf_datasetid)) < 0)
2225
  BAIL(NC_EHDFERR);
2226
2227
    /* Get var chunking info. */
2228
    if ((retval = get_chunking_info(propid, var)))
2229
  BAIL(retval);
2230
2231
    /* Get filter info for a var. */
2232
    if ((retval = get_filter_info(propid, var)))
2233
  BAIL(retval);
2234
2235
    /* Get fill value, if defined. */
2236
    if ((retval = get_fill_info(propid, var)))
2237
  BAIL(retval);
2238
2239
    /* Is this a deflated variable with a chunksize greater than the
2240
     * current cache size? */
2241
    if ((retval = nc4_adjust_var_cache(var)))
2242
  BAIL(retval);
2243
2244
    /* Is there an attribute which means quantization was used? */
2245
    if ((retval = get_quantize_info(var)))
2246
  BAIL(retval);
2247
2248
    if (var->coords_read && !var->dimscale)
2249
  if ((retval = get_attached_info(var, hdf5_var, var->ndims, hdf5_var->hdf_datasetid)))
2250
      goto done;;
2251
#endif
2252
2253
    /* Remember that we have read the metadata for this var. */
2254
0
    var->meta_read = NC_TRUE;
2255
0
done:
2256
0
    return ZUNTRACE(retval);
2257
0
}
2258
2259
#if 0
2260
int
2261
ncz_create_superblock(NCZ_FILE_INFO_T* zinfo)
2262
{
2263
    int stat = NC_NOERR;
2264
    NCjson* json = NULL;
2265
    NCZMAP* map = NULL;
2266
    char version[1024];
2267
2268
    ZTRACE(4,"zinfo=%s",zinfo->common.file->controller->path);
2269
2270
    /* If V2, then do not create a superblock per-se */
2271
    if(!(zinfo->controls.flags & FLAG_NCZARR_V1)) goto done;
2272
2273
    map = zinfo->map;
2274
2275
    /* create superblock json */
2276
    if((stat = NCJnew(NCJ_DICT,&json)))
2277
  goto done;
2278
2279
    /* fill */
2280
    snprintf(version,sizeof(version),"%d",zinfo->zarr.zarr_version);
2281
    if((stat = NCJaddstring(json,NCJ_STRING,"zarr_format"))) goto done;
2282
    if((stat = NCJaddstring(json,NCJ_INT,version))) goto done;
2283
    if((stat = NCJaddstring(json,NCJ_STRING,NCZ_V2_VERSION))) goto done;
2284
    {
2285
  char ver[1024];
2286
  snprintf(ver,sizeof(ver),"%lu.%lu.%lu",
2287
     zinfo->zarr.nczarr_version.major,
2288
     zinfo->zarr.nczarr_version.minor,
2289
     zinfo->zarr.nczarr_version.release);
2290
  if((stat = NCJaddstring(json,NCJ_STRING,ver))) goto done;
2291
    }
2292
    /* Write back to map */
2293
    if((stat=NCZ_uploadjson(map,NCZMETAROOT,json)))
2294
        goto done;
2295
done:
2296
    NCJreclaim(json);
2297
    return ZUNTRACE(stat);
2298
}
2299
#endif
2300
2301
/* Compute the set of dim refs for this variable, taking purezarr and xarray into account */
2302
static int
2303
computedimrefs(NC_FILE_INFO_T* file, NC_VAR_INFO_T* var, int purezarr, int xarray, int ndims, NClist* dimnames, size64_t* shapes, NC_DIM_INFO_T** dims)
2304
0
{
2305
0
    int stat = NC_NOERR;
2306
0
    int i;
2307
0
    int createdims = 0; /* 1 => we need to create the dims in root if they do not already exist */
2308
0
    NCZ_FILE_INFO_T* zfile = (NCZ_FILE_INFO_T*)file->format_file_info;
2309
0
    NCZ_VAR_INFO_T* zvar = (NCZ_VAR_INFO_T*)(var->format_var_info);
2310
0
    NCjson* jatts = NULL;
2311
2312
0
    ZTRACE(3,"file=%s var=%s purezarr=%d xarray=%d ndims=%d shape=%s",
2313
0
      file->controller->path,var->hdr.name,purezarr,xarray,(int)ndims,nczprint_vector(ndims,shapes));
2314
0
    assert(zfile && zvar);
2315
2316
0
    if(purezarr && xarray) {/* Read in the attributes to get xarray dimdef attribute; Note that it might not exist */
2317
  /* Note that if xarray && !purezarr, then xarray will be superceded by the nczarr dimensions key */
2318
0
        char zdimname[4096];
2319
0
  if(zvar->xarray == NULL) {
2320
0
      assert(nclistlength(dimnames) == 0);
2321
0
      if((stat = ncz_read_atts(file,(NC_OBJ*)var))) goto done;
2322
0
  }
2323
0
  if(zvar->xarray != NULL) {
2324
      /* convert xarray to the dimnames */
2325
0
      for(i=0;i<nclistlength(zvar->xarray);i++) {
2326
0
          snprintf(zdimname,sizeof(zdimname),"/%s",(const char*)nclistget(zvar->xarray,i));
2327
0
          nclistpush(dimnames,strdup(zdimname));
2328
0
      }
2329
0
  }
2330
0
  createdims = 1; /* may need to create them */
2331
0
    }
2332
2333
    /* If pure zarr and we have no dimref names, then fake it */
2334
0
    if(purezarr && nclistlength(dimnames) == 0) {
2335
0
  createdims = 1;
2336
0
        for(i=0;i<ndims;i++) {
2337
      /* Compute the set of absolute paths to dimrefs */
2338
0
            char zdimname[4096];
2339
0
      snprintf(zdimname,sizeof(zdimname),"/%s_%llu",ZDIMANON,shapes[i]);
2340
0
      nclistpush(dimnames,strdup(zdimname));
2341
0
  }
2342
0
    }
2343
2344
    /* Now, use dimnames to get the dims; create if necessary */
2345
0
    if((stat = parsedimrefs(file,dimnames,shapes,dims,createdims)))
2346
0
        goto done;
2347
2348
0
done:
2349
0
    NCJreclaim(jatts);
2350
0
    return ZUNTRACE(THROW(stat));
2351
0
}
2352
2353
/**
2354
Implement the JSON convention:
2355
Stringify it as the value and make the attribute be of type "char".
2356
*/
2357
2358
static int
2359
json_convention_read(NCjson* json, NCjson** jtextp)
2360
0
{
2361
0
    int stat = NC_NOERR;
2362
0
    NCjson* jtext = NULL;
2363
0
    char* text = NULL;
2364
2365
0
    if(json == NULL) {stat = NC_EINVAL; goto done;}
2366
0
    if(NCJunparse(json,0,&text)) {stat = NC_EINVAL; goto done;}
2367
0
    if(NCJnewstring(NCJ_STRING,text,&jtext)) {stat = NC_EINVAL; goto done;}
2368
0
    *jtextp = jtext; jtext = NULL;
2369
0
done:
2370
0
    NCJreclaim(jtext);
2371
0
    nullfree(text);
2372
0
    return stat;
2373
0
}
2374
2375
#if 0
2376
/**
2377
Implement the JSON convention:
2378
Parse it as JSON and use that as its value in .zattrs.
2379
*/
2380
static int
2381
json_convention_write(size_t len, const void* data, NCjson** jsonp, int* isjsonp)
2382
{
2383
    int stat = NC_NOERR;
2384
    NCjson* jexpr = NULL;
2385
    int isjson = 0;
2386
2387
    assert(jsonp != NULL);
2388
    if(NCJparsen(len,(char*)data,0,&jexpr)) {
2389
  /* Ok, just treat as sequence of chars */
2390
  if((stat = NCJnewstringn(NCJ_STRING, len, data, &jexpr))) goto done;
2391
    }
2392
    isjson = 1;
2393
    *jsonp = jexpr; jexpr = NULL;
2394
    if(isjsonp) *isjsonp = isjson;
2395
done:
2396
    NCJreclaim(jexpr);
2397
    return stat;
2398
}
2399
#endif
2400
2401
/* Convert an attribute "types list to an envv style list */
2402
static int
2403
jtypes2atypes(NCjson* jtypes, NClist* atypes)
2404
0
{
2405
0
    int i, stat = NC_NOERR;
2406
0
    for(i=0;i<NCJlength(jtypes);i+=2) {
2407
0
  const NCjson* key = NCJith(jtypes,i);
2408
0
  const NCjson* value = NCJith(jtypes,i+1);
2409
0
  if(NCJsort(key) != NCJ_STRING) {stat = (THROW(NC_ENCZARR)); goto done;}
2410
0
  if(NCJsort(value) != NCJ_STRING) {stat = (THROW(NC_ENCZARR)); goto done;}
2411
0
  nclistpush(atypes,strdup(NCJstring(key)));
2412
0
  nclistpush(atypes,strdup(NCJstring(value)));
2413
0
    }
2414
0
done:
2415
0
    return stat;
2416
0
}