diff --git a/libdispatch/dinfermodel.c b/libdispatch/dinfermodel.c
index d991b9e..d170dc2 100644
--- a/libdispatch/dinfermodel.c
+++ b/libdispatch/dinfermodel.c
@@ -143,7 +143,15 @@ static const struct MACRODEF {
{NULL,NULL,{NULL}}
};
-/* Mode inferences: if mode contains key, then add the inference and infer again */
+/*
+Mode inferences: if mode contains key value, then add the inferred value;
+Warning: be careful how this list is constructed to avoid infinite inferences.
+In order to (mostly) avoid that consequence, any attempt to
+infer a value that is already present will be ignored.
+This effectively means that the inference graph
+must be a DAG and may not have cycles.
+You have been warned.
+*/
static const struct MODEINFER {
char* key;
char* inference;
@@ -151,6 +159,7 @@ static const struct MODEINFER {
{"zarr","nczarr"},
{"xarray","zarr"},
{"noxarray","nczarr"},
+{"noxarray","zarr"},
{NULL,NULL}
};
@@ -202,6 +211,7 @@ static int processmacros(NClist** fraglistp);
static char* envvlist2string(NClist* pairs, const char*);
static void set_default_mode(int* cmodep);
static int parseonchar(const char* s, int ch, NClist* segments);
+static int mergelist(NClist** valuesp);
static int openmagic(struct MagicFile* file);
static int readmagic(struct MagicFile* file, long pos, char* magic);
@@ -217,8 +227,9 @@ static int parsepair(const char* pair, char** keyp, char** valuep);
static NClist* parsemode(const char* modeval);
static const char* getmodekey(const NClist* envv);
static int replacemode(NClist* envv, const char* newval);
-static int inferone(const char* mode, NClist* newmodes);
+static void infernext(NClist* current, NClist* next);
static int negateone(const char* mode, NClist* modes);
+static void cleanstringlist(NClist* strs, int caseinsensitive);
/*
If the path looks like a URL, then parse it, reformat it.
@@ -416,28 +427,6 @@ envvlist2string(NClist* envv, const char* delim)
return result;
}
-/* Convert a list into a comma'd string */
-static char*
-list2string(NClist* list)
-{
- int i;
- NCbytes* buf = NULL;
- char* result = NULL;
-
- if(list == NULL || nclistlength(list)==0) return strdup("");
- buf = ncbytesnew();
- for(i=0;i<nclistlength(list);i++) {
- const char* m = nclistget(list,i);
- if(m == NULL || strlen(m) == 0) continue;
- if(i > 0) ncbytescat(buf,",");
- ncbytescat(buf,m);
- }
- result = ncbytesextract(buf);
- ncbytesfree(buf);
- if(result == NULL) result = strdup("");
- return result;
-}
-
/* Given a mode= argument, fill in the impl */
static int
processmodearg(const char* arg, NCmodel* model)
@@ -504,9 +493,10 @@ processinferences(NClist* fraglenv)
{
int stat = NC_NOERR;
const char* modeval = NULL;
- NClist* modes = NULL;
NClist* newmodes = nclistnew();
- int i,inferred = 0;
+ NClist* currentmodes = NULL;
+ NClist* nextmodes = nclistnew();
+ int i;
char* newmodeval = NULL;
if(fraglenv == NULL || nclistlength(fraglenv) == 0) goto done;
@@ -515,22 +505,53 @@ processinferences(NClist* fraglenv)
if((modeval = getmodekey(fraglenv))==NULL) goto done;
/* Get the mode as list */
- modes = parsemode(modeval);
-
- /* Repeatedly walk the mode list until no more new positive inferences */
- do {
- for(i=0;i<nclistlength(modes);i++) {
- const char* mode = nclistget(modes,i);
- inferred = inferone(mode,newmodes);
- nclistpush(newmodes,strdup(mode)); /* keep key */
- if(!inferred) nclistpush(newmodes,strdup(mode));
+ currentmodes = parsemode(modeval);
+
+#ifdef DEBUG
+ printlist(currentmodes,"processinferences: initial mode list");
+#endif
+
+ /* Do what amounts to breadth first inferencing down the inference DAG. */
+
+ for(;;) {
+ NClist* tmp = NULL;
+ /* Compute the next set of inferred modes */
+#ifdef DEBUG
+printlist(currentmodes,"processinferences: current mode list");
+#endif
+ infernext(currentmodes,nextmodes);
+#ifdef DEBUG
+printlist(nextmodes,"processinferences: next mode list");
+#endif
+ /* move current modes into list of newmodes */
+ for(i=0;i<nclistlength(currentmodes);i++) {
+ nclistpush(newmodes,nclistget(currentmodes,i));
}
- } while(inferred);
+ nclistsetlength(currentmodes,0); /* clear current mode list */
+ if(nclistlength(nextmodes) == 0) break; /* nothing more to do */
+#ifdef DEBUG
+printlist(newmodes,"processinferences: new mode list");
+#endif
+ /* Swap current and next */
+ tmp = currentmodes;
+ currentmodes = nextmodes;
+ nextmodes = tmp;
+ tmp = NULL;
+ }
+ /* cleanup any unused elements in currenmodes */
+ nclistclearall(currentmodes);
+
+ /* Ensure no duplicates */
+ cleanstringlist(newmodes,1);
+
+#ifdef DEBUG
+ printlist(newmodes,"processinferences: final inferred mode list");
+#endif
/* Remove negative inferences */
- for(i=0;i<nclistlength(modes);i++) {
- const char* mode = nclistget(modes,i);
- inferred = negateone(mode,newmodes);
+ for(i=0;i<nclistlength(newmodes);i++) {
+ const char* mode = nclistget(newmodes,i);
+ negateone(mode,newmodes);
}
/* Store new mode value */
@@ -541,11 +562,13 @@ processinferences(NClist* fraglenv)
done:
nullfree(newmodeval);
- nclistfreeall(modes);
nclistfreeall(newmodes);
+ nclistfreeall(currentmodes);
+ nclistfreeall(nextmodes);
return check(stat);
}
+
static int
negateone(const char* mode, NClist* newmodes)
{
@@ -568,23 +591,28 @@ negateone(const char* mode, NClist* newmodes)
return changed;
}
-static int
-inferone(const char* mode, NClist* newmodes)
+static void
+infernext(NClist* current, NClist* next)
{
- const struct MODEINFER* tests = modeinferences;
- int changed = 0;
- for(;tests->key;tests++) {
- if(strcasecmp(tests->key,mode)==0) {
- /* Append the inferred mode; dups removed later */
- nclistpush(newmodes,strdup(tests->inference));
- changed = 1;
+ int i;
+ for(i=0;i<nclistlength(current);i++) {
+ const struct MODEINFER* tests = NULL;
+ const char* cur = nclistget(current,i);
+ for(tests=modeinferences;tests->key;tests++) {
+ if(strcasecmp(tests->key,cur)==0) {
+ /* Append the inferred mode unless dup */
+ if(!nclistmatch(next,tests->inference,1))
+ nclistpush(next,strdup(tests->inference));
+ }
}
}
- return changed;
}
+/*
+Given a list of strings, remove nulls and duplicates
+*/
static int
-mergekey(NClist** valuesp)
+mergelist(NClist** valuesp)
{
int i,j;
int stat = NC_NOERR;
@@ -686,12 +714,12 @@ cleanfragments(NClist** fraglenvp)
/* collect all unique keys */
collectallkeys(fraglenv,allkeys);
- /* Collect all values for same key across all fragments */
+ /* Collect all values for same key across all fragment pairs */
for(i=0;i<nclistlength(allkeys);i++) {
key = nclistget(allkeys,i);
collectvaluesbykey(fraglenv,key,tmp);
/* merge the key values, remove duplicate */
- if((stat=mergekey(&tmp))) goto done;
+ if((stat=mergelist(&tmp))) goto done;
/* Construct key,value pair and insert into newlist */
key = strdup(key);
nclistpush(newlist,key);
@@ -923,7 +951,7 @@ NC_infermodel(const char* path, int* omodep, int iscreate, int useparallel, void
}
} else {/* Not URL */
- if(*newpathp) *newpathp = NULL;
+ if(newpathp) *newpathp = NULL;
}
/* Phase 8: mode inference from mode flags */
@@ -1101,6 +1129,71 @@ parsemode(const char* modeval)
return modes;
}
+/* Convert a list into a comma'd string */
+static char*
+list2string(NClist* list)
+{
+ int i;
+ NCbytes* buf = NULL;
+ char* result = NULL;
+
+ if(list == NULL || nclistlength(list)==0) return strdup("");
+ buf = ncbytesnew();
+ for(i=0;i<nclistlength(list);i++) {
+ const char* m = nclistget(list,i);
+ if(m == NULL || strlen(m) == 0) continue;
+ if(i > 0) ncbytescat(buf,",");
+ ncbytescat(buf,m);
+ }
+ result = ncbytesextract(buf);
+ ncbytesfree(buf);
+ if(result == NULL) result = strdup("");
+ return result;
+}
+
+#if 0
+/* Given a comma separated string, remove duplicates; mostly used to cleanup mode list */
+static char*
+cleancommalist(const char* commalist, int caseinsensitive)
+{
+ NClist* tmp = nclistnew();
+ char* newlist = NULL;
+ if(commalist == NULL || strlen(commalist)==0) return nulldup(commalist);
+ (void)parseonchar(commalist,',',tmp);/* split on commas */
+ cleanstringlist(tmp,caseinsensitive);
+ newlist = list2string(tmp);
+ nclistfreeall(tmp);
+ return newlist;
+}
+#endif
+
+/* Given a list of strings, remove nulls and duplicated */
+static void
+cleanstringlist(NClist* strs, int caseinsensitive)
+{
+ int i,j;
+ if(nclistlength(strs) == 0) return;
+ /* Remove nulls */
+ for(i=nclistlength(strs)-1;i>=0;i--) {
+ if(nclistget(strs,i)==NULL) nclistremove(strs,i);
+ }
+ /* Remove duplicates*/
+ for(i=0;i<nclistlength(strs);i++) {
+ const char* value = nclistget(strs,i);
+ /* look ahead for duplicates */
+ for(j=nclistlength(strs)-1;j>i;j--) {
+ int match;
+ const char* candidate = nclistget(strs,j);
+ if(caseinsensitive)
+ match = (strcasecmp(value,candidate) == 0);
+ else
+ match = (strcmp(value,candidate) == 0);
+ if(match) {char* dup = nclistremove(strs,j); nullfree(dup);}
+ }
+ }
+}
+
+
/**************************************************/
/**
* @internal Given an existing file, figure out its format and return
@@ -1502,8 +1595,10 @@ printlist(NClist* list, const char* tag)
{
int i;
fprintf(stderr,"%s:",tag);
- for(i=0;i<nclistlength(list);i++)
+ for(i=0;i<nclistlength(list);i++) {
fprintf(stderr," %s",(char*)nclistget(list,i));
+ fprintf(stderr,"[%p]",(char*)nclistget(list,i));
+ }
fprintf(stderr,"\n");
dbgflush();
}
diff --git a/libdispatch/ncbytes.c b/libdispatch/ncbytes.c
index 45e281c..dc9144e 100644
--- a/libdispatch/ncbytes.c
+++ b/libdispatch/ncbytes.c
@@ -122,9 +122,7 @@ ncbytesappend(NCbytes* bb, char elem)
int
ncbytescat(NCbytes* bb, const char* s)
{
- if(s == NULL) {
- return 1;
- }
+ if(s == NULL) return 1;
ncbytesappendn(bb,(void*)s,strlen(s)+1); /* include trailing null*/
/* back up over the trailing null*/
if(bb->length == 0) return ncbytesfail();
diff --git a/libdispatch/nclist.c b/libdispatch/nclist.c
index 49f0dde..b5f8158 100644
--- a/libdispatch/nclist.c
+++ b/libdispatch/nclist.c
@@ -183,6 +183,7 @@ nclistremove(NClist* l, size_t i)
return elem;
}
+/* Match on == */
int
nclistcontains(NClist* l, void* elem)
{
@@ -193,7 +194,7 @@ nclistcontains(NClist* l, void* elem)
return 0;
}
-/* Return 1/0 */
+/* Match on str(case)cmp */
int
nclistmatch(NClist* l, const char* elem, int casesensitive)
{
@@ -230,7 +231,6 @@ nclistelemremove(NClist* l, void* elem)
return found;
}
-
/* Extends nclist to include a unique operator
which remove duplicate values; NULL values removed
return value is always 1.
diff --git a/dap4_test/Makefile.am b/dap4_test/Makefile.am
index 158a1fc..3e44863 100644
--- a/dap4_test/Makefile.am
+++ b/dap4_test/Makefile.am
@@ -43,7 +43,7 @@ pingurl4_SOURCES = pingurl4.c
if ENABLE_DAP_REMOTE_TESTS
if BUILD_UTILITIES
# relies on ncdump
- TESTS += test_hyrax.sh test_thredds.sh
+# TESTS += test_hyrax.sh test_thredds.sh
if AX_IGNORE
TESTS += test_remote.sh
endif
diff --git a/libnczarr/zsync.c b/libnczarr/zsync.c
index d5cbee9..a595fb1 100644
--- a/libnczarr/zsync.c
+++ b/libnczarr/zsync.c
@@ -1159,15 +1159,9 @@ mininttype(unsigned long long u64, int negative)
long long i64 = (long long)u64; /* keep bit pattern */
if(!negative && u64 >= NC_MAX_INT64) return NC_UINT64;
if(i64 < 0) {
- if(i64 >= NC_MIN_BYTE) return NC_BYTE;
- if(i64 >= NC_MIN_SHORT) return NC_SHORT;
if(i64 >= NC_MIN_INT) return NC_INT;
return NC_INT64;
}
- if(i64 <= NC_MAX_BYTE) return NC_BYTE;
- if(i64 <= NC_MAX_UBYTE) return NC_UBYTE;
- if(i64 <= NC_MAX_SHORT) return NC_SHORT;
- if(i64 <= NC_MAX_USHORT) return NC_USHORT;
if(i64 <= NC_MAX_INT) return NC_INT;
if(i64 <= NC_MAX_UINT) return NC_UINT;
return NC_INT64;
diff --git a/nczarr_test/ref_quotes.cdl b/nczarr_test/ref_quotes.cdl
index 97802cd..9caeae4 100644
--- a/nczarr_test/ref_quotes.cdl
+++ b/nczarr_test/ref_quotes.cdl
@@ -5,7 +5,7 @@ dimensions:
lon = 30 ;
variables:
float fractional_snow_cover(time, lat, lon) ;
- fractional_snow_cover:ID = 68b ;
+ fractional_snow_cover:ID = 68 ;
fractional_snow_cover:esa_cci_path = NaN ;
fractional_snow_cover:long_name = "Surface Fraction Covered by Snow" ;
fractional_snow_cover:orig_attrs = "{\'comment\': \'Grid cell fractional snow cover based on the Globsnow CCI product.\', \'long_name\': \'Surface fraction covered by snow.\', \'project_name\': \'GlobSnow\', \'references\': \'Luojus, Kari, et al. \"ESA DUE Globsnow-Global Snow Database for Climate Research.\" ESA Special Publication. Vol. 686. 2010.\', \'source_name\': \'MFSC\', \'standard_name\': \'surface_snow_area_fraction\', \'units\': \'percent\', \'url\': \'http://www.globsnow.info/\'}" ;