Files
2026-08-03 18:42:47 +08:00

460 lines
12 KiB
C

/* Copyright 2018, UCAR/Unidata
See the COPYRIGHT file for more information. */
#ifndef NCVUTILS_H
#define NCVUTILS_H 1
/* Define a header-only simple version of a dynamically expandable list and byte buffer */
/* To be used in code that should be independent of libnetcdf */
typedef struct VList {
size_t alloc;
size_t length;
void** content;
} VList;
typedef struct VString {
int nonextendible; /* 1 => fail if an attempt is made to extend this string*/
size_t alloc;
size_t length;
char* content;
} VString;
/* VString has a fixed expansion size */
#define VSTRALLOC 64
#if defined(_CPLUSPLUS_) || defined(__CPLUSPLUS__)
#define EXTERNC extern "C"
#else
#define EXTERNC extern
#endif
static int util_initialized = 0;
static void util_initialize(void);
/**************************************************/
/* Forward */
static VList* vlistnew(void);
static void vlistfree(VList* l);
static void vlistexpand(VList* l);
static void* vlistget(VList* l, size_t index) /* Return the ith element of l */;
static void vlistpush(VList* l, void* elem);
static void* vlistfirst(VList* l) /* remove first element */;
static void vlistinsert(VList* l, size_t pos, void* elem);
static void* vlistremove(VList* l, size_t pos);
static void vlistfreeall(VList* l) /* call free() on each list element*/;
static VString* vsnew(void);
static void vsfree(VString* vs);
static void vsexpand(VString* vs);
static void vsappendn(VString* vs, const char* elem, size_t n);
static void vsappend(VString* vs, const char elem);
static void vsinsertn(VString* vs, size_t pos, const void* s, size_t slen);
static void vsremoven(VString* vs, size_t pos, size_t elide);
static void vssetalloc(VString* vs, size_t newalloc);
static void vssetlength(VString* vs, size_t newlen);
static void vssetcontents(VString* vs, char* contents, size_t length);
static char* vsextract(VString* vs);
static char* vsgetp(VString* vs, size_t index);
static VString* vsclone(VString* vs);
/* Following are always "in-lined"*/
#define vlistcontents(l) ((l)==NULL?NULL:(l)->content)
#define vlistlength(l) ((l)==NULL?0:(l)->length)
#define vlistclear(l) vlistsetlength(l,0)
#define vlistsetlength(l,len) do{if((l)!=NULL) (l)->length=len;} while(0)
#define vscontents(vs) ((vs)==NULL?NULL:(vs)->content)
#define vslength(vs) ((vs)==NULL?0:(vs)->length)
#define vscat(vs,s) vsappendn(vs,s,0)
#define vsclear(vs) vssetlength(vs,0)
#define vsnulterm(vs) do{vssetalloc(vs,vs->length+1); vs->content[vs->length] = '\0';}while(0)
/**************************************************/
static VList*
vlistnew(void)
{
VList* l;
if(!util_initialized) util_initialize();
l = (VList*)calloc(1,sizeof(VList));
assert(l != NULL);
return l;
}
static void
vlistfree(VList* l)
{
if(l == NULL) return;
if(l->content != NULL) {free(l->content); l->content = NULL;}
free(l);
}
static void
vlistexpand(VList* l)
{
void** newcontent = NULL;
size_t newsz;
if(l == NULL) return;
newsz = (l->length * 2) + 1; /* basically double allocated space */
if(l->alloc >= newsz) return; /* space already allocated */
newcontent=(void**)calloc(newsz,sizeof(void*));
assert(newcontent != NULL);
if(l->alloc > 0 && l->length > 0 && l->content != NULL) { /* something to copy */
memcpy((void*)newcontent,(void*)l->content,sizeof(void*)*l->length);
}
if(l->content != NULL) free(l->content);
l->content=newcontent;
l->alloc=newsz;
/* size is the same */
}
static void*
vlistget(VList* l, size_t index) /* Return the ith element of l */
{
if(l == NULL || l->length == 0) return NULL;
assert(index < l->length);
return l->content[index];
}
static void
vlistpush(VList* l, void* elem)
{
if(l == NULL) return;
while(l->alloc < (l->length+1)) vlistexpand(l);
l->content[l->length] = elem;
l->length++;
}
static void*
vlistfirst(VList* l) /* remove first element */
{
size_t i,len;
void* elem;
if(l == NULL || l->length == 0) return NULL;
elem = l->content[0];
len = l->length;
for(i=1;i<len;i++) l->content[i-1] = l->content[i];
l->length--;
return elem;
}
static void
vlistinsert(VList* l, size_t pos, void* elem)
{
size_t delta;
assert(l != NULL);
if(pos >= vlistlength(l)) return;
while(l->alloc < (l->length+1)) vlistexpand(l);
assert(l->length == pos);
delta = (l->length - pos);
memmove(l->content+pos+1,l->content+pos,sizeof(void*)*delta);
l->length++;
l->content[pos] = elem;
}
static void*
vlistremove(VList* l, size_t pos)
{
size_t delta;
void* old = NULL;
assert(l != NULL && l->length > 0);
assert(pos < vlistlength(l));
old = l->content[pos];
assert(l->length > pos);
delta = (l->length - pos) - 1;
memmove(l->content+pos,l->content+pos+1,sizeof(void*)*delta);
l->length++;
return old;
}
static void
vlistfreeall(VList* l) /* call free() on each list element*/
{
unsigned i;
if(l == NULL || l->length == 0) return;
for(i=0;i<l->length;i++) if(l->content[i] != NULL) {free(l->content[i]);}
vlistfree(l);
}
static VString*
vsnew(void)
{
VString* vs = NULL;
if(!util_initialized) util_initialize();
vs = (VString*)calloc(1,sizeof(VString));
assert(vs != NULL);
return vs;
}
static void
vsfree(VString* vs)
{
if(vs == NULL) return;
if(vs->content != NULL) free(vs->content);
free(vs);
}
static void
vsexpand(VString* vs)
{
char* newcontent = NULL;
size_t newsz;
if(vs == NULL) return;
assert(vs->nonextendible == 0);
newsz = (vs->alloc + VSTRALLOC); /* increasse allocated space by fixed amount */
if(vs->alloc >= newsz) return; /* space already allocated */
newcontent=(char*)calloc(1,newsz+1);/* always room for nul term */
assert(newcontent != NULL);
if(vs->alloc > 0 && vs->length > 0 && vs->content != NULL) /* something to copy */
memcpy((void*)newcontent,(void*)vs->content,vs->length);
newcontent[vs->length] = '\0'; /* ensure null terminated */
if(vs->content != NULL) free(vs->content);
vs->content=newcontent;
vs->alloc=newsz;
/* length is the same */
}
static void
vsappendn(VString* vs, const char* elem, size_t n)
{
size_t need;
assert(vs != NULL && elem != NULL);
if(n == 0) {n = strlen(elem);}
need = vs->length + n;
if(vs->nonextendible) {
/* Space must already be available */
assert(vs->alloc >= need);
} else {
while(vs->alloc < need)
vsexpand(vs);
}
memcpy(&vs->content[vs->length],elem,n);
vs->length += n;
if(!vs->nonextendible)
vsnulterm(vs); /* guarantee nul term */
}
static void
vsappend(VString* vs, char elem)
{
char s[2];
s[0] = elem;
s[1] = '\0';
vsappendn(vs,s,1);
}
/**
Insert seq of elems s where |s|==n*elems into vs at position pos.
@param vs
@param pos where to insert; if pos > |vs->content| then expand vs.
@param elems to insert
@param elem no. of elems in elems
@return void
*/
static void
vsinsertn(VString* vs, size_t pos, const void* s, size_t slen)
{
size_t totalspace = 0;
size_t vslen = 0;
assert(vs != NULL && s != NULL);
if(slen == 0) {slen = strlen(s);}
vslen = vslength(vs);
#if 0
initial: |len.........|
case 1: |pos....||...slen...||...len - pos...|
case 2: |pos==len....||...slen...|
case 3: |len.........||...pos-len...||...slen...|
#endif
if(pos < vslen) { /* Case 1 */
/* make space for slen bytes at pos */
totalspace = vslen+slen;
vssetalloc(vs,(totalspace));
vssetlength(vs,(totalspace));
memmove((void*)(vs->content+pos+slen),(void*)(vs->content+pos),(vslen - pos));
memcpy((void*)(vs->content+(pos)),(void*)s,slen);
} else if(pos == vslen) { /* Case 2 */
/* make space for slen bytes at pos (== length) */
totalspace = vslen+slen;
vssetalloc(vs,(totalspace));
vssetlength(vs,(totalspace));
/* append s at pos */
memcpy((void*)(vs->content+(pos)),(void*)s,slen);
} else /*pos > vslen */ { /* Case 3 */
/* make space for slen bytes at pos */
totalspace = pos + slen;
vssetalloc(vs,(totalspace));
vssetlength(vs,(totalspace));
/* clear space from length..pos */
memset((void*)(vs->content+(vslen)),0,(pos-vslen));
/* append s at pos */
memmove((void*)(vs->content+((pos+slen))),(void*)(vs->content+(pos)),(vslen - pos));
memcpy((void*)(vs->content+(pos)),(void*)s,slen);
}
vsnulterm(vs);
}
/**
Remove seq of elems s where |s|==n*elems starting at position pos.
@param vs
@param pos where to remove; if pos > |vs->content| then do nothing
@param elide no. of elems remove
@return void
Side effect: reduce index by elided amount if index is past pos
*/
static void
vsremoven(VString* vs, size_t pos, size_t elide)
{
size_t vslen = 0;
size_t srcpos = 0;
size_t srclen = 0; /* amount to memmove */
assert(vs != NULL);
if(elide == 0) goto done; /* nothing to move */
vslen = vslength(vs);
srcpos = pos + elide;
/* edge cases */
if(srcpos >= vslen) { /* remove everything from pos to vslen */
vssetlength(vs,pos);
} else {
srclen = (vslen - srcpos);
memmove((void*)(vscontents(vs)+(pos)),(void*)(vscontents(vs)+(srcpos)),srclen);
vssetlength(vs,vslen - elide);
}
done:
vsnulterm(vs);
}
/**
Set the allocated capacity of the VString's capacity.
@param vs the array to expand
@param newalloc make sure alloc is at least this amount
@return void
*/
static void
vssetalloc(VString* vs, size_t newalloc)
{
while(vs->alloc < newalloc) vsexpand(vs);
}
static void
vssetlength(VString* vs, size_t newlen)
{
size_t oldlen;
assert(vs != NULL);
oldlen = vs->length;
if(newlen > oldlen) {
vssetalloc(vs,newlen+1);
}
vs->length = newlen;
vsnulterm(vs);
}
/* Set unexpandible contents */
static void
vssetcontents(VString* vs, char* contents, size_t length)
{
assert(vs != NULL && contents != NULL);
vs->length = 0;
if(!vs->nonextendible && vs->content != NULL) free(vs->content);
vs->content = contents;
vs->length = length;
vs->alloc = length;
vs->nonextendible = 1;
}
static char*
vsgetp(VString* vs, size_t pos)
{
assert(vs->length >= pos);
return vs->content + (pos);
}
/**
Shallow clone a VString object.
@param v the variable-length string to clone
@return ptr to clone
*/
static VString*
vsclone(VString* vs)
{
VString* clone = NULL;
clone = (VString*)calloc(1,sizeof(VString));
assert(vs != NULL);
*clone = *vs; /* copy the fields */
if(clone->content != NULL) {
clone->content = (char*)calloc(1,clone->alloc);
assert(clone->content != NULL);
if(clone->length > 0)
memcpy(clone->content,vs->content,clone->length+1);
vsnulterm(clone);
}
return clone;
}
/* Extract the content and leave content null */
static char*
vsextract(VString* vs)
{
char* x = NULL;
if(vs == NULL) return NULL;
if(vs->content == NULL) {
/* guarantee content existence and nul terminated */
if((vs->content = calloc(1,sizeof(char)))==NULL) return NULL;
vs->length = 0;
}
x = vs->content;
vs->content = NULL;
vs->length = 0;
vs->alloc = 0;
return x;
}
static void
util_initialize(void)
{
/* quiet compiler */
void* f = NULL;
f = f;
f = (void*)vlistnew;
f = (void*)vlistfree;
f = (void*)vlistexpand;
f = (void*)vlistget;
f = (void*)vlistinsert;
f = (void*)vlistremove;
f = (void*)vlistpush;
f = (void*)vlistfirst;
f = (void*)vlistfreeall;
f = (void*)vsnew;
f = (void*)vsfree;
f = (void*)vsexpand;
f = (void*)vssetcontents;
f = (void*)vsinsertn;
f = (void*)vsremoven;
f = (void*)vsappendn;
f = (void*)vsappend;
f = (void*)vsextract;
f = (void*)vsclone;
f = (void*)vsgetp;
util_initialized = 1;
}
/* Following are always "in-lined"*/
#define vlistcontents(l) ((l)==NULL?NULL:(l)->content)
#define vlistlength(l) ((l)==NULL?0:(l)->length)
#define vlistclear(l) vlistsetlength(l,0)
#define vlistsetlength(l,len) do{if((l)!=NULL) (l)->length=len;} while(0)
#define vscontents(vs) ((vs)==NULL?NULL:(vs)->content)
#define vslength(vs) ((vs)==NULL?0:(vs)->length)
#define vscat(vs,s) vsappendn(vs,s,0)
#define vsclear(vs) vssetlength(vs,0)
#define vssetlength(vs,len) do{if((vs)!=NULL) (vs)->length=len;} while(0)
#endif /*NCVUTIL_H*/