Coverage Report

Created: 2023-05-28 06:42

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