Coverage Report

Created: 2023-05-28 06:42

/src/netcdf-c/libnczarr/zutil.c
Line
Count
Source (jump to first uncovered line)
1
/*********************************************************************
2
 *   Copyright 2018, UCAR/Unidata
3
 *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4
 *********************************************************************/
5
6
/**
7
 * @file
8
 * @internal Misc. utility code
9
 *
10
 * @author Dennis Heimbigner
11
 */
12
13
#include "zincludes.h"
14
15
#undef DEBUG
16
17
/**************************************************/
18
/* Static zarr type name table */
19
20
/* Table of nc_type X {Zarr,NCZarr} X endianness
21
Issue: Need to distinquish NC_STRING && MAXSTRLEN==1 from NC_CHAR
22
in a way that allows other Zarr implementations to read the data.
23
24
Available info:
25
Write: we have the netcdf type, so there is no ambiguity.
26
Read: we have the variable type and also any attribute dtype,
27
but those types are ambiguous.
28
We also have the attribute vs variable type problem.
29
For pure zarr, we have to infer the type of an attribute,
30
so if we have "var:strattr = \"abcdef\"", then we need
31
to decide how to infer the type: NC_STRING vs NC_CHAR.
32
33
Solution:
34
For variables and for NCZarr type attributes, distinquish by using:
35
* ">S1" for NC_CHAR.
36
* "|S1" for NC_STRING && MAXSTRLEN==1
37
* "|Sn" for NC_STRING && MAXSTRLEN==n
38
This is admittedly a bit of a hack, and the first case in particular
39
will probably cause errors in some other Zarr implementations; the Zarr
40
spec is unclear about what combinations are legal.
41
Note that we could use "|U1", but since this is utf-16 or utf-32
42
in python, it may cause problems when reading what amounts to utf-8.
43
44
For attributes, we infer:
45
* NC_CHAR if the hint is 0
46
  - e.g. var:strattr = 'abcdef'" => NC_CHAR
47
* NC_STRING if hint is NC_STRING.
48
  - e.g. string var:strattr = \"abc\", \"def\"" => NC_STRING
49
50
Note also that if we read a pure zarr file we will probably always
51
see "|S1", so we will never see a variable of type NC_CHAR.
52
We might however see an attribute of type string.
53
*/
54
static const struct ZTYPES {
55
    char* zarr[3];
56
    char* nczarr[3];
57
} znames[NUM_ATOMIC_TYPES] = {
58
/* nc_type          Pure Zarr          NCZarr
59
                   NE   LE     BE       NE    LE    BE*/
60
/*NC_NAT*/  {{NULL,NULL,NULL},   {NULL,NULL,NULL}},
61
/*NC_BYTE*/ {{"|i1","<i1",">i1"},{"|i1","<i1",">i1"}},
62
/*NC_CHAR*/ {{">S1",">S1",">S1"},{">S1",">S1",">S1"}},
63
/*NC_SHORT*/  {{"|i2","<i2",">i2"},{"|i2","<i2",">i2"}},
64
/*NC_INT*/  {{"|i4","<i4",">i4"},{"|i4","<i4",">i4"}},
65
/*NC_FLOAT*/  {{"|f4","<f4",">f4"},{"|f4","<f4",">f4"}},
66
/*NC_DOUBLE*/ {{"|f8","<f8",">f8"},{"|f8","<f8",">f8"}},
67
/*NC_UBYTE*/  {{"|u1","<u1",">u1"},{"|u1","<u1",">u1"}},
68
/*NC_USHORT*/ {{"|u2","<u2",">u2"},{"|u2","<u2",">u2"}},
69
/*NC_UINT*/ {{"|u4","<u4",">u4"},{"|u4","<u4",">u4"}},
70
/*NC_INT64*/  {{"|i8","<i8",">i8"},{"|i8","<i8",">i8"}},
71
/*NC_UINT64*/ {{"|u8","<u8",">u8"},{"|u8","<u8",">u8"}},
72
/*NC_STRING*/ {{"|S%d","|S%d","|S%d"},{"|S%d","|S%d","|S%d"}},
73
};
74
75
#if 0
76
static const char* zfillvalue[NUM_ATOMIC_TYPES] = {
77
NULL, /*NC_NAT*/
78
"-127", /*NC_BYTE*/
79
"0", /*NC_CHAR*/
80
"-32767", /*NC_SHORT*/
81
"-2147483647", /*NC_INT*/
82
"9.9692099683868690e+36f", /* near 15 * 2^119 */ /*NC_FLOAT*/
83
"9.9692099683868690e+36", /*NC_DOUBLE*/
84
"255", /*NC_UBYTE*/
85
"65535", /*NC_USHORT*/
86
"4294967295", /*NC_UINT*/
87
"-9223372036854775806", /*NC_INT64*/
88
"18446744073709551614", /*NC_UINT64*/
89
"", /*NC_STRING*/
90
};
91
#endif
92
93
/* map nc_type -> NCJ_SORT */
94
static int zjsonsort[NUM_ATOMIC_TYPES] = {
95
NCJ_UNDEF, /*NC_NAT*/
96
NCJ_INT, /*NC_BYTE*/
97
NCJ_INT, /*NC_CHAR*/
98
NCJ_INT, /*NC_SHORT*/
99
NCJ_INT, /*NC_INT*/
100
NCJ_DOUBLE, /*NC_FLOAT*/
101
NCJ_DOUBLE, /*NC_DOUBLE*/
102
NCJ_INT, /*NC_UBYTE*/
103
NCJ_INT, /*NC_USHORT*/
104
NCJ_INT, /*NC_UINT*/
105
NCJ_INT, /*NC_INT64*/
106
NCJ_INT, /*NC_UINT64*/
107
NCJ_STRING, /*NC_STRING*/
108
};
109
110
/* Forward */
111
112
/**************************************************/
113
114
/**
115
@internal Get key for a group
116
@param grp - [in] group
117
@param pathp - [out] full path
118
@return NC_NOERR
119
@author Dennis Heimbigner
120
*/
121
int
122
NCZ_grpkey(const NC_GRP_INFO_T* grp, char** pathp)
123
0
{
124
0
    int stat = NC_NOERR;
125
0
    NClist* segments = nclistnew();
126
0
    NCbytes* path = NULL;
127
0
    NC_GRP_INFO_T* parent = NULL;
128
0
    int i;
129
130
0
    nclistinsert(segments,0,(void*)grp);
131
0
    parent = grp->parent;
132
0
    while(parent != NULL) {
133
0
        nclistinsert(segments,0,parent);
134
0
        parent = parent->parent;
135
0
    }
136
0
    path = ncbytesnew();
137
0
    for(i=0;i<nclistlength(segments);i++) {
138
0
  grp = nclistget(segments,i);
139
0
  if(i > 1) ncbytescat(path,"/"); /* Assume root is named "/" */
140
0
  ncbytescat(path,grp->hdr.name);
141
0
    }        
142
0
    if(pathp) *pathp = ncbytesextract(path);
143
144
0
    nclistfree(segments);
145
0
    ncbytesfree(path);
146
0
    return stat;
147
148
0
}
149
150
/**
151
@internal Get key for a var
152
@param var - [in] var
153
@param pathp - [out] full path
154
@return NC_NOERR
155
@author Dennis Heimbigner
156
*/
157
int
158
NCZ_varkey(const NC_VAR_INFO_T* var, char** pathp)
159
0
{
160
0
    int stat = NC_NOERR;
161
0
    char* grppath = NULL;
162
0
    char* varpath = NULL;
163
164
    /* Start by creating the full path for the parent group */
165
0
    if((stat = NCZ_grpkey(var->container,&grppath)))
166
0
  goto done;
167
    /* Create the suffix path using the var name */
168
0
    if((stat = nczm_concat(grppath,var->hdr.name,&varpath)))
169
0
  goto done;
170
    /* return path */
171
0
    if(pathp) {*pathp = varpath; varpath = NULL;}
172
173
0
done:
174
0
    nullfree(grppath);
175
0
    nullfree(varpath);
176
0
    return stat;
177
0
}
178
179
/**
180
@internal Get key for a dimension
181
@param dim - [in] dim
182
@param pathp - [out] full path
183
@return NC_NOERR
184
@author Dennis Heimbigner
185
*/
186
int
187
NCZ_dimkey(const NC_DIM_INFO_T* dim, char** pathp)
188
0
{
189
0
    int stat = NC_NOERR;
190
0
    char* grppath = NULL;
191
0
    char* dimpath = NULL;
192
193
    /* Start by creating the full path for the parent group */
194
0
    if((stat = NCZ_grpkey(dim->container,&grppath)))
195
0
  goto done;
196
    /* Create the suffix path using the dim name */
197
0
    if((stat = nczm_concat(grppath,dim->hdr.name,&dimpath)))
198
0
  goto done;
199
    /* return path */
200
0
    if(pathp) {*pathp = dimpath; dimpath = NULL;}
201
202
0
done:
203
0
    nullfree(grppath);
204
0
    nullfree(dimpath);
205
0
    return stat;
206
0
}
207
208
/**
209
@internal Split a key into pieces along '/' character; elide any leading '/'
210
@param  key - [in]
211
@param segments - [out] split path
212
@return NC_NOERR
213
@author Dennis Heimbigner
214
*/
215
int
216
ncz_splitkey(const char* key, NClist* segments)
217
0
{
218
0
    return nczm_split(key,segments);
219
0
}
220
221
/**************************************************/
222
/* Json sync code */
223
224
/**
225
@internal Down load a .z... structure into memory
226
@param zmap - [in] controlling zarr map
227
@param key - [in] .z... object to load
228
@param jsonp - [out] root of the loaded json
229
@return NC_NOERR
230
@author Dennis Heimbigner
231
*/
232
int
233
NCZ_downloadjson(NCZMAP* zmap, const char* key, NCjson** jsonp)
234
0
{
235
0
    int stat = NC_NOERR;
236
0
    size64_t len;
237
0
    char* content = NULL;
238
0
    NCjson* json = NULL;
239
240
0
    if((stat = nczmap_len(zmap, key, &len)))
241
0
  goto done;
242
0
    if((content = malloc(len+1)) == NULL)
243
0
  {stat = NC_ENOMEM; goto done;}
244
0
    if((stat = nczmap_read(zmap, key, 0, len, (void*)content)))
245
0
  goto done;
246
0
    content[len] = '\0';
247
248
0
    if((stat = NCJparse(content,0,&json)) < 0)
249
0
  {stat = NC_ENCZARR; goto done;}
250
251
0
    if(jsonp) {*jsonp = json; json = NULL;}
252
253
0
done:
254
0
    NCJreclaim(json);
255
0
    nullfree(content);
256
0
    return stat;
257
0
}
258
259
/**
260
@internal  Upload a modified json tree to a .z... structure.
261
@param zmap - [in] controlling zarr map
262
@param key - [in] .z... object to load
263
@param json - [in] root of the json tree
264
@return NC_NOERR
265
@author Dennis Heimbigner
266
*/
267
int
268
NCZ_uploadjson(NCZMAP* zmap, const char* key, NCjson* json)
269
0
{
270
0
    int stat = NC_NOERR;
271
0
    char* content = NULL;
272
273
0
    ZTRACE(4,"zmap=%p key=%s",zmap,key);
274
275
#ifdef DEBUG
276
fprintf(stderr,"uploadjson: %s\n",key); fflush(stderr);
277
#endif
278
    /* Unparse the modified json tree */
279
0
    if((stat = NCJunparse(json,0,&content)))
280
0
  goto done;
281
0
    ZTRACEMORE(4,"\tjson=%s",content);
282
    
283
    /* Write the metadata */
284
0
    if((stat = nczmap_write(zmap, key, 0, strlen(content), content)))
285
0
  goto done;
286
287
0
done:
288
0
    nullfree(content);
289
0
    return ZUNTRACE(stat);
290
0
}
291
292
#if 0
293
/**
294
@internal create object, return empty dict; ok if already exists.
295
@param zmap - [in] map
296
@param key - [in] key of the object
297
@param jsonp - [out] return parsed json
298
@return NC_NOERR
299
@return NC_EINVAL if object exists
300
@author Dennis Heimbigner
301
*/
302
int
303
NCZ_createdict(NCZMAP* zmap, const char* key, NCjson** jsonp)
304
{
305
    int stat = NC_NOERR;
306
    NCjson* json = NULL;
307
308
    /* See if it already exists */
309
    stat = NCZ_downloadjson(zmap,key,&json);
310
    if(stat != NC_NOERR) {
311
  if(stat == NC_EEMPTY) {/* create it */
312
      if((stat = nczmap_def(zmap,key,NCZ_ISMETA)))
313
    goto done;      
314
        } else
315
      goto done;
316
    } else {
317
  /* Already exists, fail */
318
  stat = NC_EINVAL;
319
  goto done;
320
    }
321
    /* Create the empty dictionary */
322
    if((stat = NCJnew(NCJ_DICT,&json)))
323
  goto done;
324
    if(jsonp) {*jsonp = json; json = NULL;}
325
done:
326
    NCJreclaim(json);
327
    return stat;
328
}
329
330
/**
331
@internal create object, return empty array; ok if already exists.
332
@param zmap - [in] map
333
@param key - [in] key of the object
334
@param jsonp - [out] return parsed json
335
@return NC_NOERR
336
@return NC_EINVAL if object exits
337
@author Dennis Heimbigner
338
*/
339
int
340
NCZ_createarray(NCZMAP* zmap, const char* key, NCjson** jsonp)
341
{
342
    int stat = NC_NOERR;
343
    NCjson* json = NULL;
344
345
    stat = NCZ_downloadjson(zmap,key,&json);
346
    if(stat != NC_NOERR) {
347
  if(stat == NC_EEMPTY) {/* create it */
348
      if((stat = nczmap_def(zmap,key,NCZ_ISMETA)))
349
    goto done;      
350
      /* Create the initial array */
351
      if((stat = NCJnew(NCJ_ARRAY,&json)))
352
    goto done;
353
        } else {
354
      stat = NC_EINVAL;
355
      goto done;
356
  }
357
    }
358
    if(json->sort != NCJ_ARRAY) {stat = NC_ENCZARR; goto done;}
359
    if(jsonp) {*jsonp = json; json = NULL;}
360
done:
361
    NCJreclaim(json);
362
    return stat;
363
}
364
#endif /*0*/
365
366
/**
367
@internal Get contents of a meta object; fail it it does not exist
368
@param zmap - [in] map
369
@param key - [in] key of the object
370
@param jsonp - [out] return parsed json
371
@return NC_NOERR
372
@return NC_EEMPTY [object did not exist]
373
@author Dennis Heimbigner
374
*/
375
int
376
NCZ_readdict(NCZMAP* zmap, const char* key, NCjson** jsonp)
377
0
{
378
0
    int stat = NC_NOERR;
379
0
    NCjson* json = NULL;
380
381
0
    if((stat = NCZ_downloadjson(zmap,key,&json)))
382
0
  goto done;
383
0
    if(NCJsort(json) != NCJ_DICT) {stat = NC_ENCZARR; goto done;}
384
0
    if(jsonp) {*jsonp = json; json = NULL;}
385
0
done:
386
0
    NCJreclaim(json);
387
0
    return stat;
388
0
}
389
390
/**
391
@internal Get contents of a meta object; fail it it does not exist
392
@param zmap - [in] map
393
@param key - [in] key of the object
394
@param jsonp - [out] return parsed json
395
@return NC_NOERR
396
@return NC_EEMPTY [object did not exist]
397
@author Dennis Heimbigner
398
*/
399
int
400
NCZ_readarray(NCZMAP* zmap, const char* key, NCjson** jsonp)
401
0
{
402
0
    int stat = NC_NOERR;
403
0
    NCjson* json = NULL;
404
405
0
    if((stat = NCZ_downloadjson(zmap,key,&json)))
406
0
  goto done;
407
0
    if(NCJsort(json) != NCJ_ARRAY) {stat = NC_ENCZARR; goto done;}
408
0
    if(jsonp) {*jsonp = json; json = NULL;}
409
0
done:
410
0
    NCJreclaim(json);
411
0
    return stat;
412
0
}
413
414
#if 0
415
/**
416
@internal Given an nc_type, produce the corresponding
417
default fill value as a string.
418
@param nctype - [in] nc_type
419
@param defaltp - [out] pointer to hold pointer to the value
420
@return NC_NOERR
421
@author Dennis Heimbigner
422
*/
423
424
int
425
ncz_default_fill_value(nc_type nctype, const char** dfaltp)
426
{
427
    if(nctype <= 0 || nctype > NC_MAX_ATOMIC_TYPE) return NC_EINVAL;
428
    if(dfaltp) *dfaltp = zfillvalue[nctype];
429
    return NC_NOERR;          
430
}
431
#endif
432
433
/**
434
@internal Given an nc_type, produce the corresponding
435
fill value JSON type
436
@param nctype - [in] nc_type
437
@param sortp - [out] pointer to hold pointer to the JSON type
438
@return NC_NOERR
439
@author Dennis Heimbigner
440
*/
441
442
int
443
ncz_fill_value_sort(nc_type nctype, int* sortp)
444
0
{
445
0
    if(nctype <= 0 || nctype > NC_MAX_ATOMIC_TYPE) return NC_EINVAL;
446
0
    if(sortp) *sortp = zjsonsort[nctype];
447
0
    return NC_NOERR;         
448
0
}
449
450
/* Return 1 if this machine is little endian */
451
int
452
NCZ_isLittleEndian(void)
453
0
{
454
0
    union {
455
0
        unsigned char bytes[SIZEOF_INT];
456
0
  int i;
457
0
    } u;
458
0
    u.i = 1;
459
0
    return (u.bytes[0] == 1 ? 1 : 0);
460
0
}
461
462
463
/*
464
Given a path to a group, return the list of objects
465
that contain another object with the name of the tag.
466
For example, we can get the immediate list of subgroups
467
by using the tag ".zgroup".
468
Basically we return the set of X where <prefix>/X/<tag>
469
is an object in the map.
470
Note: need to test with "/", "", and with and without trailing "/".
471
*/
472
int
473
NCZ_subobjects(NCZMAP* map, const char* prefix, const char* tag, char dimsep, NClist* objlist)
474
0
{
475
0
    int i,stat=NC_NOERR;
476
0
    NClist* matches = nclistnew();
477
0
    NCbytes* path = ncbytesnew();
478
479
    /* Get the list of names just below prefix */
480
0
    if((stat = nczmap_search(map,prefix,matches))) goto done;
481
0
    for(i=0;i<nclistlength(matches);i++) {
482
0
  const char* name = nclistget(matches,i);
483
0
  size_t namelen= strlen(name); 
484
  /* Ignore keys that start with .z or .nc or a potential chunk name */
485
0
  if(namelen >= 3 && name[0] == '.' && name[1] == 'n' && name[2] == 'c')
486
0
      continue;
487
0
  if(namelen >= 2 && name[0] == '.' && name[1] == 'z')
488
0
      continue;
489
0
  if(NCZ_ischunkname(name,dimsep))
490
0
      continue;
491
  /* Create <prefix>/<name>/<tag> and see if it exists */
492
0
  ncbytesclear(path);
493
0
  ncbytescat(path,prefix);
494
0
  ncbytescat(path,"/");
495
0
  ncbytescat(path,name);
496
0
  ncbytescat(path,tag);
497
  /* See if this object exists */
498
0
        if((stat = nczmap_exists(map,ncbytescontents(path))) == NC_NOERR)
499
0
      nclistpush(objlist,name);
500
0
    }
501
502
0
done:
503
0
    nclistfreeall(matches);
504
0
    ncbytesfree(path);
505
0
    return stat;
506
0
}
507
508
#if 0
509
/* Convert a netcdf-4 type integer */
510
int
511
ncz_nctypedecode(const char* snctype, nc_type* nctypep)
512
{
513
    unsigned nctype = 0;
514
    if(sscanf(snctype,"%u",&nctype)!=1) return NC_EINVAL;
515
    if(nctypep) *nctypep = nctype;
516
    return NC_NOERR;
517
}
518
#endif
519
520
/**
521
@internal Given an nc_type+other, produce the corresponding dtype string.
522
@param nctype     - [in] nc_type
523
@param endianness - [in] endianness
524
@param purezarr   - [in] 1=>pure zarr, 0 => nczarr
525
@param strlen     - [in] max string length
526
@param namep      - [out] pointer to hold pointer to the dtype; user frees
527
@return NC_NOERR
528
@return NC_EINVAL
529
@author Dennis Heimbigner
530
*/
531
532
int
533
ncz_nctype2dtype(nc_type nctype, int endianness, int purezarr, int len, char** dnamep)
534
0
{
535
0
    char dname[64];
536
0
    char* format = NULL;
537
538
0
    if(nctype <= NC_NAT || nctype > NC_MAX_ATOMIC_TYPE) return NC_EINVAL;
539
0
    if(purezarr)
540
0
        format = znames[nctype].zarr[endianness];
541
0
    else
542
0
        format = znames[nctype].nczarr[endianness];
543
0
    snprintf(dname,sizeof(dname),format,len);
544
0
    if(dnamep) *dnamep = strdup(dname);
545
0
    return NC_NOERR;   
546
0
}
547
548
/*
549
@internal Convert a numcodecs dtype spec to a corresponding nc_type.
550
@param nctype   - [in] dtype the dtype to convert
551
@param nctype   - [in] typehint help disambiguate char vs string
552
@param purezarr - [in] 1=>pure zarr, 0 => nczarr
553
@param nctypep  - [out] hold corresponding type
554
@param endianp  - [out] hold corresponding endianness
555
@param typelenp - [out] hold corresponding type size (for fixed length strings)
556
@return NC_NOERR
557
@return NC_EINVAL
558
@author Dennis Heimbigner
559
*/
560
561
int
562
ncz_dtype2nctype(const char* dtype, nc_type typehint, int purezarr, nc_type* nctypep, int* endianp, int* typelenp)
563
0
{
564
0
    int stat = NC_NOERR;
565
0
    int typelen = 0;
566
0
    int count;
567
0
    char tchar;
568
0
    nc_type nctype = NC_NAT;
569
0
    int endianness = -1;
570
0
    const char* p;
571
0
    int n;
572
573
0
    if(endianp) *endianp = NC_ENDIAN_NATIVE;
574
0
    if(nctypep) *nctypep = NC_NAT;
575
576
0
    if(dtype == NULL) goto zerr;
577
0
    p = dtype;
578
0
    switch (*p++) {
579
0
    case '<': endianness = NC_ENDIAN_LITTLE; break;
580
0
    case '>': endianness = NC_ENDIAN_BIG; break;
581
0
    case '|': endianness = NC_ENDIAN_NATIVE; break;
582
0
    default: p--; endianness = NC_ENDIAN_NATIVE; break;
583
0
    }
584
0
    tchar = *p++; /* get the base type */
585
    /* Decode the type length */
586
0
    count = sscanf(p,"%d%n",&typelen,&n);
587
0
    if(count == 0) goto zerr;
588
0
    p += n;
589
590
    /* Short circuit fixed length strings */
591
0
    if(tchar == 'S') {
592
  /* Fixed length string */
593
0
  switch (typelen) {
594
0
  case 1:
595
0
      nctype = (endianness == NC_ENDIAN_BIG ? NC_CHAR : NC_STRING);
596
0
      if(purezarr) nctype = NC_STRING; /* Zarr has no NC_CHAR type */
597
0
      break;
598
0
  default:
599
0
      nctype = NC_STRING;
600
0
      break;
601
0
  }
602
  /* String/char have no endianness */
603
0
  endianness = NC_ENDIAN_NATIVE;
604
0
    } else {
605
0
  switch(typelen) {
606
0
        case 1:
607
0
      switch (tchar) {
608
0
        case 'i': nctype = NC_BYTE; break;
609
0
        case 'u': nctype = NC_UBYTE; break;
610
0
      default: goto zerr;
611
0
      }
612
0
      break;
613
0
        case 2:
614
0
  switch (tchar) {
615
0
  case 'i': nctype = NC_SHORT; break;
616
0
  case 'u': nctype = NC_USHORT; break;
617
0
  default: goto zerr;
618
0
  }
619
0
  break;
620
0
        case 4:
621
0
  switch (tchar) {
622
0
  case 'i': nctype = NC_INT; break;
623
0
  case 'u': nctype = NC_UINT; break;
624
0
  case 'f': nctype = NC_FLOAT; break;
625
0
  default: goto zerr;
626
0
  }
627
0
  break;
628
0
        case 8:
629
0
  switch (tchar) {
630
0
  case 'i': nctype = NC_INT64; break;
631
0
  case 'u': nctype = NC_UINT64; break;
632
0
  case 'f': nctype = NC_DOUBLE; break;
633
0
  default: goto zerr;
634
0
  }
635
0
  break;
636
0
        default: goto zerr;
637
0
        }
638
0
    }
639
640
#if 0
641
    /* Convert NC_ENDIAN_NATIVE and NC_ENDIAN_NA */
642
    if(endianness == NC_ENDIAN_NATIVE)
643
        endianness = (NC_isLittleEndian()?NC_ENDIAN_LITTLE:NC_ENDIAN_BIG);
644
#endif
645
646
0
    if(nctypep) *nctypep = nctype;
647
0
    if(typelenp) *typelenp = typelen;
648
0
    if(endianp) *endianp = endianness;
649
650
0
done:
651
0
    return stat;
652
0
zerr:
653
0
    stat = NC_ENCZARR;
654
0
    goto done;
655
0
}
656
657
/* Infer the attribute's type based
658
primarily on the first atomic value encountered
659
recursively.
660
*/
661
int
662
NCZ_inferattrtype(NCjson* value, nc_type typehint, nc_type* typeidp)
663
0
{
664
0
    int i,stat = NC_NOERR;
665
0
    nc_type typeid;
666
0
    NCjson* j = NULL;
667
0
    unsigned long long u64;
668
0
    long long i64;
669
0
    int negative = 0;
670
671
0
    if(NCJsort(value) == NCJ_ARRAY && NCJlength(value) == 0)
672
0
        {typeid = NC_NAT; goto done;} /* Empty array is illegal */
673
674
0
    if(NCJsort(value) == NCJ_NULL)
675
0
        {typeid = NC_NAT; goto done;} /* NULL is also illegal */
676
677
0
    if(NCJsort(value) == NCJ_DICT) /* Complex JSON expr -- a dictionary */
678
0
        {typeid = NC_NAT; goto done;}
679
680
    /* If an array, make sure all the elements are simple */
681
0
    if(value->sort == NCJ_ARRAY) {
682
0
  for(i=0;i<NCJlength(value);i++) {
683
0
      j=NCJith(value,i);
684
0
      if(!NCJisatomic(j))
685
0
          {typeid = NC_NAT; goto done;}
686
0
  }
687
0
    }
688
689
    /* Infer from first element */
690
0
    if(value->sort == NCJ_ARRAY) {
691
0
        j=NCJith(value,0);
692
0
  return NCZ_inferattrtype(j,typehint,typeidp);
693
0
    }
694
695
    /* At this point, value is a primitive JSON Value */
696
697
0
    switch (NCJsort(value)) {
698
0
    case NCJ_NULL:
699
0
        typeid = NC_NAT;
700
0
  return NC_NOERR;
701
0
    case NCJ_DICT:
702
0
      typeid = NC_CHAR;
703
0
  goto done;
704
0
    case NCJ_UNDEF:
705
0
  return NC_EINVAL;
706
0
    default: /* atomic */
707
0
  break;
708
0
    }
709
710
0
    if(NCJstring(value) != NULL)
711
0
        negative = (NCJstring(value)[0] == '-');
712
0
    switch (value->sort) {
713
0
    case NCJ_INT:
714
0
  if(negative) {
715
0
      sscanf(NCJstring(value),"%lld",&i64);
716
0
      u64 = (unsigned long long)i64;
717
0
  } else
718
0
      sscanf(NCJstring(value),"%llu",&u64);
719
0
  typeid = NCZ_inferinttype(u64,negative);
720
0
  break;
721
0
    case NCJ_DOUBLE:
722
0
  typeid = NC_DOUBLE;
723
0
  break;
724
0
    case NCJ_BOOLEAN:
725
0
  typeid = NC_UBYTE;
726
0
  break;
727
0
    case NCJ_STRING: /* requires special handling as an array of characters */
728
0
  typeid = NC_CHAR;
729
0
  break;
730
0
    default:
731
0
  stat = NC_ENCZARR;
732
0
    }
733
0
done:
734
0
    if(typeidp) *typeidp = typeid;
735
0
    return stat;
736
0
}
737
738
/* Infer the int type from the value;
739
   minimum type will be int.
740
*/
741
int
742
NCZ_inferinttype(unsigned long long u64, int negative)
743
0
{
744
0
    long long i64 = (long long)u64; /* keep bit pattern */
745
0
    if(!negative && u64 >= NC_MAX_INT64) return NC_UINT64;
746
0
    if(i64 < 0) {
747
0
  if(i64 >= NC_MIN_INT) return NC_INT;
748
0
  return NC_INT64;
749
0
    }
750
0
    if(i64 <= NC_MAX_INT) return NC_INT;
751
0
    if(i64 <= NC_MAX_UINT) return NC_UINT;
752
0
    return NC_INT64;
753
0
}
754
 
