Coverage Report

Created: 2025-10-28 07:06

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