Coverage Report

Created: 2026-01-04 06:24

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