netcdf-c/libsrc/var.c
Dennis Heimbigner 3db4f013bf Primary change: add dap4 support
Specific changes:
1. Add dap4 code: libdap4 and dap4_test.
   Note that until the d4ts server problem is solved, dap4 is turned off.
2. Modify various files to support dap4 flags:
	configure.ac, Makefile.am, CMakeLists.txt, etc.
3. Add nc_test/test_common.sh. This centralizes
   the handling of the locations of various
   things in the build tree: e.g. where is
   ncgen.exe located. See nc_test/test_common.sh
   for details.
4. Modify .sh files to use test_common.sh
5. Obsolete separate oc2 by moving it to be part of
   netcdf-c. This means replacing code with netcdf-c
   equivalents.
5. Add --with-testserver to configure.ac to allow
   override of the servers to be used for --enable-dap-remote-tests.
6. There were multiple versions of nctypealignment code. Try to
   centralize in libdispatch/doffset.c and include/ncoffsets.h
7. Add a unit test for the ncuri code because of its complexity.
8. Move the findserver code out of libdispatch and into
   a separate, self contained program in ncdap_test and dap4_test.
9. Move the dispatch header files (nc{3,4}dispatch.h) to
   .../include because they are now shared by modules.
10. Revamp the handling of TOPSRCDIR and TOPBUILDDIR for shell scripts.
11. Make use of MREMAP if available
12. Misc. minor changes e.g.
	- #include <config.h> -> #include "config.h"
	- Add some no-install headers to /include
	- extern -> EXTERNL and vice versa as needed
	- misc header cleanup
	- clean up checking for misc. unix vs microsoft functions
13. Change copyright decls in some files to point to LICENSE file.
14. Add notes to RELEASENOTES.md
2017-03-08 17:01:10 -07:00

802 lines
15 KiB
C