755
/**
756
@internal Similar to NCZ_grppath, but using group ids.
757
@param gid - [in] group id
758
@param pathp - [out] full path
759
@return NC_NOERR
760
@author Dennis Heimbigner
761
*/
762
int
763
NCZ_grpname_full(int gid, char** pathp)
764
0
{
765
0
    int stat = NC_NOERR;
766
0
    size_t len;
767
0
    char* path = NULL;
768
769
0
    if((stat = nc_inq_grpname_full(gid,&len,NULL))) return stat;
770
0
    if((path=malloc(len+1)) == NULL) return NC_ENOMEM;    
771
0
    if((stat = nc_inq_grpname_full(gid,&len,path))) return stat;
772
0
    path[len] = '\0'; /* ensure null terminated */
773
0
    if(pathp) {*pathp = path; path = NULL;}
774
0
    return stat;
775
0
}
776
777
/**
778
@internal Parse a commified string list
779
@param s [in] string to parse
780
@param list - [in/out] storage for the parsed list
781
@return NC_NOERR
782
@author Dennis Heimbigner
783
*/
784
int
785
NCZ_comma_parse(const char* s, NClist* list)
786
0
{
787
0
    int stat = NC_NOERR;
788
0
    const char* p = NULL;
789
0
    const char* endp = NULL;
790
791
0
    if(s == NULL || *s == '\0') goto done;
792
793
    /* Split s at the commas or EOL */
794
0
    p = s;
795
0
    for(;;) {
796
0
  char* s;
797
0
  ptrdiff_t slen;
798
0
  endp = strchr(p,',');
799
0
  if(endp == NULL) endp = p + strlen(p);
800
0
  slen = (endp - p);
801
0
  if((s = malloc(slen+1)) == NULL) {stat = NC_ENOMEM; goto done;}
802
0
  memcpy(s,p,slen);
803
0
  s[slen] = '\0';
804
0
  if(nclistmatch(list,s,0)) {
805
0
      nullfree(s); /* duplicate */
806
0
  } else {
807
0
      nclistpush(list,s);
808
0
  }
809
0
  if(*endp == '\0') break;
810
0
  p = endp+1;
811
0
    }
812
813
0
done:
814
0
    return stat;
815
0
}
816
817
/**************************************************/
818
/* Endianness support */
819
/* signature: void swapinline16(void* ip) */
820
0
#define swapinline16(ip) \
821
0
{ \
822
0
    union {char b[2]; unsigned short i;} u; \
823
0
    char* src = (char*)(ip); \
824
0
    u.b[0] = src[1]; \
825
0
    u.b[1] = src[0]; \
826
0
    *((unsigned short*)ip) = u.i; \
827
0
}
828
829
/* signature: void swapinline32(void* ip) */
830
0
#define swapinline32(ip) \
831
0
{ \
832
0
    union {char b[4]; unsigned int i;} u; \
833
0
    char* src = (char*)(ip); \
834
0
    u.b[0] = src[3]; \
835
0
    u.b[1] = src[2]; \
836
0
    u.b[2] = src[1]; \
837
0
    u.b[3] = src[0]; \
838
0
    *((unsigned int*)ip) = u.i; \
839
0
}
840
841
/* signature: void swapinline64(void* ip) */
842
0
#define swapinline64(ip) \
843
0
{ \
844
0
    union {char b[8]; unsigned long long i;} u; \
845
0
    char* src = (char*)(ip); \
846
0
    u.b[0] = src[7]; \
847
0
    u.b[1] = src[6]; \
848
0
    u.b[2] = src[5]; \
849
0
    u.b[3] = src[4]; \
850
0
    u.b[4] = src[3]; \
851
0
    u.b[5] = src[2]; \
852
0
    u.b[6] = src[1]; \
853
0
    u.b[7] = src[0]; \
854
0
    *((unsigned long long*)ip) = u.i; \
855
0
}
856
857
int
858
NCZ_swapatomicdata(size_t datalen, void* data, int typesize)
859
0
{
860
0
    int stat = NC_NOERR;
861
0
    int i;
862
863
0
    assert(datalen % typesize == 0);
864
865
0
    if(typesize == 1) goto done;
866
867
    /*(typesize > 1)*/
868
0
    for(i=0;i<datalen;) {
869
0
  char* p = ((char*)data) + i;
870
0
        switch (typesize) {
871
0
        case 2: swapinline16(p); break;
872
0
        case 4: swapinline32(p); break;
873
0
        case 8: swapinline64(p); break;
874
0
        default: break;
875
0
  }
876
0
  i += typesize;
877
0
    }
878
0
done:
879
0
    return THROW(stat);
880
0
}
881
882
char**
883
NCZ_clonestringvec(size_t len, const char** vec)
884
0
{
885
0
    char** clone = NULL;
886
0
    size_t i;
887
0
    if(vec == NULL) return NULL;
888
0
    if(len == 0) { /* Figure out size as envv vector */
889
0
        const char** p;
890
0
        for(p=vec;*p;p++) len++;
891
0
    }
892
0
    clone = malloc(sizeof(char*) * (1+len));
893
0
    if(clone == NULL) return NULL;
894
0
    for(i=0;i<len;i++) {
895
0
  char* s = strdup(vec[i]);
896
0
  if(s == NULL) return NULL;
897
0
  clone[i] = s;
898
0
    }
899
0
    clone[len] = NULL;
900
0
    return clone;
901
0
}
902
903
void
904
NCZ_freestringvec(size_t len, char** vec)
905
0
{
906
0
    size_t i;
907
0
    if(vec == NULL) return;
908
0
    if(len == 0) { /* Figure out size as envv vector */
909
0
        char** p;
910
0
        for(p=vec;*p;p++) len++;
911
0
    }
912
0
    for(i=0;i<len;i++) {
913
0
  nullfree(vec[i]);
914
0
    }
915
0
    nullfree(vec);
916
0
}
917
918
int
919
NCZ_ischunkname(const char* name,char dimsep)
920
0
{
921
0
    int stat = NC_NOERR;
922
0
    const char* p;
923
0
    if(strchr("0123456789",name[0])== NULL)
924
0
        stat = NC_ENCZARR;
925
0
    else for(p=name;*p;p++) {
926
0
        if(*p != dimsep && strchr("0123456789",*p) == NULL) /* approximate */
927
0
      {stat = NC_ENCZARR; break;}
928
0
    }
929
0
    return stat;
930
0
}
931
932
char*
933
NCZ_chunkpath(struct ChunkKey key)
934
0
{
935
0
    size_t plen = nulllen(key.varkey)+1+nulllen(key.chunkkey);
936
0
    char* path = (char*)malloc(plen+1);
937
    
938
0
    if(path == NULL) return NULL;
939
0
    path[0] = '\0';
940
0
    strlcat(path,key.varkey,plen+1);
941
0
    strlcat(path,"/",plen+1);
942
0
    strlcat(path,key.chunkkey,plen+1);
943
0
    return path;    
944
0
}
945
946
int
947
NCZ_reclaim_fill_value(NC_VAR_INFO_T* var)
948
0
{
949
0
    int stat = NC_NOERR;
950
0
    if(var->fill_value) {
951
0
  int ncid = var->container->nc4_info->controller->ext_ncid;
952
0
  int tid = var->type_info->hdr.id;
953
0
  stat = nc_reclaim_data_all(ncid,tid,var->fill_value,1);
954
0
  var->fill_value = NULL;
955
0
    }
956
    /* Reclaim any existing fill_chunk */
957
0
    if(!stat) stat = NCZ_reclaim_fill_chunk(((NCZ_VAR_INFO_T*)var->format_var_info)->cache);
958
0
    return stat;
959
0
}
960
961
int
962
NCZ_copy_fill_value(NC_VAR_INFO_T* var, void**  dstp)
963
0
{
964
0
    int stat = NC_NOERR;
965
0
    int ncid = var->container->nc4_info->controller->ext_ncid;
966
0
    int tid = var->type_info->hdr.id;
967
0
    void* dst = NULL;
968
969
0
    if(var->fill_value) {
970
0
  if((stat = nc_copy_data_all(ncid,tid,var->fill_value,1,&dst))) goto done;
971
0
    }
972
0
    if(dstp) {*dstp = dst; dst = NULL;}
973
0
done:
974
0
    if(dst) (void)nc_reclaim_data_all(ncid,tid,dst,1);
975
0
    return stat;
976
0
}
977
978
979
/* Get max str len for a variable or grp */
980
/* Has side effect of setting values in the
981
   internal data structures */
