Coverage Report

Created: 2025-12-09 06:48

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