/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 | } |