mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-11-21 03:13:42 +08:00
738 lines
13 KiB
C
738 lines
13 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 "nc.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
#include "ncx.h"
|
|
#include "rnd.h"
|
|
#include "utf8proc.h"
|
|
|
|
#ifndef OFF_T_MAX
|
|
#define OFF_T_MAX (~ (off_t) 0 - (~ (off_t) 0 << (CHAR_BIT * sizeof (off_t) - 1)))
|
|
#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*/
|
|
}
|
|
|
|
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;
|
|
NC_var *varp;
|
|
|
|
char *name = (char *)utf8proc_NFC((const unsigned char *)uname);
|
|
if(name == NULL)
|
|
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));
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
NC_var **loc;
|
|
size_t slen;
|
|
int varid;
|
|
char *name;
|
|
|
|
assert(ncap != NULL);
|
|
|
|
if(ncap->nelems == 0)
|
|
return -1;
|
|
|
|
loc = (NC_var **) ncap->value;
|
|
|
|
/* normalized version of uname */
|
|
name = (char *)utf8proc_NFC((const unsigned char *)uname);
|
|
if(name == NULL)
|
|
return NC_ENOMEM;
|
|
slen = strlen(name);
|
|
|
|
for(varid = 0; (size_t) varid < ncap->nelems; varid++, loc++)
|
|
{
|
|
if(strlen((*loc)->name->cp) == slen &&
|
|
strncmp((*loc)->name->cp, name, slen) == 0)
|
|
{
|
|
if(varpp != NULL)
|
|
*varpp = *loc;
|
|
free(name);
|
|
return(varid); /* Normal return */
|
|
}
|
|
}
|
|
free(name);
|
|
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:
|
|
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;
|
|
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;
|
|
const NC_dim *dimp;
|
|
off_t product = 1;
|
|
|
|
varp->xsz = ncx_szof(varp->type);
|
|
|
|
if(varp->ndims == 0)
|
|
{
|
|
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( (off_t)(*shp) <= OFF_T_MAX / product )
|
|
{
|
|
product *= *shp;
|
|
} 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_SHORT :
|
|
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[ii] > vlen_max / prod) {
|
|
return 0; /* size in bytes won't fit in a 32-bit int */
|
|
}
|
|
prod *= varp->shape[ii];
|
|
}
|
|
return 1; /* OK */
|
|
}
|
|
|
|
|
|
/*
|
|
* Given valid ncp and varid, return var
|
|
* else NULL on error
|
|
* Formerly
|
|
NC_hlookupvar()
|
|
*/
|
|
NC_var *
|
|
NC_lookupvar(NC *ncp, int varid)
|
|
{
|
|
NC_var *varp;
|
|
|
|
if(varid == NC_GLOBAL)
|
|
{
|
|
/* Global is error in this context */
|
|
return(NULL);
|
|
}
|
|
|
|
varp = elem_NC_vararray(&ncp->vars, (size_t)varid);
|
|
if(varp == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
assert(varp != NULL);
|
|
|
|
return(varp);
|
|
}
|
|
|
|
|
|
/* Public */
|
|
|
|
int
|
|
NC3_def_var( int ncid, const char *name, nc_type type,
|
|
int ndims, const int *dimids, int *varidp)
|
|
{
|
|
int status;
|
|
NC *ncp;
|
|
int varid;
|
|
NC_var *varp;
|
|
|
|
status = NC_check_id(ncid, &ncp);
|
|
if(status != NC_NOERR)
|
|
return status;
|
|
|
|
if(!NC_indef(ncp))
|
|
{
|
|
return NC_ENOTINDEFINE;
|
|
}
|
|
|
|
status = NC_check_name(name);
|
|
if(status != NC_NOERR)
|
|
return status;
|
|
|
|
status = nc_cktype(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 *ncp;
|
|
NC_var *varp;
|
|
int varid;
|
|
|
|
status = NC_check_id(ncid, &ncp);
|
|
if(status != NC_NOERR)
|
|
return status;
|
|
|
|
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 *ncp;
|
|
NC_var *varp;
|
|
size_t ii;
|
|
|
|
status = NC_check_id(ncid, &ncp);
|
|
if(status != NC_NOERR)
|
|
return status;
|
|
|
|
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 *ncp;
|
|
NC_var *varp;
|
|
NC_string *old, *newStr;
|
|
int other;
|
|
char *newname; /* normalized */
|
|
|
|
status = NC_check_id(ncid, &ncp);
|
|
if(status != NC_NOERR)
|
|
return status;
|
|
|
|
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;
|
|
}
|
|
|
|
varp = NC_lookupvar(ncp, varid);
|
|
if(varp == NULL)
|
|
{
|
|
/* invalid varid */
|
|
return NC_ENOTVAR; /* TODO: is this the right error code? */
|
|
}
|
|
|
|
old = varp->name;
|
|
newname = (char *)utf8proc_NFC((const unsigned char *)unewname);
|
|
if(newname == NULL)
|
|
return NC_ENOMEM;
|
|
if(NC_indef(ncp))
|
|
{
|
|
newStr = new_NC_string(strlen(newname),newname);
|
|
free(newname);
|
|
if(newStr == NULL)
|
|
return(-1);
|
|
varp->name = newStr;
|
|
free_NC_string(old);
|
|
return NC_NOERR;
|
|
}
|
|
|
|
/* else, not in define mode */
|
|
status = set_NC_string(varp->name, newname);
|
|
free(newname);
|
|
if(status != NC_NOERR)
|
|
return status;
|
|
|
|
set_NC_hdirty(ncp);
|
|
|
|
if(NC_doHsync(ncp))
|
|
{
|
|
status = NC_sync(ncp);
|
|
if(status != NC_NOERR)
|
|
return status;
|
|
}
|
|
|
|
return NC_NOERR;
|
|
}
|