diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a1a0d87667..52f24a0309 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,10 @@ Release Notes {#RELEASE_NOTES} This file contains a high-level description of this package's evolution. Releases are in reverse chronological order (most recent first). Note that, as of netcdf 4.2, the `netcdf-c++` and `netcdf-fortran` libraries have been separated into their own libraries. +## 4.9.4 - TBD + +* Step 1 in splitting PR [Github #3068](https://github.com/Unidata/netcdf-c/pull/3068). Update ncjson.[ch] and ncproplist.[ch]. Also fix references to old API. Also fix include/netcdf_ncjson.h and include/netcdf_proplist.h builds. See [Github #3086](https://github.com/Unidata/netcdf-c/pull/3086) for more information. + ## 4.9.3 - February 7, 2025 ## Known Issues diff --git a/include/Makefile.am b/include/Makefile.am index f47bdf4dd2..9253e61932 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -48,21 +48,26 @@ BUILT_SOURCES = netcdf_json.h netcdf_proplist.h # marked with a macro (OPTSTATIC) that marks the entry point as # static inside netcdf_json.h. This is an ugly hack to avoid # having to reference libnetcdf in the nczarr code wrappers. -# Note also that we incorporate the core of ncexternl.h into the netcdf_json.h file. +# Note that the file is built in builddir in case the build +# is out of source. # Give the recipe for building netcdf_json.h netcdf_json.h: ${top_srcdir}/libdispatch/ncjson.c ${top_srcdir}/include/ncjson.h ${top_srcdir}/include/ncexternl.h rm -fr ${builddir}/netcdf_json.h - cat ${srcdir}/ncjson.h | sed -e 's/NCJSON_H/NETCDF_JSON_H/' >> ${builddir}/netcdf_json.h - echo '#ifdef NETCDF_JSON_H' >> ${builddir}/netcdf_json.h - sed -e '/ncjson.h/d' < ${srcdir}/../libdispatch/ncjson.c >> ${builddir}/netcdf_json.h + cat ${srcdir}/ncjson.h \ + | sed -e '/!NCJSON_H/d' \ + | sed -e 's/NCJSON_H/NETCDF_JSON_H/' \ + >> ${builddir}/netcdf_json.h + cat ${srcdir}/../libdispatch/ncjson.c | sed -e '/ncjson.h/d' >> ${builddir}/netcdf_json.h echo '#endif /*NETCDF_JSON_H*/' >> ${builddir}/netcdf_json.h # netcdf_proplist is analogous to netcdf_json but, of course, using libdispatch/ncproplist.c and include/ncproplist.h # Give the recipe for building netcdf_proplist.h. Basically same as for netcdf_json.h netcdf_proplist.h: ${top_srcdir}/libdispatch/ncproplist.c ${top_srcdir}/include/ncproplist.h ${top_srcdir}/include/ncexternl.h rm -fr ${builddir}/netcdf_proplist.h - cat ${srcdir}/ncproplist.h | sed -e 's/NCPROPLIST_H/NETCDF_PROPLIST_H/' >> ${builddir}/netcdf_proplist.h - echo '#ifdef NETCDF_PROPLIST_H' >> ${builddir}/netcdf_proplist.h - sed -e '/ncproplist.h/d' < ${srcdir}/../libdispatch/ncproplist.c >> ${builddir}/netcdf_proplist.h + cat ${srcdir}/ncproplist.h \ + | sed -e '/!NCPROPLIST_H/d' \ + | sed -e 's/NCPROPLIST_H/NETCDF_PROPLIST_H/' \ + >> ${builddir}/netcdf_proplist.h + cat ${srcdir}/../libdispatch/ncproplist.c | sed -e '/ncproplist.h/d' >> ${builddir}/netcdf_proplist.h echo '#endif /*NETCDF_PROPLIST_H*/' >> ${builddir}/netcdf_proplist.h diff --git a/include/ncconfigure.h b/include/ncconfigure.h index 214b8150f6..bfc68d5731 100644 --- a/include/ncconfigure.h +++ b/include/ncconfigure.h @@ -134,7 +134,7 @@ unsigned long long int strtoull(const char*, char**, int); #endif /*_WIN32*/ #ifndef nulldup -#define nulldup(s) ((s)==NULL?NULL:strdup(s)) +#define nulldup(s) ((s)==NULL?s:strdup(s)) #endif #ifndef nulllen diff --git a/include/ncjson.h b/include/ncjson.h index 1de7ca0cbf..7297500e71 100644 --- a/include/ncjson.h +++ b/include/ncjson.h @@ -3,7 +3,7 @@ */ #ifndef NCJSON_H -#define NCJSON_H +#define NCJSON_H 1 #ifndef OPTEXPORT #ifdef NETCDF_JSON_H @@ -11,13 +11,18 @@ #else /*!NETCDF_JSON_H*/ #ifdef _WIN32 #define OPTEXPORT __declspec(dllexport) -#else +#else /*!WIN32*/ #define OPTEXPORT extern -#endif +#endif /*WIN32*/ #endif /*NETCDF_JSON_H*/ #endif /*OPTEXPORT*/ /**************************************************/ + +/* Return codes */ +#define NCJ_OK 0 /* must equal NC_NOERR in netcdf.h */ +#define NCJ_ERR (-1) /* must equal NC_ERROR in netcdf.h */ + /* Json object sorts (note use of term sort rather than e.g. type or discriminant) */ #define NCJ_UNDEF 0 #define NCJ_STRING 1 @@ -30,6 +35,10 @@ #define NCJ_NSORTS 8 +/* Dump/text/unparse flags */ +#define NCJFLAG_NONE 0 +#define NCJFLAG_INDENTED 1 + /* Define a struct to store primitive values as unquoted strings. The sort will provide more info. Do not bother with a union since the amount of saved space is minimal. @@ -39,8 +48,9 @@ typedef struct NCjson { int sort; /* of this object */ char* string; /* sort != DICT|ARRAY */ struct NCjlist { - size_t len; - struct NCjson** contents; + size_t alloc; + size_t len; + struct NCjson** contents; } list; /* sort == DICT|ARRAY */ } NCjson; @@ -48,16 +58,15 @@ typedef struct NCjson { don't use union so we can know when to reclaim sval */ struct NCJconst {int bval; long long ival; double dval; char* sval;}; -#define NCJconst_empty {0,0,0.0,NULL} /**************************************************/ /* Extended API */ -/* Return 0 if ok else -1 */ +/* Return NCJ_OK if ok else NCJ_ERR */ #if defined(__cplusplus) extern "C" { -#endif +#endif /*__cplusplus*/ /* Parse a string to NCjson*/ OPTEXPORT int NCJparse(const char* text, unsigned flags, NCjson** jsonp); @@ -80,24 +89,35 @@ OPTEXPORT int NCJnewstringn(int sort, size_t len, const char* value, NCjson** js /* Get dict key value by name */ OPTEXPORT int NCJdictget(const NCjson* dict, const char* key, const NCjson** valuep); +/* Functional version of NCJdictget */ +OPTEXPORT NCjson* NCJdictlookup(const NCjson* dict, const char* key); + /* Convert one json sort to value of another type; don't use union so we can know when to reclaim sval */ OPTEXPORT int NCJcvt(const NCjson* value, int outsort, struct NCJconst* output); -/* Insert an atomic value to an array or dict object. */ +/* Append an atomic value to an array or dict object. */ OPTEXPORT int NCJaddstring(NCjson* json, int sort, const char* s); /* Append value to an array or dict object. */ OPTEXPORT int NCJappend(NCjson* object, NCjson* value); -/* Insert key-value pair into a dict object. key will be copied */ -OPTEXPORT int NCJinsert(NCjson* object, const char* key, NCjson* value); +/* Append string value to an array or dict object. */ +OPTEXPORT int NCJappendstring(NCjson* object, int sort, const char* s); -/* Insert key-value pair as strings into a dict object. - key and value will be copied */ +/* Append int value to an array or dict object. */ +OPTEXPORT int NCJappendint(NCjson* object, long long n); + +/* Insert (string)key-(NCjson*)value pair into a dict object. key will be copied; jvalue will not */ +OPTEXPORT int NCJinsert(NCjson* object, const char* key, NCjson* jvalue); + +/* Insert key-value pair into a dict object. key and value will be copied */ OPTEXPORT int NCJinsertstring(NCjson* object, const char* key, const char* value); -/* Insert key-value pair where value is an int */ -OPTEXPORT int NCJinsertint(NCjson* object, const char* key, long long ivalue); +/* Overwrite key-value pair in a dict object. Act like NCJinsert if key not found */ +OPTEXPORT int NCJoverwrite(NCjson* object, const char* key, NCjson* value); + +/* Insert key-value pair into a dict object. key and value will be copied */ +OPTEXPORT int NCJinsertint(NCjson* object, const char* key, long long n); /* Unparser to convert NCjson object to text in buffer */ OPTEXPORT int NCJunparse(const NCjson* json, unsigned flags, char** textp); @@ -106,37 +126,57 @@ OPTEXPORT int NCJunparse(const NCjson* json, unsigned flags, char** textp); OPTEXPORT int NCJclone(const NCjson* json, NCjson** clonep); #ifndef NETCDF_JSON_H + /* dump NCjson* object to output file */ OPTEXPORT void NCJdump(const NCjson* json, unsigned flags, FILE*); + /* convert NCjson* object to output string */ -OPTEXPORT const char* NCJtotext(const NCjson* json); +OPTEXPORT const char* NCJtotext(const NCjson* json, unsigned flags); + +/* Sort a dictionary by key */ +OPTEXPORT void NCJdictsort(NCjson* jdict); + #endif /*NETCDF_JSON_H*/ #if defined(__cplusplus) } -#endif +#endif /*__cplusplus*/ /* Getters */ -#define NCJsort(x) ((x)->sort) -#define NCJstring(x) ((x)->string) -#define NCJlength(x) ((x)==NULL ? 0 : (x)->list.len) -#define NCJdictlength(x) ((x)==NULL ? 0 : (x)->list.len/2) -#define NCJcontents(x) ((x)->list.contents) +#define NCJsort(x) ((x)==NULL?NCJ_UNDEF:(x)->sort) +#define NCJstring(x) ((x)==NULL?NULL:(x)->string) +#define NCJarraylength(x) ((x)==NULL ? 0 : (x)->list.len) +#define NCJdictlength(x) ((x)==NULL ? 0 : ((x)->list.len) / 2) +#define NCJcontents(x) ((x)==NULL?NULL:(x)->list.contents) #define NCJith(x,i) ((x)->list.contents[i]) -#define NCJdictith(x,i) ((x)->list.contents[2*i]) +#define NCJdictkey(x,i) ((x)->list.contents[(i)*2]) +#define NCJdictvalue(x,i) ((x)->list.contents[((i)*2)+1]) /* Setters */ #define NCJsetsort(x,s) (x)->sort=(s) #define NCJsetstring(x,y) (x)->string=(y) #define NCJsetcontents(x,c) (x)->list.contents=(c) -#define NCJsetlength(x,l) (x)->list.len=(l) +#define NCJsetarraylength(x,l) (x)->list.len=(l) +#define NCJsetdictlength(x,l) (x)->list.len=((l)*2) /* Misc */ #define NCJisatomic(j) ((j)->sort != NCJ_ARRAY && (j)->sort != NCJ_DICT && (j)->sort != NCJ_NULL && (j)->sort != NCJ_UNDEF) /**************************************************/ +/* Error detection helper */ +#undef NCJDEBUG +#ifdef NCJDEBUG +static int +NCJBREAKPOINT(int err) +{ + (void)NCJBREAKPOINT; + return err; +} +#else +#define NCJBREAKPOINT(err) (err) +#endif /*NCJDEBUG*/ +#define NCJcheck(expr) do{if((expr) < 0) {stat = NCJBREAKPOINT(NCJ_ERR); goto done;}}while(0) -#endif /*NCJSON_H*/ - - +/**************************************************/ +#endif /*!NCJSON_H*/ /* Leave the ! as a tag for sed */ diff --git a/include/ncproplist.h b/include/ncproplist.h index 9561789a6c..044f6ca255 100644 --- a/include/ncproplist.h +++ b/include/ncproplist.h @@ -22,12 +22,16 @@ /**************************************************/ /* This is used to store a property list mapping a small number of -fixed-sized key strings to an arbitrary uintptr_t value. The -uintptr_t type is used to ensure that the value can be a pointer or a -small string upto sizeof(uintptr_t) - 1 (for trailing nul). The big -problem is reclaiming the value if it a pointer. The fact that the -number of keys is small makes it feasible to use linear search. -This is currently only used for plugins, but may be extended to other uses. +keys to objects. The uintptr_t type is used to ensure that the value can be a pointer or a +small string upto sizeof(uintptr_t) - 1 (for trailing nul) or an integer constant. + +There are two operations that may be defined for the property: +1. reclaiming the value when proplist is free'd and property value points to allocated data of arbitrary complexity. +2. coping the value (for cloning) if it points to allocated data of arbitrary complexity. + +The fact that the number of keys is small makes it feasible to use +linear search. This is currently only used for plugins, but may be +extended to other uses. */ /*! Proplist-related structs. @@ -38,23 +42,40 @@ This is currently only used for plugins, but may be extended to other uses. 1. It is critical that |uintptr_t| == |void*| */ -#define NCPROPSMAXKEY 31 /* characters assert (NCPROPSMAXKEY+1)/8 == 0*/ +#define NCPROPSMAXKEY 31 /* characters; assert (NCPROPSMAXKEY+1)/8 == 0*/ -/* Returns 0 => error; 1 => success */ -typedef int (*NCPreclaimfcn)(uintptr_t userdata, const char* key, void* value, uintptr_t size); +/* Opaque forward */ +struct NCPpair; -/* The property list proper is a sequence of these objects */ -typedef struct NCProperty { +/* This function performs all of the following operations on a complex type */ +typedef enum NCPtypeop {NCP_RECLAIM=1,NCP_COPY=2} NCPtypeop; + +/* There are three possible types for a property value */ +typedef enum NCPtype { + NCP_CONST=0, /* Value is a simple uintptr_t constant */ + NCP_BYTES=2, /* Value points to a counted sequence of bytes; If a string, + then it includes the nul term character */ + NCP_COMPLEX=3 /* Value points to an arbitraryily complex structure */ +} NCPtype; + +/* (Returns < 0 => error) (>= 0 => success) */ +typedef int (*NCPtypefcn)(NCPtypeop op, struct NCPpair* input, struct NCPpair* output); + +/* Expose this prefix of NCProperty; used in clone and lookup */ +/* Hold just the key+value pair */ +typedef struct NCPpair { char key[NCPROPSMAXKEY+1]; /* copy of the key string; +1 for trailing nul */ - uintptr_t flags; -# define NCPF_SIMPLE (1<<0) /* non-reclaimable */ -# define NCPF_BYTES (1<<1) /* reclaimable bytes */ -# define NCPF_COMPLEX (1<<2) /* extended case */ + NCPtype sort; uintptr_t value; uintptr_t size; /* size = |value| as ptr to memory, if string, then include trailing nul */ - uintptr_t userdata; /* extra data for following functions */ - NCPreclaimfcn reclaim; -} NCProperty; +} NCPpair; + +/* The property list proper is a sequence of these objects */ +typedef struct NCPproperty { + NCPpair pair; /* Allowed by C language standard */ + uintptr_t userdata; /* extra data for the type function */ + NCPtypefcn typefcn; /* Process type operations */ +} NCPproperty; /* The property list object. @@ -62,7 +83,7 @@ The property list object. typedef struct NCproplist { size_t alloc; /* allocated space to hold properties */ size_t count; /* # of defined properties */ - NCProperty* properties; + NCPproperty* properties; } NCproplist; /**************************************************/ @@ -72,19 +93,24 @@ typedef struct NCproplist { extern "C" { #endif +/* All int valued functions return < 0 if error; >= 0 otherwise */ + + /* Create, free, etc. */ OPTEXPORT NCproplist* ncproplistnew(void); OPTEXPORT int ncproplistfree(NCproplist*); -/* Locate a proplist entry */ -OPTEXPORT int ncproplistadd(NCproplist* plist,const char* key, uintptr_t value); /* use when reclaim not needed */ - /* Insert properties */ OPTEXPORT int ncproplistadd(NCproplist* plist,const char* key, uintptr_t value); /* use when reclaim not needed */ OPTEXPORT int ncproplistaddstring(NCproplist* plist, const char* key, const char* str); /* use when value is simple string (char*) */ -OPTEXPORT int ncproplistaddbytes(NCproplist* plist, const char* key, void* value, uintptr_t size); /* use when value is simple ptr and reclaim is simple free function */ -OPTEXPORT int ncproplistaddx(NCproplist* plist, const char* key, void* value, uintptr_t size, uintptr_t userdata, NCPreclaimfcn); /* fully extended case */ +/* Insert an instance of type NCP_BYTES */ +OPTEXPORT int ncproplistaddbytes(NCproplist* plist, const char* key, void* value, uintptr_t size); + +/* Add instance of a complex type */ +OPTEXPORT int ncproplistaddx(NCproplist* plist, const char* key, void* value, uintptr_t size, uintptr_t userdata, NCPtypefcn typefcn); + +/* clone; keys are copies and values are copied using the NCPtypefcn */ OPTEXPORT int ncproplistclone(const NCproplist* src, NCproplist* clone); /* @@ -105,4 +131,4 @@ OPTEXPORT int ncproplistith(const NCproplist*, size_t i, char* const * keyp, uin } #endif -#endif /*NCPROPLIST_H*/ +#endif /*!NCPROPLIST_H*/ /* WARNING: Do not remove the !; used in building netcdf_proplist.h */ diff --git a/libdispatch/ncjson.c b/libdispatch/ncjson.c index 6a9b46cc6d..377b714381 100644 --- a/libdispatch/ncjson.c +++ b/libdispatch/ncjson.c @@ -6,9 +6,18 @@ TODO: make utf8 safe */ +/* +WARNING: +If you modify this file, +then you need to go to +the include/ directory +and do the command: + make netcdf_json.h +*/ + #ifdef HAVE_CONFIG_H #include "config.h" -#endif +#endif /*HAVE_CONFIG_H*/ #include #include #include @@ -16,20 +25,19 @@ TODO: make utf8 safe #include "ncjson.h" +#undef NCJCATCH #undef NCJDEBUG -#define NCJTRACE +#undef NCJTRACE -#ifdef NCJDEBUG +#ifdef NCJCATCH /* Warning: do not evaluate err more than once */ #define NCJTHROW(err) ncjbreakpoint(err) static int ncjbreakpoint(int err) {return err;} -#else +#else /*!NCJCATCH*/ #define NCJTHROW(err) (err) -#endif +#endif /*NCJCATCH*/ /**************************************************/ -#define NCJ_OK 0 -#define NCJ_ERR (-1) #define NCJ_EOF -2 @@ -48,6 +56,8 @@ static int ncjbreakpoint(int err) {return err;} /* JSON_WORD Subsumes Number also */ #define JSON_WORD "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$+-." +#define NCJ_DEFAULTALLOC 16 + /**************************************************/ typedef struct NCJparser { char* text; @@ -61,6 +71,7 @@ typedef struct NCJparser { # define NCJ_TRACE 1 } NCJparser; +/* This is used only by the unparser */ typedef struct NCJbuf { size_t len; /* |text|; does not include nul terminator */ char* text; /* NULL || nul terminated */ @@ -71,20 +82,20 @@ typedef struct NCJbuf { #if defined(_WIN32) && !defined(__MINGW32__) #define strdup _strdup #define strcasecmp _stricmp -#else +#else /*!WIN32 || __MINGW32*/ #include -#endif +#endif /*defined(_WIN32) && !defined(__MINGW32__)*/ #ifndef nullfree #define nullfree(x) {if(x)free(x);} -#endif +#endif /*nullfree*/ #ifndef nulldup #define nulldup(x) ((x)?strdup(x):(x)) -#endif +#endif /*nulldup*/ #if defined NCJDEBUG || defined NCJTRACE static char* tokenname(int token); -#endif +#endif /*defined NCJDEBUG || defined NCJTRACE*/ /**************************************************/ /* Forward */ @@ -101,39 +112,38 @@ static void NCJreclaimArray(struct NCjlist*); static void NCJreclaimDict(struct NCjlist*); static int NCJunescape(NCJparser* parser); static char unescape1(char c); + static int listappend(struct NCjlist* list, NCjson* element); +static int listsetalloc(struct NCjlist* list, size_t sz); +static int listlookup(const struct NCjlist* list, const char* key, size_t* indexp); static int NCJcloneArray(const NCjson* array, NCjson** clonep); static int NCJcloneDict(const NCjson* dict, NCjson** clonep); + +/* These are used only by the unparser */ static int NCJunparseR(const NCjson* json, NCJbuf* buf, unsigned flags); static int bytesappendquoted(NCJbuf* buf, const char* s); static int bytesappend(NCJbuf* buf, const char* s); static int bytesappendc(NCJbuf* bufp, char c); -/* Static'ize everything for plugins */ +/* Hide everything for plugins */ #ifdef NETCDF_JSON_H #define OPTSTATIC static -static int NCJparsen(size_t len, const char* text, unsigned flags, NCjson** jsonp); -static int NCJnew(int sort, NCjson** objectp); -static int NCJnewstring(int sort, const char* value, NCjson** jsonp); -static int NCJnewstringn(int sort, size_t len, const char* value, NCjson** jsonp); -static int NCJclone(const NCjson* json, NCjson** clonep); -static int NCJaddstring(NCjson* json, int sort, const char* s); -static int NCJinsert(NCjson* object, const char* key, NCjson* jvalue); -static int NCJinsertstring(NCjson* object, const char* key, const char* value); -static int NCJinsertint(NCjson* object, const char* key, long long ivalue); -static int NCJappend(NCjson* object, NCjson* value); -static int NCJunparse(const NCjson* json, unsigned flags, char** textp); #else /*!NETCDF_JSON_H*/ #define OPTSTATIC #endif /*NETCDF_JSON_H*/ +/* List legal nan and infinity names (lower case); keep in strcasecmp sorted order */ +static const char* NANINF[] = {"-infinity","infinity","-infinityf","infinityf","nan","nanf"}; +static const size_t NNANINF = 6; + /**************************************************/ OPTSTATIC int NCJparse(const char* text, unsigned flags, NCjson** jsonp) { - return NCJparsen(strlen(text),text,flags,jsonp); + size_t textlen = strlen(text); + return NCJparsen(textlen,text,flags,jsonp); } OPTSTATIC int @@ -167,7 +177,7 @@ NCJparsen(size_t len, const char* text, unsigned flags, NCjson** jsonp) parser->status = NCJ_OK; #ifdef NCJDEBUG fprintf(stderr,"json: |%s|\n",parser->text); -#endif +#endif /*NCJDEBUG*/ if((stat=NCJparseR(parser,&json))==NCJ_ERR) goto done; /* Must consume all of the input */ if(parser->pos != (parser->text+len)) {stat = NCJ_ERR; goto done;} @@ -375,7 +385,7 @@ NCJlex(NCJparser* parser) } else if(c == NCJ_ESCAPE) { parser->pos++; c = *parser->pos; - *parser->pos = unescape1(c); + *parser->pos = (char)unescape1(c); continue; } else if(strchr(JSON_WORD, c) != NULL) { start = parser->pos; @@ -388,14 +398,14 @@ NCJlex(NCJparser* parser) count = (size_t)((parser->pos) - start); if(NCJyytext(parser,start,count)) goto done; /* Discriminate the word string to get the proper sort */ - if(testbool(parser->yytext) == NCJ_OK) + if(testbool(parser->yytext)) token = NCJ_BOOLEAN; /* do int test first since double subsumes int */ - else if(testint(parser->yytext) == NCJ_OK) + else if(testint(parser->yytext)) token = NCJ_INT; - else if(testdouble(parser->yytext) == NCJ_OK) + else if(testdouble(parser->yytext)) token = NCJ_DOUBLE; - else if(testnull(parser->yytext) == NCJ_OK) + else if(testnull(parser->yytext)) token = NCJ_NULL; else token = NCJ_STRING; @@ -422,7 +432,7 @@ NCJlex(NCJparser* parser) } #ifdef NCJDEBUG fprintf(stderr,"%s(%d): |%s|\n",tokenname(token),token,parser->yytext); -#endif +#endif /*NCJDEBUG*/ } /*for(;;)*/ done: if(parser->status == NCJ_ERR) @@ -436,16 +446,15 @@ fprintf(stderr,"%s(%d): |%s|\n",tokenname(token),token,parser->yytext); } fprintf(stderr,">>>> token=%s:'%s'\n",tokenname(token),(txt?txt:"")); } -#endif +#endif /*NCJTRACE*/ return token; } static int testnull(const char* word) { - if(strcasecmp(word,NCJ_TAG_NULL)==0) - return NCJTHROW(NCJ_OK); - return NCJTHROW(NCJ_ERR); + if(strcasecmp(word,NCJ_TAG_NULL)==0) return 1; + return 0; } static int @@ -453,8 +462,8 @@ testbool(const char* word) { if(strcasecmp(word,NCJ_TAG_TRUE)==0 || strcasecmp(word,NCJ_TAG_FALSE)==0) - return NCJTHROW(NCJ_OK); - return NCJTHROW(NCJ_ERR); + return 1; + return 0; } static int @@ -465,7 +474,17 @@ testint(const char* word) int count = 0; /* Try to convert to number */ ncvt = sscanf(word,"%lld%n",&i,&count); - return NCJTHROW((ncvt == 1 && strlen(word)==(size_t)count ? NCJ_OK : NCJ_ERR)); + return (ncvt == 1 && strlen(word)==((size_t)count) ? 1 : 0); +} + +static int +nancmp(const void* keyp, const void* membpp) +{ + int cmp; + const char* key = (const char*)keyp; + const char** membp = (const char**)membpp; + cmp = strcasecmp(key,*membp); + return cmp; } static int @@ -474,17 +493,14 @@ testdouble(const char* word) int ncvt; double d; int count = 0; + void* pos = NULL; + /* Check for Nan and Infinity */ - if(0==(int)strcasecmp("nan",word)) return NCJTHROW(NCJ_OK); - if(0==(int)strcasecmp("infinity",word)) return NCJTHROW(NCJ_OK); - if(0==(int)strcasecmp("-infinity",word)) return NCJTHROW(NCJ_OK); - /* Allow the XXXf versions as well */ - if(0==(int)strcasecmp("nanf",word)) return NCJTHROW(NCJ_OK); - if(0==(int)strcasecmp("infinityf",word)) return NCJTHROW(NCJ_OK); - if(0==(int)strcasecmp("-infinityf",word)) return NCJTHROW(NCJ_OK); + pos = bsearch(word, NANINF, NNANINF, sizeof(char*), nancmp); + if(pos != NULL) return 1; /* Try to convert to number */ - ncvt = sscanf(word,"%lg%n",&d,&count); - return NCJTHROW((ncvt == 1 && strlen(word)==(size_t)count ? NCJ_OK : NCJ_ERR)); + ncvt = sscanf(word,"%lg%n",&d,&count); + return (ncvt == 1 && strlen(word)==((size_t)count) ? 1 : 0); } static int @@ -537,6 +553,7 @@ NCJreclaimArray(struct NCjlist* array) } nullfree(array->contents); array->contents = NULL; + array->len = 0; } static void @@ -593,8 +610,7 @@ NCJnewstringn(int sort, size_t len, const char* value, NCjson** jsonp) if(jsonp) *jsonp = NULL; if(value == NULL) {stat = NCJTHROW(NCJ_ERR); goto done;} - if((stat = NCJnew(sort,&json))==NCJ_ERR) - goto done; + if((stat = NCJnew(sort,&json))==NCJ_ERR) goto done; if((json->string = (char*)malloc(len+1))==NULL) {stat = NCJTHROW(NCJ_ERR); goto done;} memcpy(json->string,value,len); @@ -607,18 +623,20 @@ NCJnewstringn(int sort, size_t len, const char* value, NCjson** jsonp) } OPTSTATIC int -NCJdictget(const NCjson* dict, const char* key, const NCjson** valuep) +NCJdictget(const NCjson* dict, const char* key, const NCjson** jvaluep) { int stat = NCJ_OK; size_t i; if(dict == NULL || dict->sort != NCJ_DICT) {stat = NCJTHROW(NCJ_ERR); goto done;} - if(valuep) {*valuep = NULL;} - for(i=0;istring != NULL && strcmp(jkey->string,key)==0) { - if(valuep) {*valuep = NCJith(dict,i+1); break;} + if(jvaluep) {*jvaluep = jvalue;} + break; } } @@ -626,6 +644,17 @@ NCJdictget(const NCjson* dict, const char* key, const NCjson** valuep) return NCJTHROW(stat); } +/* Functional version of NCJdictget */ +OPTSTATIC NCjson* +NCJdictlookup(const NCjson* dict, const char* key) +{ + int stat; + const NCjson* jvalue = NULL; + stat = NCJdictget(dict,key,&jvalue); + if(stat != NCJ_OK) jvalue = NULL; + return jvalue; +} + /* Unescape the text in parser->yytext; can do in place because unescaped string will always be shorter */ @@ -649,7 +678,7 @@ NCJunescape(NCJparser* parser) default: break;/* technically not Json conformant */ } } - *q++ = c; + *q++ = (char)c; } *q = '\0'; return NCJTHROW(NCJ_OK); @@ -695,7 +724,7 @@ tokenname(int token) } return ("NCJ_UNDEF"); } -#endif +#endif /*defined NCJDEBUG || defined NCJTRACE*/ /* Convert a JSON value to an equivalent value of a specified sort */ OPTSTATIC int @@ -723,7 +752,7 @@ NCJcvt(const NCjson* jvalue, int outsort, struct NCJconst* output) break; case CASE(NCJ_INT,NCJ_BOOLEAN): - sscanf(jvalue->string,"%lldd",&output->ival); + sscanf(jvalue->string,"%lld",&output->ival); output->bval = (output->ival?1:0); break; case CASE(NCJ_INT,NCJ_INT): @@ -778,30 +807,72 @@ static int listappend(struct NCjlist* list, NCjson* json) { int stat = NCJ_OK; - NCjson** newcontents = NULL; assert(list->len == 0 || list->contents != NULL); - if(json == NULL) - {stat = NCJTHROW(NCJ_ERR); goto done;} - if(list->len == 0) { - nullfree(list->contents); - list->contents = (NCjson**)calloc(2,sizeof(NCjson*)); - if(list->contents == NULL) - {stat = NCJTHROW(NCJ_ERR); goto done;} - list->contents[0] = json; - list->len++; - } else { - if((newcontents = (NCjson**)calloc((size_t)(2*list->len)+1,sizeof(NCjson*)))==NULL) - {stat = NCJTHROW(NCJ_ERR); goto done;} - memcpy(newcontents,list->contents, (size_t)list->len*sizeof(NCjson*)); - newcontents[list->len] = json; - list->len++; - free(list->contents); - list->contents = newcontents; newcontents = NULL; + if(json == NULL) {stat = NCJTHROW(NCJ_ERR); goto done;} + /* Make space for two new elements; better than one, but still probably not optimal */ + if((stat = listsetalloc(list,list->len + 2))<0) goto done; + /* Append the new item */ + list->contents[list->len++] = json; + +done: + return NCJTHROW(stat); +} + +/* Locate the index of a key in a list of (key,value) pairs. +@param list pointer to the list +@param key for which to search +@param indexp store index of match here +@return NCJ_OK if key found, NCJ_EOF if not found, NCJ_ERR if error +*/ +static int +listlookup(const struct NCjlist* list, const char* key, size_t* indexp) +{ + int stat = NCJ_OK; + int i,len,match = -1; + + if(list == NULL || key == NULL || strlen(key) == 0 || list->len %2 == 1) + {stat = NCJTHROW(NCJ_ERR); goto done;} + len = (int)list->len; /* => |list| < 2 billion or do */ + for(i=0;icontents[i]; + if(jkey != NULL && jkey->string != NULL && strcmp(jkey->string,key)==0) {match = i;break;} } + if(match < 0) {stat = NCJ_EOF;} else {if(indexp) *indexp = (size_t)match;} +done: + return NCJTHROW(stat); +} +/* Increase the space available to dict/array. + Even if alloc is zero, ensure that the object's list alloc is >= 1. +@param list pointer to the list +@param alloc increase allocation to this size +@return NCJ_ERR|NCJ_OK +*/ +static int +listsetalloc(struct NCjlist* list, size_t alloc) +{ + int stat = NCJ_OK; + NCjson** newcontents = NULL; + + if(list == NULL) {stat = NCJTHROW(NCJ_ERR); goto done;} + assert(list->alloc == 0 || list->contents != NULL); + if(alloc == 0) alloc = 1; /* Guarantee that the list->content is not NULL */ + if(list->alloc >= alloc) goto done; + /* Since alloc > list->alloc > 0, we need to allocate space */ + if((newcontents=(NCjson**)calloc(alloc,sizeof(NCjson*))) == NULL) {stat = NCJTHROW(NCJ_ERR); goto done;} + list->alloc = alloc; + if(list->contents != NULL && list->len > 0) { + /* Preserve any existing contents */ + memcpy((void*)newcontents, + (void*)list->contents, + sizeof(NCjson*)*list->len); + } + free(list->contents); + list->contents = newcontents; newcontents = NULL; + assert(list->alloc > 0 && list->contents != NULL); done: - nullfree(newcontents); + if(newcontents != NULL) free(newcontents); return NCJTHROW(stat); } @@ -812,6 +883,8 @@ NCJclone(const NCjson* json, NCjson** clonep) { int stat = NCJ_OK; NCjson* clone = NULL; + + if(clonep) *clonep = NULL; if(json == NULL) goto done; switch(NCJsort(json)) { case NCJ_INT: @@ -819,7 +892,8 @@ NCJclone(const NCjson* json, NCjson** clonep) case NCJ_BOOLEAN: case NCJ_STRING: if((stat=NCJnew(NCJsort(json),&clone))==NCJ_ERR) goto done; - if((NCJstring(clone) = strdup(NCJstring(json))) == NULL) + NCJsetstring(clone,strdup(NCJstring(json))); + if(NCJstring(clone)==NULL) {stat = NCJTHROW(NCJ_ERR); goto done;} break; case NCJ_NULL: @@ -846,7 +920,8 @@ NCJcloneArray(const NCjson* array, NCjson** clonep) size_t i; NCjson* clone = NULL; if((stat=NCJnew(NCJ_ARRAY,&clone))==NCJ_ERR) goto done; - for(i=0;ilist,array->list.len))<0) goto done; + for(i=0;ilist,dict->list.len))<0) goto done; + for(i=0;isort != NCJ_DICT || key == NULL || jvalue == NULL) - {stat = NCJTHROW(NCJ_ERR); goto done;} - if((stat = NCJnewstring(NCJ_STRING,key,&jkey))==NCJ_ERR) goto done; - if((stat = NCJappend(object,jkey))==NCJ_ERR) goto done; - if((stat = NCJappend(object,jvalue))==NCJ_ERR) goto done; + NCjson* jprev = NULL; + int found; + + if(jdict == NULL + || NCJsort(jdict) != NCJ_DICT + || key == NULL + || jvalue == NULL) {stat = NCJTHROW(NCJ_ERR); goto done;} + for(found=(-1),i=0;i < NCJdictlength(jdict); i++) { + jkey = NCJdictkey(jdict,i); + if (jkey != NULL && strcmp(NCJstring(jkey), key) == 0) { + found = (int)i; + break; + } + } + if(found >= 0) { + jprev = NCJdictvalue(jdict,found); + // replace existing values for new key + NCJreclaim(jprev); // free old value + NCJdictvalue(jdict,found) = jvalue; jvalue = NULL; + jkey = NULL; /* avoid reclamation */ + } else { /* not found */ + if((stat=listsetalloc(&jdict->list,jdict->list.len+2))<0) goto done; + NCJcheck(NCJnewstring(NCJ_STRING, key, (NCjson**)&jkey)); + NCJcheck(NCJappend(jdict,jkey)); jkey = NULL; + NCJcheck(NCJappend(jdict,jvalue)); jvalue = NULL; + } done: + NCJreclaim(jkey); + NCJreclaim(jvalue); return NCJTHROW(stat); } -/* Insert key-value pair as strings into a dict object. - key and value will be strdup'd */ +/* Insert key-value pair into a dict object. key will be strdup'd */ OPTSTATIC int NCJinsertstring(NCjson* object, const char* key, const char* value) { int stat = NCJ_OK; + NCjson* jkey = NULL; NCjson* jvalue = NULL; - if(value == NULL) - NCJnew(NCJ_NULL,&jvalue); - else - NCJnewstring(NCJ_STRING,value,&jvalue); - NCJinsert(object,key,jvalue); + if(key == NULL || value == NULL) + {stat = NCJTHROW(NCJ_ERR); goto done;} + if((stat = NCJnewstring(NCJ_STRING,key,&jkey))==NCJ_ERR) goto done; + if((stat = NCJnewstring(NCJ_STRING,value,&jvalue))==NCJ_ERR) goto done; + if((stat = NCJappend(object,jkey))==NCJ_ERR) goto done; + if((stat = NCJappend(object,jvalue))==NCJ_ERR) goto done; +done: return NCJTHROW(stat); } -/* Insert key-value pair with value being an integer */ +/* Insert key-value pair into a dict object. key will be strdup'd */ OPTSTATIC int -NCJinsertint(NCjson* object, const char* key, long long ivalue) +NCJinsertint(NCjson* object, const char* key, long long value) { int stat = NCJ_OK; + NCjson* jkey = NULL; NCjson* jvalue = NULL; - char digits[128]; - snprintf(digits,sizeof(digits),"%lld",ivalue); - NCJnewstring(NCJ_STRING,digits,&jvalue); - NCJinsert(object,key,jvalue); + char digits[64]; + + if(key == NULL) + {stat = NCJTHROW(NCJ_ERR); goto done;} + if((stat = NCJnewstring(NCJ_STRING,key,&jkey))==NCJ_ERR) goto done; + snprintf(digits,sizeof(digits),"%lld",value); + if((stat = NCJnewstring(NCJ_INT,digits,&jvalue))==NCJ_ERR) goto done; + listsetalloc(&object->list,object->list.len + 2); + if((stat = NCJappend(object,jkey))==NCJ_ERR) goto done; + if((stat = NCJappend(object,jvalue))==NCJ_ERR) goto done; +done: return NCJTHROW(stat); } @@ -954,6 +1066,76 @@ NCJappend(NCjson* object, NCjson* value) return NCJTHROW(NCJ_OK); } +/* Append string value to an array or dict object. */ +OPTSTATIC int +NCJappendstring(NCjson* object, int sort, const char* s) +{ + NCjson* js = NULL; + if(object == NULL || s == NULL) + return NCJTHROW(NCJ_ERR); + NCJnewstring(sort,s,&js); + switch (object->sort) { + case NCJ_ARRAY: + case NCJ_DICT: + listappend(&object->list,js); + break; + default: + return NCJTHROW(NCJ_ERR); + } + return NCJTHROW(NCJ_OK); +} + +/* Append int value into an array/dict object. */ +OPTSTATIC int +NCJappendint(NCjson* object, long long value) +{ + int stat = NCJ_OK; + NCjson* jvalue = NULL; + char digits[64]; + + snprintf(digits,sizeof(digits),"%lld",value); + NCJcheck(NCJnewstring(NCJ_INT,digits,&jvalue)); + NCJcheck(NCJappend(object,jvalue)); jvalue = NULL; +done: + NCJreclaim(jvalue); + return NCJTHROW(stat); +} + +/* Overwrite key-value pair in a dict object. + If key does not exist, then act like NCJinsert(). +*/ +OPTSTATIC int +NCJoverwrite(NCjson* dict, const char* key, NCjson* jvalue) +{ + int stat = NCJ_OK; + size_t index; + NCjson* jkey = NULL; + NCjson* oldvalue = NULL; + + if(dict == NULL + || dict->sort != NCJ_DICT + || key == NULL + || jvalue == NULL) {stat = NCJTHROW(NCJ_ERR); goto done;} + /* See if key already exists */ + switch(stat=listlookup(&dict->list,key,&index)) { + case NCJ_OK: + /* Overwrite value part */ + oldvalue = dict->list.contents[index]; + dict->list.contents[index] = jvalue; + NCJreclaim(oldvalue); + break; + case NCJ_EOF: /* Not found */ + if((stat=listsetalloc(&dict->list,dict->list.len+2))<0) goto done; + if((stat = NCJappend(dict,jkey))==NCJ_ERR) goto done; + if((stat = NCJappend(dict,jvalue))==NCJ_ERR) goto done; + break; + case NCJ_ERR: + default: goto done; + } +done: + return NCJTHROW(stat); +} + /**************************************************/ /* Unparser to convert NCjson object to text in buffer */ @@ -1047,7 +1229,7 @@ escape(const char* text, NCJbuf* buf) bytesappendc(buf,NCJ_ESCAPE); bytesappendc(buf,replace); } else - bytesappendc(buf,c); + bytesappendc(buf,(char)c); } return NCJTHROW(NCJ_OK); } @@ -1101,22 +1283,12 @@ bytesappendc(NCJbuf* bufp, const char c) return bytesappend(bufp,s); } -OPTSTATIC void -NCJdump(const NCjson* json, unsigned flags, FILE* out) -{ - char* text = NULL; - (void)NCJunparse(json,0,&text); - if(out == NULL) out = stderr; - fprintf(out,"%s\n",text); - fflush(out); - nullfree(text); -} - OPTSTATIC const char* -NCJtotext(const NCjson* json) +NCJtotext(const NCjson* json, unsigned flags) { static char outtext[4096]; char* text = NULL; + NC_UNUSED(flags); if(json == NULL) {strcpy(outtext,""); goto done;} (void)NCJunparse(json,0,&text); strncpy(outtext,text,sizeof(outtext)); @@ -1125,20 +1297,54 @@ NCJtotext(const NCjson* json) return outtext; } +OPTSTATIC void +NCJdump(const NCjson* json, unsigned flags, FILE* out) +{ + const char* text = NCJtotext(json,flags); + if(out == NULL) out = stderr; + fprintf(out,"%s\n",text); + fflush(out); +} + +static int +pairsort(const void* a, const void* b) +{ + const NCjson** j1 = (const NCjson**)a; + const NCjson** j2 = (const NCjson**)b; + return strcmp(NCJstring(*j1),NCJstring(*j2)); +} + +OPTSTATIC void +NCJdictsort(NCjson* jdict) +{ + assert(NCJsort(jdict) == NCJ_DICT); + qsort((void*)NCJcontents(jdict),NCJdictlength(jdict),2*sizeof(NCjson*),pairsort); +} + /* Hack to avoid static unused warning */ static void netcdf_supresswarnings(void) { - void* ignore; + void* ignore = NULL; ignore = (void*)netcdf_supresswarnings; - ignore = (void*)NCJinsert; - ignore = (void*)NCJaddstring; - ignore = (void*)NCJcvt; - ignore = (void*)NCJdictget; ignore = (void*)NCJparse; - ignore = (void*)NCJdump; - ignore = (void*)NCJtotext; + ignore = (void*)NCJparsen; + ignore = (void*)NCJreclaim; + ignore = (void*)NCJnew; + ignore = (void*)NCJnewstring; + ignore = (void*)NCJnewstringn; + ignore = (void*)NCJdictget; + ignore = (void*)NCJdictlookup; + ignore = (void*)NCJcvt; + ignore = (void*)NCJaddstring; + ignore = (void*)NCJappend; + ignore = (void*)NCJappendstring; + ignore = (void*)NCJappendint; + ignore = (void*)NCJinsert; ignore = (void*)NCJinsertstring; ignore = (void*)NCJinsertint; + ignore = (void*)NCJoverwrite; + ignore = (void*)NCJdictsort; + ignore = (void*)NCJdump; (void)ignore; } diff --git a/libdispatch/ncproplist.c b/libdispatch/ncproplist.c index 77d8023a00..1801ec54c8 100644 --- a/libdispatch/ncproplist.c +++ b/libdispatch/ncproplist.c @@ -27,7 +27,15 @@ #endif /**************************************************/ - +/* Hide everything for plugins */ +#ifdef NETCDF_PROPLIST_H +#define OPTSTATIC static +#else /*!NETCDF_PROPLIST_H*/ +#define OPTSTATIC +#endif /*NETCDF_PROPLIST_H*/ + +/**************************************************/ + #define MINPROPS 2 #define EXPANDFACTOR 1 @@ -49,7 +57,7 @@ static int ncproplistfree(NCproplist* plist); static int ncproplistadd(NCproplist* plist, const char* key, uintptr_t value); static int ncproplistaddbytes(NCproplist* plist, const char* key, void* value, uintptr_t size); static int ncproplistaddstring(NCproplist* plist, const char* key, const char* str); -static int ncproplistaddx(NCproplist* plist, const char* key, void* value, uintptr_t size, uintptr_t userdata, NCPreclaimfcn fcn); +static int ncproplistaddx(NCproplist* plist, const char* key, void* value, uintptr_t size, uintptr_t userdata, NCPtypefcn fcn); static int ncproplistclone(const NCproplist* src, NCproplist* clone); static int ncproplistget(const NCproplist* plist, const char* key, uintptr_t* valuep, uintptr_t* sizep); static int ncproplistith(const NCproplist* plist, size_t i, char* const * keyp, uintptr_t const * valuep, uintptr_t* sizep); @@ -59,7 +67,7 @@ static int ncproplistith(const NCproplist* plist, size_t i, char* const * keyp, /** - * Create new property list + * Create new empty property list * @return pointer to the created property list. */ OPTSTATIC NCproplist* @@ -73,151 +81,175 @@ ncproplistnew(void) } /** - * Reclaim memory used by a property list - * @param plist to reclaim - * @return NC_NOERR if succeed, NC_EXXX otherwise. + * Reclaim property pairs used by a property list + * @param plist to clear + * @return >= 0 if succeed, < 0 if fail */ -OPTSTATIC int -ncproplistfree(NCproplist* plist) +static int +ncproplistclear(NCproplist* plist) { - int stat = NC_NOERR; + int stat = 0; size_t i; if(plist == NULL) goto done; if(plist->properties != NULL) { for(i=0;icount;i++) { - NCProperty* prop = &plist->properties[i]; - void* ptr = (void*)prop->value; /* convert to ptr */ - assert(prop->flags & (NCPF_SIMPLE|NCPF_BYTES|NCPF_COMPLEX)); - if(prop->flags & NCPF_SIMPLE) continue; /* no reclaim needed */ - if(prop->flags & NCPF_BYTES) { + NCPproperty* prop = &plist->properties[i]; + void* ptr = (void*)prop->pair.value; /* convert to ptr */ + switch (prop->pair.sort) { + case NCP_CONST: /* value need not be free'd */ + break; + case NCP_BYTES: /* simple free of the value */ if(ptr != NULL) free(ptr); - } else { /* (prop->flags & NCPF_COMPLEX) */ - int ok; - assert(prop->reclaim != NULL); - ok = prop->reclaim(prop->userdata, prop->key, ptr, prop->size); - if(!ok && stat == NC_NOERR) stat = NC_EINVAL; + break; + case NCP_COMPLEX: /* Need the typeop fcn */ + assert(prop->typefcn != NULL); + stat = prop->typefcn(NCP_RECLAIM,&prop->pair,NULL); + if(stat < 0) goto done; + break; } } - free(plist->properties); } + plist->count = 0; +done: + return stat; +} + +/** + * Reclaim memory used by a property list + * @param plist to reclaim + * @return >= 0 if succeed, < 0 if fail + */ +OPTSTATIC int +ncproplistfree(NCproplist* plist) +{ + int stat = 0; + if(plist == NULL) goto done; + if((stat = ncproplistclear(plist))<0) goto done; + free(plist->properties); free(plist); done: return stat; } /** - * Add a non-reclaimable entry to the property list + * Add an NCP_CONST entry to the property list * @param plist into which the value is be inserted. * @param key * @param value - * @return NC_NOERR if succeed, NC_EXXX otherwise. + * @return >= 0 if succeed, < 0 if fail */ OPTSTATIC int ncproplistadd(NCproplist* plist, const char* key, uintptr_t value) { int stat = NC_NOERR; - NCProperty* prop = NULL; + NCPproperty* prop = NULL; size_t keylen; if(plist == NULL) goto done; if(!hasspace(plist,1)) {if((stat = extendplist(plist,(plist->count+1)*EXPANDFACTOR))) goto done;} /* extra space */ prop = &plist->properties[plist->count]; keylen = strlen(key); if(keylen > NCPROPSMAXKEY) keylen = NCPROPSMAXKEY; /* truncate */ - memcpy(prop->key,key,keylen); - prop->key[keylen] = '\0'; - prop->value = value; - prop->flags = NCPF_SIMPLE; + memcpy(prop->pair.key,key,keylen); + prop->pair.key[keylen] = '\0'; + prop->pair.value = value; + prop->pair.sort = NCP_CONST; plist->count++; done: return stat; } - + /** - * Add a reclaimable entry to the property list, where the value - * can be reclaimed using a simple free(); + * Add a byte string to the property list. + * The proplist takes control of the value => do not free. * @param plist into which the value is be inserted. * @param key * @param value ptr to memory chunk - * @param size |*value| - * @return NC_NOERR if succeed, NC_EXXX otherwise. + * @param size |value| + * @return >= 0 if succeed, < 0 if fail */ OPTSTATIC int ncproplistaddbytes(NCproplist* plist, const char* key, void* value, uintptr_t size) { int stat = NC_NOERR; - NCProperty* prop = NULL; + NCPproperty* prop = NULL; size_t keylen; + + NC_UNUSED(size); if(plist == NULL) goto done; if(!hasspace(plist,1)) {if((stat = extendplist(plist,(plist->count+1)*EXPANDFACTOR))) goto done;} /* extra space */ prop = &plist->properties[plist->count]; keylen = strlen(key); if(keylen > NCPROPSMAXKEY) keylen = NCPROPSMAXKEY; /* truncate */ - memcpy(prop->key,key,keylen); - prop->key[keylen] = '\0'; - prop->value = (uintptr_t)value; - prop->flags = NCPF_BYTES; + memcpy(prop->pair.key,key,keylen); + prop->pair.key[keylen] = '\0'; + prop->pair.value = (uintptr_t)value; + prop->pair.sort = NCP_BYTES; plist->count++; done: return stat; } /** - * Add a reclaimable entry to the property list, where the value - * can be reclaimed using a simple free(); + * Add a nul terminated string to the property list. + * Wraps ncproplistaddbytes. + * The proplist takes control of the value => do not free. * @param plist into which the value is be inserted. * @param key - * @param value ptr to memory chunk - * @param size |*value| - * @return NC_NOERR if succeed, NC_EXXX otherwise. + * @param value ptr to char* string + * @param size strlen(value)+1 + * @return >= 0 if succeed, < 0 if fail. */ OPTSTATIC int ncproplistaddstring(NCproplist* plist, const char* key, const char* str) { uintptr_t size = 0; - if(str) size = (uintptr_t)strlen(str); + if(str) size = (uintptr_t)(strlen(str)+1); return ncproplistaddbytes(plist,key,(void*)str,size); } /** * Most general case for adding a property. + * The value is always a ptr to some arbitrary complex structure. + * The proplist takes control of the value => do not free. * @param plist into which the value is be inserted. * @param key * @param value * @param size * @param userdata extra environment data for the reclaim function. - * @param fcn the reclaim function - * @return NC_NOERR if succeed, NC_EXXX otherwise. + * @param fcn the type operations function + * @return >= 0 if succeed, < 0 otherwise. */ OPTSTATIC int -ncproplistaddx(NCproplist* plist, const char* key, void* value, uintptr_t size, uintptr_t userdata, NCPreclaimfcn fcn) +ncproplistaddx(NCproplist* plist, const char* key, void* value, uintptr_t size, uintptr_t userdata, NCPtypefcn fcn) { int stat = NC_NOERR; - NCProperty* prop = NULL; + NCPproperty* prop = NULL; size_t keylen; if(plist == NULL) goto done; if(!hasspace(plist,1)) {if((stat = extendplist(plist,(plist->count+1)*EXPANDFACTOR))) goto done;} /* extra space */ prop = &plist->properties[plist->count]; keylen = strlen(key); if(keylen > NCPROPSMAXKEY) keylen = NCPROPSMAXKEY; /* truncate */ - memcpy(prop->key,key,keylen); - prop->key[keylen] = '\0'; - prop->value = (uintptr_t)value; - prop->size = size; - prop->reclaim = fcn; + memcpy(prop->pair.key,key,keylen); + prop->pair.key[keylen] = '\0'; + prop->pair.value = (uintptr_t)value; + prop->pair.size = size; + prop->typefcn = fcn; prop->userdata = userdata; - prop->flags = NCPF_COMPLEX; + prop->pair.sort = NCP_COMPLEX; plist->count++; done: return stat; } +/* Clone using the NCtypefcn to copy values */ OPTSTATIC int ncproplistclone(const NCproplist* src, NCproplist* clone) { int stat = NC_NOERR; size_t i; - NCProperty* srcprops; - NCProperty* cloneprops; + NCPproperty* srcprops; + NCPproperty* cloneprops; if(src == NULL || clone == NULL) {stat = NC_EINVAL; goto done;} if((stat=ncproplistinit(clone))) goto done; @@ -225,16 +257,25 @@ ncproplistclone(const NCproplist* src, NCproplist* clone) srcprops = src->properties; cloneprops = clone->properties; for(i=0;icount;i++) { - cloneprops[i] = srcprops[i]; - strncpy(cloneprops[i].key,srcprops[i].key,sizeof(cloneprops[i].key)); -#if 0 - cloneprops[i]->flags = srcprops[i]->flags; - cloneprops[i]->value = srcprops[i]->value; - cloneprops[i]->size = srcprops[i]->size; - cloneprops[i]->userdata = srcprops[i]->userdata; - cloneprops[i]->reclaim = srcprops->reclaim; -#endif + NCPproperty* sp = &srcprops[i]; + NCPproperty* cp = &cloneprops[i]; + void* p = NULL; + *cp = *sp; /* Do a mass copy of the property and then fixup as needed */ + switch (sp->pair.sort) { + case NCP_CONST: + break; + case NCP_BYTES: + p = malloc(cp->pair.size); + memcpy(p,(void*)sp->pair.value,sp->pair.size); + cp->pair.value = (uintptr_t)p; + break; + case NCP_COMPLEX: /* Need the typeop fcn */ + stat = sp->typefcn(NCP_COPY,&sp->pair,&cp->pair); + if(stat < 0) goto done; + break; + } } + clone->count = src->count; done: return stat; } @@ -245,10 +286,10 @@ extendplist(NCproplist* plist, size_t nprops) { int stat = NC_NOERR; size_t newsize = plist->count + nprops; - NCProperty* newlist = NULL; + NCPproperty* newlist = NULL; if((plist->alloc >= newsize) || (nprops == 0)) goto done; /* Already enough space */ - newlist = realloc(plist->properties,newsize*sizeof(NCProperty)); + newlist = realloc(plist->properties,newsize*sizeof(NCPproperty)); if(newlist == NULL) {stat = NC_ENOMEM; goto done;} plist->properties = newlist; newlist = NULL; plist->alloc = newsize; @@ -269,14 +310,14 @@ ncproplistget(const NCproplist* plist, const char* key, uintptr_t* valuep, uintp { int stat = NC_ENOOBJECT; /* assume not found til proven otherwise */ size_t i; - NCProperty* props; + NCPproperty* props; uintptr_t value = 0; uintptr_t size = 0; if(plist == NULL || key == NULL) goto done; for(i=0,props=plist->properties;icount;i++,props++) { - if(strcmp(props->key,key)==0) { - value = props->value; - size = props->size; + if(strcmp(props->pair.key,key)==0) { + value = props->pair.value; + size = props->pair.size; stat = NC_NOERR; /* found */ break; } @@ -302,13 +343,13 @@ OPTSTATIC int ncproplistith(const NCproplist* plist, size_t i, char* const * keyp, uintptr_t const * valuep, uintptr_t* sizep) { int stat = NC_NOERR; - NCProperty* prop = NULL; + NCPproperty* prop = NULL; if(plist == NULL) goto done; if(i >= plist->count) {stat = NC_EINVAL; goto done;} prop = &plist->properties[i]; - if(keyp) *((char**)keyp) = (char*)prop->key; - if(valuep) *((uintptr_t*)valuep) = (uintptr_t)prop->value; - if(sizep) *sizep = prop->size; + if(keyp) *((char**)keyp) = (char*)prop->pair.key; + if(valuep) *((uintptr_t*)valuep) = (uintptr_t)prop->pair.value; + if(sizep) *sizep = prop->pair.size; done: return stat; } @@ -322,11 +363,17 @@ ncproplistith(const NCproplist* plist, size_t i, char* const * keyp, uintptr_t c static int ncproplistinit(NCproplist* plist) { - /* Assume property list will hold at lease MINPROPS properties */ - plist->alloc = MINPROPS; - plist->count = 0; - plist->properties = (NCProperty*)calloc(MINPROPS,sizeof(NCProperty)); - return (plist->properties?NC_NOERR:NC_ENOMEM); + int stat = 0; + /* Assume property list will hold at lease MINPROPS properties */ + if(plist->alloc == 0) { + plist->alloc = MINPROPS; + plist->properties = (NCPproperty*)calloc(plist->alloc,sizeof(NCPproperty)); + plist->count = 0; + } else { + if((stat = ncproplistclear(plist))<0) goto done; + } +done: + return stat; } /* Suppress unused statics warning */ @@ -347,4 +394,3 @@ ncproplist_unused(void) unused = (void*)ncproplistith; unused = unused; } - diff --git a/libnczarr/zsync.c b/libnczarr/zsync.c index c9d55ee751..e30c5f85f7 100644 --- a/libnczarr/zsync.c +++ b/libnczarr/zsync.c @@ -908,12 +908,14 @@ zcharify(const NCjson* src, NCbytes* buf) { int stat = NC_NOERR; size_t i; - struct NCJconst jstr = NCJconst_empty; + struct NCJconst jstr; + + memset(&jstr,0,sizeof(jstr)); if(NCJsort(src) != NCJ_ARRAY) { /* singleton */ if((stat = NCJcvt(src, NCJ_STRING, &jstr))<0) {stat = NC_EINVAL; goto done;} ncbytescat(buf,jstr.sval); - } else for(i=0;ixarray = nclistnew())==NULL) {stat = NC_ENOMEM; goto done;} - for(i=0;ixarray,strdup(NCJstring(k))); @@ -1517,9 +1519,9 @@ define_var1(NC_FILE_INFO_T* file, NC_GRP_INFO_T* grp, const char* varname) if(jdimrefs != NULL) { /* Extract the dimref names */ assert((NCJsort(jdimrefs) == NCJ_ARRAY)); if(zvar->scalar) { - assert(NCJlength(jdimrefs) == 0); + assert(NCJarraylength(jdimrefs) == 0); } else { - rank = NCJlength(jdimrefs); + rank = NCJarraylength(jdimrefs); for(j=0;jhdr.name); @@ -1583,7 +1585,7 @@ define_var1(NC_FILE_INFO_T* file, NC_GRP_INFO_T* grp, const char* varname) rank = 0; zarr_rank = 1; /* Zarr does not support scalars */ } else - rank = (zarr_rank = NCJlength(jvalue)); + rank = (zarr_rank = NCJarraylength(jvalue)); if(zarr_rank > 0) { /* Save the rank of the variable */ @@ -1890,9 +1892,9 @@ parse_group_content(const NCjson* jcontent, NClist* dimdefs, NClist* varnames, N if(jvalue != NULL) { if(NCJsort(jvalue) != NCJ_DICT) {stat = (THROW(NC_ENCZARR)); goto done;} /* Extract the dimensions defined in this group */ - for(i=0;isort == NCJ_ARRAY) { - for(i=0;i 0) return 0; /* e.g. [...,[...],...] or [...,{...},...] */ - for(i=0;i < NCJlength(json);i++) { + for(i=0;i < NCJarraylength(json);i++) { NCjson* j = NCJith(json,i); if(!checksimplejson(j,depth+1)) return 0; } @@ -1052,7 +1052,7 @@ NCZ_iscomplexjson(const NCjson* json, nc_type typehint) /* If the typehint is NC_CHAR, then always treat it as complex */ if(typehint == NC_CHAR) {stat = 1; goto done;} /* Otherwise see if it is a simple vector of atomic values */ - for(i=0;i < NCJlength(json);i++) { + for(i=0;i < NCJarraylength(json);i++) { NCjson* j = NCJith(json,i); if(!NCJisatomic(j)) {stat = 1; goto done;} } diff --git a/nc_test4/test_filterx_hdf5.c b/nc_test4/test_filterx_hdf5.c index 5224bcff96..b93dd20d85 100644 --- a/nc_test4/test_filterx_hdf5.c +++ b/nc_test4/test_filterx_hdf5.c @@ -427,15 +427,15 @@ json2vector(const NCjson* jarray, size_t* np, unsigned** valuesp) if(NCJsort(jarray) != NCJ_ARRAY) {stat = NC_EINVAL; goto done;} - if(NCJlength(jarray) > 0 && NCJcontents(jarray) != NULL) { - if((values = (unsigned*)malloc(sizeof(unsigned)*NCJlength(jarray)))==NULL) + if(NCJarraylength(jarray) > 0 && NCJcontents(jarray) != NULL) { + if((values = (unsigned*)malloc(sizeof(unsigned)*NCJarraylength(jarray)))==NULL) {stat = NC_ENOMEM; goto done;} - for(i=0;i ",NCJstring(j)); - if(i+1 >= NCJlength(json)) {/* malformed */ + if(i+1 >= NCJarraylength(json)) {/* malformed */ printf(""); } else dumpR((NCjson*)NCJith(json,i+1),depth+1); @@ -322,11 +322,11 @@ dumpR(NCjson* json, int depth) } break; case NCJ_ARRAY: - if(NCJlength(json) == 0) { + if(NCJarraylength(json) == 0) { printf("[]"); } else { printf("\n"); - for(i=0;i