netcdf-c/ncgen/data.c
2024-03-25 09:25:24 +00:00

816 lines
17 KiB
C

/*********************************************************************
* Copyright 2018, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*********************************************************************/
/* $Id: data.c,v 1.7 2010/05/24 19:59:56 dmh Exp $ */
/* $Header: /upc/share/CVS/netcdf-3/ncgen/data.c,v 1.7 2010/05/24 19:59:56 dmh Exp $ */
#include "includes.h"
#include "ncoffsets.h"
#include "netcdf_aux.h"
#include "dump.h"
#include <stddef.h>
#undef VERIFY
#ifndef __MINGW32__
#define HHPRINT
#endif
#define XVSNPRINTF vsnprintf
/*
#define XVSNPRINTF lvsnprintf
extern int lvsnprintf(char*, size_t, const char*, va_list);
*/
#define DATALISTINIT 32
/* Track all known datalist*/
List* alldatalists = NULL;
NCConstant nullconstant;
NCConstant fillconstant;
Datalist* filldatalist;
Bytebuffer* codebuffer;
Bytebuffer* codetmp;
Bytebuffer* stmt;
/* Forward */
static void setconstlist(NCConstant* con, Datalist* dl);
#ifdef VERIFY
/* index of match */
static int
verify(List* all, Datalist* dl)
{
int i;
for(i=0;i<listlength(all);i++) {
void* pi = listget(all,i);
if(pi == dl)
return i;
}
return -1;
}
#endif
/**************************************************/
/**************************************************/
NCConstant*
nullconst(void)
{
NCConstant* n = ecalloc(sizeof(NCConstant));
return n;
}
int
isstringable(nc_type nctype)
{
switch (nctype) {
case NC_CHAR: case NC_STRING:
case NC_BYTE: case NC_UBYTE:
case NC_FILLVALUE:
return 1;
default: break;
}
return 0;
}
NCConstant*
list2const(Datalist* list)
{
NCConstant* con = nullconst();
ASSERT(list != NULL);
con->nctype = NC_COMPOUND;
if(!list->readonly) con->lineno = list->data[0]->lineno;
setconstlist(con,list);
con->filled = 0;
return con;
}
Datalist*
const2list(NCConstant* con)
{
Datalist* list;
ASSERT(con != NULL);
list = builddatalist(1);
if(list != NULL) {
dlappend(list,con);
}
return list;
}
/**************************************************/
#ifdef GENDEBUG
void
report(char* lead, Datalist* list)
{
extern void bufdump(Datalist*,Bytebuffer*);
Bytebuffer* buf = bbNew();
bufdump(list,buf);
fprintf(stderr,"\n%s::%s\n",lead,bbContents(buf));
fflush(stderr);
bbFree(buf);
}
#endif
/**************************************************/
static void
setconstlist(NCConstant* con, Datalist* dl)
{
#ifdef VERIFY
int pos = verify(alldatalists,dl);
if(pos >= 0) {
dumpdatalist(listget(alldatalists,pos),"XXX");
}
#endif
con->value.compoundv = dl;
}
/* Deep constant cloning; return struct not pointer to struct*/
NCConstant*
cloneconstant(NCConstant* con)
{
NCConstant* newcon = NULL;
Datalist* newdl = NULL;
char* s = NULL;
newcon = nullconst();
if(newcon == NULL) return newcon;
*newcon = *con;
switch (newcon->nctype) {
case NC_STRING:
if(newcon->value.stringv.len == 0)
s = NULL;
else {
s = (char*)ecalloc((size_t)newcon->value.stringv.len+1);
if(newcon->value.stringv.len > 0)
memcpy(s,newcon->value.stringv.stringv, (size_t)newcon->value.stringv.len);
s[newcon->value.stringv.len] = '\0';
}
newcon->value.stringv.stringv = s;
break;
case NC_OPAQUE:
s = (char*)ecalloc(newcon->value.opaquev.len+1);
if(newcon->value.opaquev.len > 0)
memcpy(s,newcon->value.opaquev.stringv, newcon->value.opaquev.len);
s[newcon->value.opaquev.len] = '\0';
newcon->value.opaquev.stringv = s;
break;
case NC_COMPOUND:
newdl = clonedatalist(con->value.compoundv);
setconstlist(newcon,newdl);
break;
default: break;
}
return newcon;
}
/* Deep constant clear*/
void
clearconstant(NCConstant* con)
{
if(con == NULL) return;
switch (con->nctype) {
case NC_STRING:
if(con->value.stringv.stringv != NULL)
efree(con->value.stringv.stringv);
break;
case NC_OPAQUE:
if(con->value.opaquev.stringv != NULL)
efree(con->value.opaquev.stringv);
break;
case NC_COMPOUND:
con->value.compoundv = NULL;
break;
default: break;
}
memset((void*)con,0,sizeof(NCConstant));
}
void
freeconstant(NCConstant* con, int shallow)
{
if(!shallow) clearconstant(con);
nullfree(con);
}
/**************************************************/
int
datalistline(Datalist* ds)
{
if(ds == NULL || ds->length == 0) return 0;
return ds->data[0]->lineno;
}
/* Go thru a databuf of possibly nested constants
and insert commas as needed; ideally, this
operation should be idempotent so that
the caller need not worry about it having already
been applied. Also, handle situation where there may be missing
matching right braces.
*/
static char* commifyr(char* p, Bytebuffer* buf);
static char* wordstring(char* p, Bytebuffer* buf, char quote);
void
commify(Bytebuffer* buf)
{
char* list,*p;
if(bbLength(buf) == 0) return;
list = bbDup(buf);
p = list;
bbClear(buf);
commifyr(p,buf);
bbNull(buf);
efree(list);
}
/* Requires that the string be balanced
WRT to braces
*/
static char*
commifyr(char* p, Bytebuffer* buf)
{
int comma = 0;
char c;
while((c=*p++)) {
if(c == ' ') continue;
if(c == ',') continue;
else if(c == '}') {
break;
}
if(comma) bbCat(buf,", "); else comma=1;
if(c == '{') {
bbAppend(buf,'{');
p = commifyr(p,buf);
bbAppend(buf,'}');
} else if(c == '\'' || c == '\"') {
p = wordstring(p,buf,c);
} else {
bbAppend(buf,c);
p=word(p,buf);
}
}
return p;
}
char*
word(char* p, Bytebuffer* buf)
{
char c;
while((c=*p++)) {
if(c == '}' || c == ' ' || c == ',') break;
if(c == '\\') {
bbAppend(buf,c);
c=*p++;
if(!c) break;
}
bbAppend(buf, c);
}
p--; /* leave terminator for parent */
return p;
}
static char*
wordstring(char* p, Bytebuffer* buf, char quote)
{
char c;
bbAppend(buf,quote);
while((c=*p++)) {
if(c == '\\') {
bbAppend(buf,c);
c = *p++;
if(c == '\0') return --p;
} else if(c == quote) {
bbAppend(buf,c);
return p;
}
bbAppend(buf,c);
}
return p;
}
static const char zeros[] =
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
void
alignbuffer(NCConstant* prim, Bytebuffer* buf)
{
int stat = NC_NOERR;
size_t alignment;
size_t pad,offset;
ASSERT(prim->nctype != NC_COMPOUND);
if(prim->nctype == NC_ECONST)
stat = ncaux_class_alignment(prim->value.enumv->typ.typecode,&alignment);
else if(usingclassic && prim->nctype == NC_STRING)
stat = ncaux_class_alignment(NC_CHAR,&alignment);
else if(prim->nctype == NC_CHAR)
stat = ncaux_class_alignment(NC_CHAR,&alignment);
else
stat = ncaux_class_alignment(prim->nctype,&alignment);
if(!stat) {
offset = bbLength(buf);
pad = getpadding(offset,alignment);
if(pad > 0)
bbAppendn(buf,(void*)zeros,pad);
}
}
/*
Following routines are in support of language-oriented output
*/
void
codedump(Bytebuffer* buf)
{
bbCatbuf(codebuffer,buf);
bbClear(buf);
}
void
codepartial(const char* txt)
{
bbCat(codebuffer,txt);
}
void
codeline(const char* line)
{
codepartial(line);
codepartial("\n");
}
void
codelined(int n, const char* txt)
{
bbindent(codebuffer,n);
bbCat(codebuffer,txt);
codepartial("\n");
}
void
codeflush(void)
{
if(bbLength(codebuffer) > 0) {
bbNull(codebuffer);
fputs(bbContents(codebuffer),stdout);
fflush(stdout);
bbClear(codebuffer);
}
}
void
bbindent(Bytebuffer* buf, const int n)
{
bbCat(buf,indented(n));
}
/* Provide an restrict snprintf that writes to an expandable buffer */
/* Simulates a simple snprintf because apparently
the IRIX one is broken wrt return value.
Supports only %u %d %f %s and %% specifiers
with optional leading hh or ll.
*/
static void
vbbprintf(Bytebuffer* buf, const char* fmt, va_list argv)
{
char tmp[128];
const char* p;
char c;
int hcount;
int lcount;
char* text;
for(p=fmt;(c=*p++);) {
hcount = 0; lcount = 0;
switch (c) {
case '%':
retry: switch ((c=*p++)) {
case '\0': bbAppend(buf,'%'); p--; break;
case '%': bbAppend(buf,c); break;
case 'h':
hcount++;
while((c=*p) && (c == 'h')) {hcount++; p++;}
if(hcount > 2) hcount = 2;
goto retry;
case 'l':
lcount++;
while((c=*p) && (c == 'l')) {
lcount++;
p++;
}
if(lcount > 2) lcount = 2;
goto retry;
case 'u':
if(hcount == 2) {
snprintf(tmp,sizeof(tmp),
#ifdef HHPRINT
"%hhu"
#else
"%2u"
#endif
,(unsigned char)va_arg(argv,unsigned int));
} else if(hcount == 1) {
snprintf(tmp,sizeof(tmp), "%hu",
(unsigned short)va_arg(argv,unsigned int));
} else if(lcount == 2) {
snprintf(tmp,sizeof(tmp),"%llu",
(unsigned long long)va_arg(argv,unsigned long long));
} else if(lcount == 1) {
snprintf(tmp,sizeof(tmp),"%lu",
(unsigned long)va_arg(argv,unsigned long));
} else {
snprintf(tmp,sizeof(tmp),"%u",
(unsigned int)va_arg(argv,unsigned int));
}
bbCat(buf,tmp);
break;
case 'd':
if(hcount == 2) {
snprintf(tmp,sizeof(tmp),
#ifdef HHPRINT
"%hhd"
#else
"%2d"
#endif
,(signed char)va_arg(argv,signed int));
} else if(hcount == 1) {
snprintf(tmp,sizeof(tmp),"%hd",
(signed short)va_arg(argv,signed int));
} else if(lcount == 2) {
snprintf(tmp,sizeof(tmp),"%lld",
(signed long long)va_arg(argv,signed long long));
} else if(lcount == 1) {
snprintf(tmp,sizeof(tmp),"%ld",
(signed long)va_arg(argv,signed long));
} else {
snprintf(tmp,sizeof(tmp),"%d",
(signed int)va_arg(argv,signed int));
}
bbCat(buf,tmp);
break;
case 'f':
if(lcount > 0) {
snprintf(tmp,sizeof(tmp),"((double)%.16g)",
(double)va_arg(argv,double));
} else {
snprintf(tmp,sizeof(tmp),"((float)%.8g)",
(double)va_arg(argv,double));
}
bbCat(buf,tmp);
break;
case 's':
text = va_arg(argv,char*);
bbCat(buf,text);
break;
case 'c':
c = (char)va_arg(argv,int);
bbAppend(buf,c);
break;
default:
PANIC1("vbbprintf: unknown specifier: %c",(char)c);
}
break;
default:
bbAppend(buf,c);
}
}
}
void
bbprintf(Bytebuffer* buf, const char *fmt, ...)
{
va_list argv;
va_start(argv,fmt);
vbbprintf(buf,fmt,argv);
va_end(argv);
}
void
bbprintf0(Bytebuffer* buf, const char *fmt, ...)
{
va_list argv;
va_start(argv,fmt);
bbClear(buf);
vbbprintf(buf,fmt,argv);
va_end(argv);
}
void
codeprintf(const char *fmt, ...)
{
va_list argv;
va_start(argv,fmt);
vbbprintf(codebuffer,fmt,argv);
va_end(argv);
}
NCConstant*
emptycompoundconst(int lineno)
{
NCConstant* c = nullconst();
c->lineno = lineno;
c->nctype = NC_COMPOUND;
setconstlist(c,builddatalist(0));
c->filled = 0;
return c;
}
/* Make an empty string constant*/
NCConstant*
emptystringconst(int lineno)
{
NCConstant* c = nullconst();
ASSERT(c != NULL);
c->lineno = lineno;
c->nctype = NC_STRING;
c->value.stringv.len = 0;
c->value.stringv.stringv = NULL;
c->filled = 0;
return c;
}
#define INDENTMAX 256
static char* dent = NULL;
char*
indented(int n)
{
char* indentation;
if(dent == NULL) {
dent = (char*)ecalloc(INDENTMAX+1);
memset((void*)dent,' ',INDENTMAX);
dent[INDENTMAX] = '\0';
}
if(n*4 >= INDENTMAX) n = INDENTMAX/4;
indentation = dent+(INDENTMAX - 4*n);
return indentation;
}
void
dlsetalloc(Datalist* dl, size_t need)
{
NCConstant** newdata = NULL;
if(dl->readonly) abort();
if(dl->alloc < need) {
newdata = (NCConstant**)ecalloc(need*sizeof(NCConstant*));
if(dl->length > 0)
memcpy(newdata,dl->data,sizeof(NCConstant*)*dl->length);
dl->alloc = need;
nullfree(dl->data);
dl->data = newdata;
}
}
void
dlextend(Datalist* dl)
{
size_t newalloc;
if(dl->readonly) abort();
newalloc = (dl->alloc > 0?2*dl->alloc:2);
dlsetalloc(dl,newalloc);
}
void
capture(Datalist* dl)
{
if(alldatalists == NULL) alldatalists = listnew();
listpush(alldatalists,dl);
}
Datalist*
builddatalist(int initial)
{
Datalist* ci;
if(initial <= 0) initial = DATALISTINIT;
initial++; /* for header*/
ci = (Datalist*)ecalloc(sizeof(Datalist));
if(ci == NULL) semerror(0,"out of memory\n");
ci->data = (NCConstant**)ecalloc(sizeof(NCConstant*) * (size_t)initial);
ci->alloc = (size_t)initial;
ci->length = 0;
return ci;
}
void
dlappend(Datalist* dl, NCConstant* constant)
{
if(dl->readonly) abort();
if(dl->length >= dl->alloc)
dlextend(dl);
dl->data[dl->length++] = (constant);
}
void
dlset(Datalist* dl, size_t pos, NCConstant* constant)
{
ASSERT(pos < dl->length);
dl->data[pos] = (constant);
}
NCConstant*
dlremove(Datalist* dl, size_t pos)
{
NCConstant* con = NULL;
ASSERT(dl->length > 0 && pos < dl->length);
con = dl->data[pos];
for(size_t i=pos+1;i<dl->length;i++)
dl->data[i-1] = dl->data[i];
dl->length--;
return con;
}
void
dlinsert(Datalist* dl, size_t pos, Datalist* insertion)
{
size_t len1 = datalistlen(dl);
size_t len2 = datalistlen(insertion);
ptrdiff_t delta = (ptrdiff_t)(len1 - pos);
dlsetalloc(dl, len2+len1+1);
/* move contents of dl up to make room for insertion */
if(delta > 0)
memmove(&dl->data[pos+len2],&dl->data[pos], (size_t)delta*sizeof(NCConstant*));
dl->length += len2;
for(size_t i=0;i<len2;i++) {
NCConstant* con = insertion->data[i];
con = cloneconstant(con);
dl->data[pos+i] = con;
}
}
/* Convert a datalist to a compound constant */
NCConstant*
builddatasublist(Datalist* dl)
{
NCConstant* d = nullconst();
d->nctype = NC_COMPOUND;
d->lineno = (dl->length > 0?dl->data[0]->lineno:0);
setconstlist(d,dl);
d->filled = 0;
return d;
}
/* Convert a subsequence of a datalist to its own datalist */
Datalist*
builddatasubset(Datalist* dl, size_t start, size_t count)
{
Datalist* subset;
if(dl == NULL || start >= datalistlen(dl)) return NULL;
if((start + count) > datalistlen(dl))
count = (datalistlen(dl) - start);
subset = (Datalist*)ecalloc(sizeof(Datalist));
subset->readonly = 1;
subset->length = count;
subset->alloc = count;
subset->data = &dl->data[start];
return subset;
}
/* Deep copy */
Datalist*
clonedatalist(Datalist* dl)
{
int i;
size_t len;
Datalist* newdl;
if(dl == NULL) return NULL;
len = datalistlen(dl);
newdl = builddatalist((int)len);
/* initialize */
for(i=0;i<len;i++) {
NCConstant* con = datalistith(dl,i);
con = cloneconstant(con);
dlappend(newdl,con);
}
return newdl;
}
/* recursive helpers */
void
reclaimconstant(NCConstant* con)
{
if(con == NULL) return;
switch (con->nctype) {
case NC_STRING:
if(con->value.stringv.stringv != NULL)
efree(con->value.stringv.stringv);
break;
case NC_OPAQUE:
if(con->value.opaquev.stringv != NULL)
efree(con->value.opaquev.stringv);
break;
case NC_COMPOUND:
#ifdef VERIFY
{int pos;
if((pos=verify(alldatalists,con->value.compoundv)) >= 0) {
dumpdatalist(listget(alldatalists,pos),"XXX");
abort();
}
}
#endif
reclaimdatalist(con->value.compoundv);
con->value.compoundv = NULL;
break;
default: break;
}
efree(con);
}
void
reclaimdatalist(Datalist* list)
{
int i;
if(list == NULL) return;
if(!list->readonly) {
if(list->data != NULL) {
for(i=0;i<list->length;i++) {
NCConstant* con = list->data[i];
if(con != NULL) reclaimconstant(con);
}
}
}
freedatalist(list);
}
/* Like reclaimdatalist, but do not try to reclaim contained constants */
void
freedatalist(Datalist* list)
{
if(list == NULL) return;
if(!list->readonly) {
efree(list->data);
list->data = NULL;
}
efree(list);
}
void
reclaimalldatalists(void)
{
for(size_t i=0;i<listlength(alldatalists);i++) {
Datalist* di = listget(alldatalists,i);
if(di != NULL)
reclaimdatalist(di);
}
efree(alldatalists);
alldatalists = NULL;
}
static void
flattenR(Datalist* result, Datalist* data, int rank, int depth)
{
int i;
NCConstant* con;
if(rank == depth) return;
if(datalistlen(data) == 0) return;
for(i=0;i<datalistlen(data);i++) {
con = datalistith(data,i);
if(depth < rank - 1) {
/* Is this is a char list, then we might have short depth */
if(islistconst(con))
flattenR(result,compoundfor(con),rank,depth+1);
else
dlappend(result,con);
} else { /* depth == rank -1, last dimension */
dlappend(result,con);
}
}
}
/* Produce a new list that is the concat of all the leaf constants */
Datalist*
flatten(Datalist* list,int rank)
{
Datalist* result = builddatalist(0);
flattenR(result,list,rank,0);
return result;
}