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