/*
* Copyright 1996, University Corporation for Atmospheric Research
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*/
/* $Id: var.c,v 1.144 2010/05/30 00:50:35 russ Exp $ */
#include "config.h"
#include "nc3internal.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include "ncx.h"
#include "rnd.h"
#include "ncutf8.h"
#ifndef OFF_T_MAX
#if 0
#define OFF_T_MAX (~ (off_t) 0 - (~ (off_t) 0 << (CHAR_BIT * sizeof (off_t) - 1)))
#endif
/* The behavior above is undefined, re: bitshifting a negative value, according
to warnings thrown by clang/gcc. An alternative OFF_T_MAX was written
based on info found at:
* http://stackoverflow.com/questions/4514572/c-question-off-t-and-other-signed-integer-types-minimum-and-maximum-values
*/
#define MAX_INT_VAL_STEP(t) \
((t) 1 << (CHAR_BIT * sizeof(t) - 1 - ((t) -1 < 1)))
#define MAX_INT_VAL(t) \
((MAX_INT_VAL_STEP(t) - 1) + MAX_INT_VAL_STEP(t))
#define MIN_INT_VAL(t) \
((t) -MAX_INT_VAL(t) - 1)
#define OFF_T_MAX MAX_INT_VAL(off_t)
#endif
/*
* Free var
* Formerly NC_free_var(var)
*/
void
free_NC_var(NC_var *varp)
{
if(varp == NULL)
return;
free_NC_attrarrayV(&varp->attrs);
free_NC_string(varp->name);
#ifndef MALLOCHACK
if(varp->dimids != NULL) free(varp->dimids);
if(varp->shape != NULL) free(varp->shape);
if(varp->dsizes != NULL) free(varp->dsizes);
#endif /*!MALLOCHACK*/
free(varp);
}
/*
* Common code for new_NC_var()
* and ncx_get_NC_var()
*/
NC_var *
new_x_NC_var(
NC_string *strp,
size_t ndims)
{
NC_var *varp;
const size_t o1 = M_RNDUP(ndims * sizeof(int));
const size_t o2 = M_RNDUP(ndims * sizeof(size_t));
#ifdef MALLOCHACK
const size_t sz = M_RNDUP(sizeof(NC_var)) +
o1 + o2 + ndims * sizeof(off_t);
#else /*!MALLOCHACK*/
const size_t o3 = ndims * sizeof(off_t);
const size_t sz = sizeof(NC_var);
#endif /*!MALLOCHACK*/
varp = (NC_var *) malloc(sz);
if(varp == NULL )
return NULL;
(void) memset(varp, 0, sz);
varp->name = strp;
varp->ndims = ndims;
if(ndims != 0)
{
#ifdef MALLOCHACK
/*
* NOTE: lint may complain about the next 3 lines:
* "pointer cast may result in improper alignment".
* We use the M_RNDUP() macro to get the proper alignment.
*/
varp->dimids = (int *)((char *)varp + M_RNDUP(sizeof(NC_var)));
varp->shape = (size_t *)((char *)varp->dimids + o1);
varp->dsizes = (off_t *)((char *)varp->shape + o2);
#else /*!MALLOCHACK*/
varp->dimids = (int*)malloc(o1);
varp->shape = (size_t*)malloc(o2);
varp->dsizes = (off_t*)malloc(o3);
#endif /*!MALLOCHACK*/
} else {
varp->dimids = NULL;
varp->shape = NULL;
varp->dsizes=NULL;
}
varp->xsz = 0;
varp->len = 0;
varp->begin = 0;
return varp;
}
/*
* Formerly
NC_new_var()
*/
static NC_var *
new_NC_var(const char *uname, nc_type type,
size_t ndims, const int *dimids)
{
NC_string *strp = NULL;
NC_var *varp = NULL;
int stat;
char* name;
stat = nc_utf8_normalize((const unsigned char *)uname,(unsigned char **)&name);
if(stat != NC_NOERR)
return NULL;
strp = new_NC_string(strlen(name), name);
free(name);
if(strp == NULL)
return NULL;
varp = new_x_NC_var(strp, ndims);
if(varp == NULL )
{
free_NC_string(strp);
return NULL;
}
varp->type = type;
if( ndims != 0 && dimids != NULL)
(void) memcpy(varp->dimids, dimids, ndims * sizeof(int));
else
varp->dimids=NULL;
return(varp);
}
static NC_var *
dup_NC_var(const NC_var *rvarp)
{
NC_var *varp = new_NC_var(rvarp->name->cp, rvarp->type,
rvarp->ndims, rvarp->dimids);
if(varp == NULL)
return NULL;
if(dup_NC_attrarrayV(&varp->attrs, &rvarp->attrs) != NC_NOERR)
{
free_NC_var(varp);
return NULL;
}
(void) memcpy(varp->shape, rvarp->shape,
rvarp->ndims * sizeof(size_t));
(void) memcpy(varp->dsizes, rvarp->dsizes,
rvarp->ndims * sizeof(size_t));
varp->xsz = rvarp->xsz;
varp->len = rvarp->len;
varp->begin = rvarp->begin;
return varp;
}
/* vararray */
/*
* Free the stuff "in" (referred to by) an NC_vararray.
* Leaves the array itself allocated.
*/
void
free_NC_vararrayV0(NC_vararray *ncap)
{
assert(ncap != NULL);
if(ncap->nelems == 0)
return;
assert(ncap->value != NULL);
{
NC_var **vpp = ncap->value;
NC_var *const *const end = &vpp[ncap->nelems];
for( /*NADA*/; vpp < end; vpp++)
{
free_NC_var(*vpp);
*vpp = NULL;
}
}
ncap->nelems = 0;
}
/*
* Free NC_vararray values.
* formerly
NC_free_array()
*/
void
free_NC_vararrayV(NC_vararray *ncap)
{
assert(ncap != NULL);
if(ncap->nalloc == 0)
return;
NC_hashmapDelete(ncap->hashmap);
ncap->hashmap = NULL;
assert(ncap->value != NULL);
free_NC_vararrayV0(ncap);
free(ncap->value);
ncap->value = NULL;
ncap->nalloc = 0;
}
int
dup_NC_vararrayV(NC_vararray *ncap, const NC_vararray *ref)
{
int status = NC_NOERR;
assert(ref != NULL);
assert(ncap != NULL);
if(ref->nelems != 0)
{
const size_t sz = ref->nelems * sizeof(NC_var *);
ncap->value = (NC_var **) malloc(sz);
if(ncap->value == NULL)
return NC_ENOMEM;
(void) memset(ncap->value, 0, sz);
ncap->nalloc = ref->nelems;
}
ncap->nelems = 0;
{
NC_var **vpp = ncap->value;
const NC_var **drpp = (const NC_var **)ref->value;
NC_var *const *const end = &vpp[ref->nelems];
for( /*NADA*/; vpp < end; drpp++, vpp++, ncap->nelems++)
{
*vpp = dup_NC_var(*drpp);
if(*vpp == NULL)
{
status = NC_ENOMEM;
break;
}
}
}
if(status != NC_NOERR)
{
free_NC_vararrayV(ncap);
return status;
}
assert(ncap->nelems == ref->nelems);
return NC_NOERR;
}
/*
* Add a new handle on the end of an array of handles
* Formerly
NC_incr_array(array, tail)
*/
static int
incr_NC_vararray(NC_vararray *ncap, NC_var *newelemp)
{
NC_var **vp;
assert(ncap != NULL);
if(ncap->nalloc == 0)
{
assert(ncap->nelems == 0);
vp = (NC_var **) malloc(NC_ARRAY_GROWBY * sizeof(NC_var *));
if(vp == NULL)
return NC_ENOMEM;
ncap->value = vp;
ncap->nalloc = NC_ARRAY_GROWBY;
ncap->hashmap = NC_hashmapCreate(0);
}
else if(ncap->nelems +1 > ncap->nalloc)
{
vp = (NC_var **) realloc(ncap->value,
(ncap->nalloc + NC_ARRAY_GROWBY) * sizeof(NC_var *));
if(vp == NULL)
return NC_ENOMEM;
ncap->value = vp;
ncap->nalloc += NC_ARRAY_GROWBY;
}
if(newelemp != NULL)
{
NC_hashmapAddVar(ncap, (long)ncap->nelems, newelemp->name->cp);
ncap->value[ncap->nelems] = newelemp;
ncap->nelems++;
}
return NC_NOERR;
}
static NC_var *
elem_NC_vararray(const NC_vararray *ncap, size_t elem)
{
assert(ncap != NULL);
/* cast needed for braindead systems with signed size_t */
if(ncap->nelems == 0 || (unsigned long)elem >= ncap->nelems)
return NULL;
assert(ncap->value != NULL);
return ncap->value[elem];
}
/* End vararray per se */
/*
* Step thru NC_VARIABLE array, seeking match on name.
* Return varid or -1 on not found.
* *varpp is set to the appropriate NC_var.
* Formerly (sort of)
NC_hvarid
*/
int
NC_findvar(const NC_vararray *ncap, const char *uname, NC_var **varpp)
{
int hash_var_id;
char *name;
int stat;
assert(ncap != NULL);
if(ncap->nelems == 0)
return -1;
/* normalized version of uname */
stat = nc_utf8_normalize((const unsigned char *)uname,(unsigned char **)&name);
if(stat != NC_NOERR)
return stat;
hash_var_id = (int)NC_hashmapGetVar(ncap, name);
free(name);
if (hash_var_id >= 0) {
if (varpp != NULL)
*varpp = ncap->value[hash_var_id];
return(hash_var_id); /* Normal return */
}
return(-1); /* not found */
}
/*
* For a netcdf type
* return the size of one element in the external representation.
* Note that arrays get rounded up to X_ALIGN boundaries.
* Formerly
NC_xtypelen
* See also ncx_len()
*/
size_t
ncx_szof(nc_type type)
{
switch(type){
case NC_BYTE:
case NC_CHAR:
case NC_UBYTE:
return(1);
case NC_SHORT :
return(2);
case NC_INT:
return X_SIZEOF_INT;
case NC_FLOAT:
return X_SIZEOF_FLOAT;
case NC_DOUBLE :
return X_SIZEOF_DOUBLE;
case NC_USHORT :
return X_SIZEOF_USHORT;
case NC_UINT :
return X_SIZEOF_UINT;
case NC_INT64 :
return X_SIZEOF_INT64;
case NC_UINT64 :
return X_SIZEOF_UINT64;
default:
assert("ncx_szof invalid type" == 0);
return 0;
}
}
/*
* 'compile' the shape and len of a variable
* Formerly
NC_var_shape(var, dims)
*/
int
NC_var_shape(NC_var *varp, const NC_dimarray *dims)
{
size_t *shp, *op;
off_t *dsp;
int *ip = NULL;
const NC_dim *dimp;
off_t product = 1;
varp->xsz = ncx_szof(varp->type);
if(varp->ndims == 0 || varp->dimids == NULL)
{
goto out;
}
/*
* use the user supplied dimension indices
* to determine the shape
*/
for(ip = varp->dimids, op = varp->shape
; ip < &varp->dimids[varp->ndims]; ip++, op++)
{
if(*ip < 0 || (size_t) (*ip) >= ((dims != NULL) ? dims->nelems : 1) )
return NC_EBADDIM;
dimp = elem_NC_dimarray(dims, (size_t)*ip);
*op = dimp->size;
if(*op == NC_UNLIMITED && ip != varp->dimids)
return NC_EUNLIMPOS;
}
/*
* Compute the dsizes
*/
/* ndims is > 0 here */
for(shp = varp->shape + varp->ndims -1,
dsp = varp->dsizes + varp->ndims -1;
shp >= varp->shape;
shp--, dsp--)
{
/*if(!(shp == varp->shape && IS_RECVAR(varp)))*/
if( shp != NULL && (shp != varp->shape || !IS_RECVAR(varp)))
{
if( ((off_t)(*shp)) <= OFF_T_MAX / product )
{
product *= (*shp > 0 ? *shp : 1);
} else
{
product = OFF_T_MAX ;
}
}
*dsp = product;
}
out :
if( varp->xsz <= (X_UINT_MAX - 1) / product ) /* if integer multiply will not overflow */
{
varp->len = product * varp->xsz;
switch(varp->type) {
case NC_BYTE :
case NC_CHAR :
case NC_UBYTE :
case NC_SHORT :
case NC_USHORT :
if( varp->len%4 != 0 )
{
varp->len += 4 - varp->len%4; /* round up */
/* *dsp += 4 - *dsp%4; */
}
break;
default:
/* already aligned */
break;
}
} else
{ /* OK for last var to be "too big", indicated by this special len */
varp->len = X_UINT_MAX;
}
#if 0
arrayp("\tshape", varp->ndims, varp->shape);
arrayp("\tdsizes", varp->ndims, varp->dsizes);
#endif
return NC_NOERR;
}
/*
* Check whether variable size is less than or equal to vlen_max,
* without overflowing in arithmetic calculations. If OK, return 1,
* else, return 0. For CDF1 format or for CDF2 format on non-LFS
* platforms, vlen_max should be 2^31 - 4, but for CDF2 format on
* systems with LFS it should be 2^32 - 4.
*/
int
NC_check_vlen(NC_var *varp, size_t vlen_max) {
size_t prod=varp->xsz; /* product of xsz and dimensions so far */
int ii;
assert(varp != NULL);
for(ii = IS_RECVAR(varp) ? 1 : 0; ii < varp->ndims; ii++) {
if(!varp->shape)
return 0; /* Shape is undefined/NULL. */
if (varp->shape[ii] > vlen_max / prod) {
return 0; /* size in bytes won't fit in a 32-bit int */
}
prod *= varp->shape[ii];
}
return 1; /* OK */
}
/*! Look up a variable by varid.
*
* Given a valid ncp structure and varid, return the var.
*
* Formerly NC_hlookupvar()
*
* @param[in] ncp NC3_INFO data structure.
* @param[in] varid The varid key for the var we are looking up.
* @param[out] varp Data structure to contain the varp pointer.
* @return Error code, if one exists, 0 otherwise.
*/
int NC_lookupvar(NC3_INFO* ncp, int varid, NC_var **varp)
{
if(varid == NC_GLOBAL)
{
/* Global is error in this context */
return NC_EGLOBAL;
}
if(varp)
*varp = elem_NC_vararray(&ncp->vars, (size_t)varid);
else
return NC_ENOTVAR;
if(*varp == NULL)
return NC_ENOTVAR;
return NC_NOERR;
}
/* Public */
int
NC3_def_var( int ncid, const char *name, nc_type type,
int ndims, const int *dimids, int *varidp)
{
int status;
NC *nc;
NC3_INFO* ncp;
int varid;
NC_var *varp = NULL;
status = NC_check_id(ncid, &nc);
if(status != NC_NOERR)
return status;
ncp = NC3_DATA(nc);
if(!NC_indef(ncp))
{
return NC_ENOTINDEFINE;
}
status = NC_check_name(name);
if(status != NC_NOERR)
return status;
status = nc3_cktype(nc->mode, type);
if(status != NC_NOERR)
return status;
/* cast needed for braindead systems with signed size_t */
if((unsigned long) ndims > X_INT_MAX) /* Backward compat */
{
return NC_EINVAL;
}
if(ncp->vars.nelems >= NC_MAX_VARS)
{
return NC_EMAXVARS;
}
varid = NC_findvar(&ncp->vars, name, &varp);
if(varid != -1)
{
return NC_ENAMEINUSE;
}
varp = new_NC_var(name, type, ndims, dimids);
if(varp == NULL)
return NC_ENOMEM;
status = NC_var_shape(varp, &ncp->dims);
if(status != NC_NOERR)
{
free_NC_var(varp);
return status;
}
status = incr_NC_vararray(&ncp->vars, varp);
if(status != NC_NOERR)
{
free_NC_var(varp);
return status;
}
if(varidp != NULL)
*varidp = (int)ncp->vars.nelems -1; /* varid */
return NC_NOERR;
}
int
NC3_inq_varid(int ncid, const char *name, int *varid_ptr)
{
int status;
NC *nc;
NC3_INFO* ncp;
NC_var *varp;
int varid;
status = NC_check_id(ncid, &nc);
if(status != NC_NOERR)
return status;
ncp = NC3_DATA(nc);
varid = NC_findvar(&ncp->vars, name, &varp);
if(varid == -1)
{
return NC_ENOTVAR;
}
*varid_ptr = varid;
return NC_NOERR;
}
int
NC3_inq_var(int ncid,
int varid,
char *name,
nc_type *typep,
int *ndimsp,
int *dimids,
int *nattsp)
{
int status;
NC *nc;
NC3_INFO* ncp;
NC_var *varp;
size_t ii;
status = NC_check_id(ncid, &nc);
if(status != NC_NOERR)
return status;
ncp = NC3_DATA(nc);
varp = elem_NC_vararray(&ncp->vars, (size_t)varid);
if(varp == NULL)
return NC_ENOTVAR;
if(name != NULL)
{
(void) strncpy(name, varp->name->cp, varp->name->nchars);
name[varp->name->nchars] = 0;
}
if(typep != 0)
*typep = varp->type;
if(ndimsp != 0)
{
*ndimsp = (int) varp->ndims;
}
if(dimids != 0)
{
for(ii = 0; ii < varp->ndims; ii++)
{
dimids[ii] = varp->dimids[ii];
}
}
if(nattsp != 0)
{
*nattsp = (int) varp->attrs.nelems;
}
return NC_NOERR;
}
int
NC3_rename_var(int ncid, int varid, const char *unewname)
{
int status;
NC *nc;
NC3_INFO* ncp;
NC_var *varp;
NC_string *old, *newStr;
int other;
char *newname; /* normalized */
status = NC_check_id(ncid, &nc);
if(status != NC_NOERR)
return status;
ncp = NC3_DATA(nc);
if(NC_readonly(ncp))
{
return NC_EPERM;
}
status = NC_check_name(unewname);
if(status != NC_NOERR)
return status;
/* check for name in use */
other = NC_findvar(&ncp->vars, unewname, &varp);
if(other != -1)
{
return NC_ENAMEINUSE;
}
status = NC_lookupvar(ncp, varid, &varp);
if(status != NC_NOERR)
{
/* invalid varid */
return status;
}
old = varp->name;
status = nc_utf8_normalize((const unsigned char *)unewname,(unsigned char **)&newname);
if(status != NC_NOERR)
return status;
if(NC_indef(ncp))
{
/* Remove old name from hashmap; add new... */
NC_hashmapRemoveVar(&ncp->vars, old->cp);
newStr = new_NC_string(strlen(newname),newname);
free(newname);
if(newStr == NULL)
return(-1);
varp->name = newStr;
NC_hashmapAddVar(&ncp->vars, varid, newStr->cp);
free_NC_string(old);
return NC_NOERR;
}
/* else, not in define mode */
/* Remove old name from hashmap; add new... */
NC_hashmapRemoveVar(&ncp->vars, old->cp);
status = set_NC_string(varp->name, newname);
free(newname);
if(status != NC_NOERR)
return status;
NC_hashmapAddVar(&ncp->vars, varid, varp->name->cp);
set_NC_hdirty(ncp);
if(NC_doHsync(ncp))
{
status = NC_sync(ncp);
if(status != NC_NOERR)
return status;
}
return NC_NOERR;
}