982
int
983
NCZ_get_maxstrlen(NC_OBJ* obj)
984
0
{
985
0
    int maxstrlen = 0;
986
0
    assert(obj->sort == NCGRP || obj->sort == NCVAR);
987
0
    if(obj->sort == NCGRP) {
988
0
        NC_GRP_INFO_T* grp = (NC_GRP_INFO_T*)obj;
989
0
  NC_FILE_INFO_T* file = grp->nc4_info;
990
0
  NCZ_FILE_INFO_T* zfile = (NCZ_FILE_INFO_T*)file->format_file_info;
991
0
  if(zfile->default_maxstrlen == 0)
992
0
      zfile->default_maxstrlen = NCZ_MAXSTR_DEFAULT;
993
0
  maxstrlen = zfile->default_maxstrlen;
994
0
    } else { /*(obj->sort == NCVAR)*/
995
0
        NC_VAR_INFO_T* var = (NC_VAR_INFO_T*)obj;
996
0
  NCZ_VAR_INFO_T* zvar = (NCZ_VAR_INFO_T*)var->format_var_info;
997
0
        if(zvar->maxstrlen == 0)
998
0
      zvar->maxstrlen = NCZ_get_maxstrlen((NC_OBJ*)var->container);
999
0
  maxstrlen = zvar->maxstrlen;
1000
0
    }
1001
0
    return maxstrlen;
1002
0
}
1003
1004
int
1005
NCZ_fixed2char(const void* fixed, char** charp, size_t count, int maxstrlen)
1006
0
{
1007
0
    size_t i;
1008
0
    unsigned char* sp = NULL;
1009
0
    const unsigned char* p = fixed;
1010
0
    memset((void*)charp,0,sizeof(char*)*count);
1011
0
    for(i=0;i<count;i++,p+=maxstrlen) {
1012
0
  if(p[0] == '\0') {
1013
0
      sp = NULL;
1014
0
  } else {
1015
0
      if((sp = (unsigned char*)malloc(maxstrlen+1))==NULL) /* ensure null terminated */
1016
0
          return NC_ENOMEM; 
1017
0
      memcpy(sp,p,maxstrlen);
1018
0
      sp[maxstrlen] = '\0';
1019
0
  }
1020
0
  charp[i] = (char*)sp;
1021
0
  sp = NULL;
1022
0
    }
1023
0
    return NC_NOERR;
1024
0
}
1025
1026
int
1027
NCZ_char2fixed(const char** charp, void* fixed, size_t count, int maxstrlen)
1028
0
{
1029
0
    size_t i;
1030
0
    unsigned char* p = fixed;
1031
0
    memset(fixed,0,maxstrlen*count); /* clear target */
1032
0
    for(i=0;i<count;i++,p+=maxstrlen) {
1033
0
  size_t len;
1034
0
  if(charp[i] != NULL) {
1035
0
      len = strlen(charp[i]);
1036
0
      if(len > maxstrlen) len = maxstrlen;
1037
0
      memcpy(p,charp[i],len);
1038
0
  } else {
1039
0
      memset(p,'\0',maxstrlen);
1040
0
  }
1041
0
    }
1042
0
    return NC_NOERR;
1043
0
}
1044
1045
/*
1046
Wrap NC_copy_data, but take string value into account when overwriting
1047
*/
1048
int
1049
NCZ_copy_data(NC_FILE_INFO_T* file, NC_TYPE_INFO_T* xtype, const void* memory, size_t count, int noclear, void* copy)
1050
0
{
1051
0
    if(xtype->hdr.id == NC_STRING && !noclear) {
1052
0
  size_t i;
1053
0
  char** scopy = (char**)copy;
1054
  /* Reclaim any string fill values in copy */
1055
0
  for(i=0;i<count;i++) {
1056
0
      nullfree(scopy[i]);
1057
0
      scopy[i] = NULL;
1058
0
  }
1059
0
    }
1060
0
    return nc_copy_data(file->controller->ext_ncid,xtype->hdr.id,memory,count,copy);
1061
0
}
1062
1063
#if 0
1064
/* Recursive helper */
1065
static int
1066
checksimplejson(NCjson* json, int depth)
1067
{
1068
    int i;
1069
1070
    switch (NCJsort(json)) {
1071
    case NCJ_ARRAY:
1072
  if(depth > 0) return 0;  /* e.g. [...,[...],...]  or [...,{...},...] */
1073
  for(i=0;i < NCJlength(json);i++) {
1074
      NCjson* j = NCJith(json,i);
1075
      if(!checksimplejson(j,depth+1)) return 0;
1076
        }
1077
  break;
1078
    case NCJ_DICT:
1079
    case NCJ_NULL:
1080
    case NCJ_UNDEF:
1081
  return 0;
1082
    default: break;
1083
    }
1084
    return 1;
1085
}
1086
#endif
1087
1088
/* Return 1 if the attribute will be stored as a complex JSON valued attribute; return 0 otherwise */
1089
int
1090
NCZ_iscomplexjson(NCjson* json, nc_type typehint)
1091
0
{
1092
0
    int i, stat = 0;
1093
1094
0
    switch (NCJsort(json)) {
1095
0
    case NCJ_ARRAY:
1096
  /* If the typehint is NC_CHAR, then always treat it as complex */
1097
0
  if(typehint == NC_CHAR) {stat = 1; goto done;}
1098
  /* Otherwise see if it is a simple vector of atomic values */
1099
0
  for(i=0;i < NCJlength(json);i++) {
1100
0
      NCjson* j = NCJith(json,i);
1101
0
      if(!NCJisatomic(j)) {stat = 1; goto done;}
1102
0
        }
1103
0
  break;
1104
0
    case NCJ_DICT:
1105
0
    case NCJ_NULL:
1106
0
    case NCJ_UNDEF:
1107
0
  stat = 1; goto done;
1108
0
    default: break;
1109
0
    }
1110
0
done:
1111
0
    return stat;
1112
0
}