netcdf-c/libsrc/v1hpg.c
Greg Sjaardema 1f9eb8093c Fix duplicate key issues in nc_hashmap
The addition of the nc_hashmap to facilitate quick
retrieval of var and dim by name did not take into
account key collisions -- two or more names hashed
to the same value.  If the keys matched, it assumed
that the names matched also.

This change fixes this incorrect assumption and
checks both the key (which is the hash of the name)
and if the keys match, it also checks that the names
match.

While there have been no instances of duplicate keys,
they are certain to occur and cause difficult to
debug issues. This fix eliminates that defect.
2016-04-05 09:07:40 -06:00

1527 lines
32 KiB
C

/*
* Copyright 1996, University Corporation for Atmospheric Research
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*/
#include "config.h"
#include "nc3internal.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "rnd.h"
#include "ncx.h"
/*
* This module defines the external representation
* of the "header" of a netcdf version one file and
* the version two variant that uses 64-bit file
* offsets instead of the 32-bit file offsets in version
* one files.
* For each of the components of the NC structure,
* There are (static) ncx_len_XXX(), v1h_put_XXX()
* and v1h_get_XXX() functions. These define the
* external representation of the components.
* The exported entry points for the whole NC structure
* are built up from these.
*/
/*
* "magic number" at beginning of file: 0x43444601 (big endian)
* assert(sizeof(ncmagic) % X_ALIGN == 0);
*/
static const schar ncmagic[] = {'C', 'D', 'F', 0x02};
static const schar ncmagic1[] = {'C', 'D', 'F', 0x01};
static const schar ncmagic5[] = {'C', 'D', 'F', 0x05};
/*
* v1hs == "Version 1 Header Stream"
*
* The netcdf file version 1 header is
* of unknown and potentially unlimited size.
* So, we don't know how much to get() on
* the initial read. We build a stream, 'v1hs'
* on top of ncio to do the header get.
*/
typedef struct v1hs {
ncio *nciop;
off_t offset; /* argument to nciop->get() */
size_t extent; /* argument to nciop->get() */
int flags; /* set to RGN_WRITE for write */
int version; /* format variant: NC_FORMAT_CLASSIC, NC_FORMAT_64BIT_OFFSET or NC_FORMAT_CDF5 */
void *base; /* beginning of current buffer */
void *pos; /* current position in buffer */
void *end; /* end of current buffer = base + extent */
} v1hs;
/*
* Release the stream, invalidate buffer
*/
static int
rel_v1hs(v1hs *gsp)
{
int status;
if(gsp->offset == OFF_NONE || gsp->base == NULL)
return NC_NOERR;
status = ncio_rel(gsp->nciop, gsp->offset,
gsp->flags == RGN_WRITE ? RGN_MODIFIED : 0);
gsp->end = NULL;
gsp->pos = NULL;
gsp->base = NULL;
return status;
}
/*
* Release the current chunk and get the next one.
* Also used for initialization when gsp->base == NULL.
*/
static int
fault_v1hs(v1hs *gsp, size_t extent)
{
int status;
if(gsp->base != NULL)
{
#ifdef __arm__
const ptrdiff_t incr = (signed char *)gsp->pos - (signed char *)gsp->base;
#else
const ptrdiff_t incr = (char *)gsp->pos - (char *)gsp->base;
#endif
status = rel_v1hs(gsp);
if(status)
return status;
gsp->offset += incr;
}
if(extent > gsp->extent)
gsp->extent = extent;
status = ncio_get(gsp->nciop,
gsp->offset, gsp->extent,
gsp->flags, &gsp->base);
if(status)
return status;
gsp->pos = gsp->base;
#ifdef __arm__
gsp->end = (signed char *)gsp->base + gsp->extent;
#else
gsp->end = (char *)gsp->base + gsp->extent;
#endif
return NC_NOERR;
}
/*
* Ensure that 'nextread' bytes are available.
*/
static int
check_v1hs(v1hs *gsp, size_t nextread)
{
#if 0 /* DEBUG */
fprintf(stderr, "nextread %lu, remaining %lu\n",
(unsigned long)nextread,
(unsigned long)((char *)gsp->end - (char *)gsp->pos));
#endif
#ifdef __arm__
if((signed char *)gsp->pos + nextread <= (signed char *)gsp->end)
return NC_NOERR;
#else
if((char *)gsp->pos + nextread <= (char *)gsp->end)
return NC_NOERR;
#endif
return fault_v1hs(gsp, nextread);
}
/* End v1hs */
/* Write a size_t to the header */
static int
v1h_put_size_t(v1hs *psp, const size_t *sp)
{
int status;
if (psp->version == 5) /* all integers in CDF-5 are 64 bits */
status = check_v1hs(psp, X_SIZEOF_INT64);
else
status = check_v1hs(psp, X_SIZEOF_SIZE_T);
if(status != NC_NOERR)
return status;
if (psp->version == 5)
return ncx_put_int64(&psp->pos, *sp);
else
return ncx_put_size_t(&psp->pos, sp);
}
/* Read a size_t from the header */
static int
v1h_get_size_t(v1hs *gsp, size_t *sp)
{
int status;
if (gsp->version == 5) /* all integers in CDF-5 are 64 bits */
status = check_v1hs(gsp, X_SIZEOF_INT64);
else
status = check_v1hs(gsp, X_SIZEOF_SIZE_T);
if(status != NC_NOERR)
return status;
if (gsp->version == 5) {
long long tmp=0;
status = ncx_get_int64((const void **)(&gsp->pos), &tmp);
*sp = (size_t)tmp;
return status;
}
else
return ncx_get_size_t((const void **)(&gsp->pos), sp);
}
/* Begin nc_type */
#define X_SIZEOF_NC_TYPE X_SIZEOF_INT
/* Write a nc_type to the header */
static int
v1h_put_nc_type(v1hs *psp, const nc_type *typep)
{
const int itype = (int) *typep;
int status = check_v1hs(psp, X_SIZEOF_INT);
if(status != NC_NOERR)
return status;
status = ncx_put_int_int(psp->pos, &itype);
#ifdef __arm__
psp->pos = (void *)((signed char *)psp->pos + X_SIZEOF_INT);
#else
psp->pos = (void *)((char *)psp->pos + X_SIZEOF_INT);
#endif
return status;
}
/* Read a nc_type from the header */
static int
v1h_get_nc_type(v1hs *gsp, nc_type *typep)
{
int type = 0;
int status = check_v1hs(gsp, X_SIZEOF_INT);
if(status != NC_NOERR)
return status;
status = ncx_get_int_int(gsp->pos, &type);
#ifdef __arm__
gsp->pos = (void *)((signed char *)gsp->pos + X_SIZEOF_INT);
#else
gsp->pos = (void *)((char *)gsp->pos + X_SIZEOF_INT);
#endif
if(status != NC_NOERR)
return status;
assert(type == NC_BYTE
|| type == NC_CHAR
|| type == NC_SHORT
|| type == NC_INT
|| type == NC_FLOAT
|| type == NC_DOUBLE
|| type == NC_UBYTE
|| type == NC_USHORT
|| type == NC_UINT
|| type == NC_INT64
|| type == NC_UINT64
|| type == NC_STRING);
/* else */
*typep = (nc_type) type;
return NC_NOERR;
}
/* End nc_type */
/* Begin NCtype (internal tags) */
#define X_SIZEOF_NCTYPE X_SIZEOF_INT
/* Write a NCtype to the header */
static int
v1h_put_NCtype(v1hs *psp, NCtype type)
{
const int itype = (int) type;
int status = check_v1hs(psp, X_SIZEOF_INT);
if(status != NC_NOERR)
return status;
status = ncx_put_int_int(psp->pos, &itype);
#ifdef __arm__
psp->pos = (void *)((signed char *)psp->pos + X_SIZEOF_INT);
#else
psp->pos = (void *)((char *)psp->pos + X_SIZEOF_INT);
#endif
return status;
}
/* Read a NCtype from the header */
static int
v1h_get_NCtype(v1hs *gsp, NCtype *typep)
{
int type = 0;
int status = check_v1hs(gsp, X_SIZEOF_INT);
if(status != NC_NOERR)
return status;
status = ncx_get_int_int(gsp->pos, &type);
#ifdef __arm__
gsp->pos = (void *)((signed char *)gsp->pos + X_SIZEOF_INT);
#else
gsp->pos = (void *)((char *)gsp->pos + X_SIZEOF_INT);
#endif
if(status != NC_NOERR)
return status;
/* else */
*typep = (NCtype) type;
return NC_NOERR;
}
/* End NCtype */
/* Begin NC_string */
/*
* How much space will the xdr'd string take.
* Formerly
NC_xlen_string(cdfstr)
*/
static size_t
ncx_len_NC_string(const NC_string *ncstrp, int version)
{
size_t sz = (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_INT; /* nchars */
assert(ncstrp != NULL);
if(ncstrp->nchars != 0)
{
#if 0
assert(ncstrp->nchars % X_ALIGN == 0);
sz += ncstrp->nchars;
#else
sz += _RNDUP(ncstrp->nchars, X_ALIGN);
#endif
}
return sz;
}
/* Write a NC_string to the header */
static int
v1h_put_NC_string(v1hs *psp, const NC_string *ncstrp)
{
int status;
#if 0
assert(ncstrp->nchars % X_ALIGN == 0);
#endif
status = v1h_put_size_t(psp, &ncstrp->nchars);
if(status != NC_NOERR)
return status;
status = check_v1hs(psp, _RNDUP(ncstrp->nchars, X_ALIGN));
if(status != NC_NOERR)
return status;
status = ncx_pad_putn_text(&psp->pos, ncstrp->nchars, ncstrp->cp);
if(status != NC_NOERR)
return status;
return NC_NOERR;
}
/* Read a NC_string from the header */
static int
v1h_get_NC_string(v1hs *gsp, NC_string **ncstrpp)
{
int status;
size_t nchars = 0;
NC_string *ncstrp;
status = v1h_get_size_t(gsp, &nchars);
if(status != NC_NOERR)
return status;
ncstrp = new_NC_string(nchars, NULL);
if(ncstrp == NULL)
{
return NC_ENOMEM;
}
#if 0
/* assert(ncstrp->nchars == nchars || ncstrp->nchars - nchars < X_ALIGN); */
assert(ncstrp->nchars % X_ALIGN == 0);
status = check_v1hs(gsp, ncstrp->nchars);
#else
status = check_v1hs(gsp, _RNDUP(ncstrp->nchars, X_ALIGN));
#endif
if(status != NC_NOERR)
goto unwind_alloc;
status = ncx_pad_getn_text((const void **)(&gsp->pos),
nchars, ncstrp->cp);
if(status != NC_NOERR)
goto unwind_alloc;
*ncstrpp = ncstrp;
return NC_NOERR;
unwind_alloc:
free_NC_string(ncstrp);
return status;
}
/* End NC_string */
/* Begin NC_dim */
/*
* How much space will the xdr'd dim take.
* Formerly
NC_xlen_dim(dpp)
*/
static size_t
ncx_len_NC_dim(const NC_dim *dimp, int version)
{
size_t sz;
assert(dimp != NULL);
sz = ncx_len_NC_string(dimp->name, version);
sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T;
return(sz);
}
/* Write a NC_dim to the header */
static int
v1h_put_NC_dim(v1hs *psp, const NC_dim *dimp)
{
int status;
status = v1h_put_NC_string(psp, dimp->name);
if(status != NC_NOERR)
return status;
status = v1h_put_size_t(psp, &dimp->size);
if(status != NC_NOERR)
return status;
return NC_NOERR;
}
/* Read a NC_dim from the header */
static int
v1h_get_NC_dim(v1hs *gsp, NC_dim **dimpp)
{
int status;
NC_string *ncstrp;
NC_dim *dimp;
status = v1h_get_NC_string(gsp, &ncstrp);
if(status != NC_NOERR)
return status;
dimp = new_x_NC_dim(ncstrp);
if(dimp == NULL)
{
status = NC_ENOMEM;
goto unwind_name;
}
status = v1h_get_size_t(gsp, &dimp->size);
if(status != NC_NOERR)
{
free_NC_dim(dimp); /* frees name */
return status;
}
*dimpp = dimp;
return NC_NOERR;
unwind_name:
free_NC_string(ncstrp);
return status;
}
/* How much space in the header is required for this NC_dimarray? */
static size_t
ncx_len_NC_dimarray(const NC_dimarray *ncap, int version)
{
size_t xlen = X_SIZEOF_NCTYPE; /* type */
xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
if(ncap == NULL)
return xlen;
/* else */
{
const NC_dim **dpp = (const NC_dim **)ncap->value;
const NC_dim *const *const end = &dpp[ncap->nelems];
for( /*NADA*/; dpp < end; dpp++)
{
xlen += ncx_len_NC_dim(*dpp,version);
}
}
return xlen;
}
/* Write a NC_dimarray to the header */
static int
v1h_put_NC_dimarray(v1hs *psp, const NC_dimarray *ncap)
{
int status;
assert(psp != NULL);
if(ncap == NULL
#if 1
/* Backward:
* This clause is for 'byte for byte'
* backward compatibility.
* Strickly speaking, it is 'bug for bug'.
*/
|| ncap->nelems == 0
#endif
)
{
/*
* Handle empty netcdf
*/
const size_t nosz = 0;
status = v1h_put_NCtype(psp, NC_UNSPECIFIED);
if(status != NC_NOERR)
return status;
status = v1h_put_size_t(psp, &nosz);
if(status != NC_NOERR)
return status;
return NC_NOERR;
}
/* else */
status = v1h_put_NCtype(psp, NC_DIMENSION);
if(status != NC_NOERR)
return status;
status = v1h_put_size_t(psp, &ncap->nelems);
if(status != NC_NOERR)
return status;
{
const NC_dim **dpp = (const NC_dim **)ncap->value;
const NC_dim *const *const end = &dpp[ncap->nelems];
for( /*NADA*/; dpp < end; dpp++)
{
status = v1h_put_NC_dim(psp, *dpp);
if(status)
return status;
}
}
return NC_NOERR;
}
/* Read a NC_dimarray from the header */
static int
v1h_get_NC_dimarray(v1hs *gsp, NC_dimarray *ncap)
{
int status;
NCtype type = NC_UNSPECIFIED;
assert(gsp != NULL && gsp->pos != NULL);
assert(ncap != NULL);
assert(ncap->value == NULL);
status = v1h_get_NCtype(gsp, &type);
if(status != NC_NOERR)
return status;
status = v1h_get_size_t(gsp, &ncap->nelems);
if(status != NC_NOERR)
return status;
if(ncap->nelems == 0)
return NC_NOERR;
/* else */
if(type != NC_DIMENSION)
return EINVAL;
ncap->value = (NC_dim **) malloc(ncap->nelems * sizeof(NC_dim *));
if(ncap->value == NULL)
return NC_ENOMEM;
ncap->nalloc = ncap->nelems;
ncap->hashmap = NC_hashmapCreate(ncap->nelems);
{
NC_dim **dpp = ncap->value;
NC_dim *const *const end = &dpp[ncap->nelems];
for( /*NADA*/; dpp < end; dpp++)
{
status = v1h_get_NC_dim(gsp, dpp);
if(status)
{
ncap->nelems = (size_t)(dpp - ncap->value);
free_NC_dimarrayV(ncap);
return status;
}
{
int dimid = (size_t)(dpp - ncap->value);
NC_hashmapAddDim(ncap, dimid, (*dpp)->name->cp);
}
}
}
return NC_NOERR;
}
/* End NC_dim */
/* Begin NC_attr */
/*
* How much space will 'attrp' take in external representation?
* Formerly
NC_xlen_attr(app)
*/
static size_t
ncx_len_NC_attr(const NC_attr *attrp, int version)
{
size_t sz;
assert(attrp != NULL);
sz = ncx_len_NC_string(attrp->name, version);
sz += X_SIZEOF_NC_TYPE; /* type */
sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* nelems */
sz += attrp->xsz;
return(sz);
}
#undef MIN
#define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn))
/*
* Put the values of an attribute
* The loop is necessary since attrp->nelems
* could potentially be quite large.
*/
static int
v1h_put_NC_attrV(v1hs *psp, const NC_attr *attrp)
{
int status;
const size_t perchunk = psp->extent;
size_t remaining = attrp->xsz;
void *value = attrp->xvalue;
size_t nbytes;
assert(psp->extent % X_ALIGN == 0);
do {
nbytes = MIN(perchunk, remaining);
status = check_v1hs(psp, nbytes);
if(status != NC_NOERR)
return status;
(void) memcpy(psp->pos, value, nbytes);
#ifdef __arm__
psp->pos = (void *)((signed char *)psp->pos + nbytes);
value = (void *)((signed char *)value + nbytes);
#else
psp->pos = (void *)((char *)psp->pos + nbytes);
value = (void *)((char *)value + nbytes);
#endif
remaining -= nbytes;
} while(remaining != 0);
return NC_NOERR;
}
/* Write a NC_attr to the header */
static int
v1h_put_NC_attr(v1hs *psp, const NC_attr *attrp)
{
int status;
status = v1h_put_NC_string(psp, attrp->name);
if(status != NC_NOERR)
return status;
status = v1h_put_nc_type(psp, &attrp->type);
if(status != NC_NOERR)
return status;
status = v1h_put_size_t(psp, &attrp->nelems);
if(status != NC_NOERR)
return status;
status = v1h_put_NC_attrV(psp, attrp);
if(status != NC_NOERR)
return status;
return NC_NOERR;
}
/*
* Get the values of an attribute
* The loop is necessary since attrp->nelems
* could potentially be quite large.
*/
static int
v1h_get_NC_attrV(v1hs *gsp, NC_attr *attrp)
{
int status;
const size_t perchunk = gsp->extent;
size_t remaining = attrp->xsz;
void *value = attrp->xvalue;
size_t nget;
do {
nget = MIN(perchunk, remaining);
status = check_v1hs(gsp, nget);
if(status != NC_NOERR)
return status;
(void) memcpy(value, gsp->pos, nget);
#ifdef __arm__
gsp->pos = (void *)((signed char *)gsp->pos + nget);
value = (void *)((signed char *)value + nget);
#else
gsp->pos = (void *)((char *)gsp->pos + nget);
value = (void *)((char *)value + nget);
#endif
remaining -= nget;
} while(remaining != 0);
return NC_NOERR;
}
/* Read a NC_attr from the header */
static int
v1h_get_NC_attr(v1hs *gsp, NC_attr **attrpp)
{
NC_string *strp;
int status;
nc_type type;
size_t nelems;
NC_attr *attrp;
status = v1h_get_NC_string(gsp, &strp);
if(status != NC_NOERR)
return status;
status = v1h_get_nc_type(gsp, &type);
if(status != NC_NOERR)
goto unwind_name;
status = v1h_get_size_t(gsp, &nelems);
if(status != NC_NOERR)
goto unwind_name;
attrp = new_x_NC_attr(strp, type, nelems);
if(attrp == NULL)
{
status = NC_ENOMEM;
goto unwind_name;
}
status = v1h_get_NC_attrV(gsp, attrp);
if(status != NC_NOERR)
{
free_NC_attr(attrp); /* frees strp */
return status;
}
*attrpp = attrp;
return NC_NOERR;
unwind_name:
free_NC_string(strp);
return status;
}
/* How much space in the header is required for this NC_attrarray? */
static size_t
ncx_len_NC_attrarray(const NC_attrarray *ncap, int version)
{
size_t xlen = X_SIZEOF_NCTYPE; /* type */
xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
if(ncap == NULL)
return xlen;
/* else */
{
const NC_attr **app = (const NC_attr **)ncap->value;
const NC_attr *const *const end = &app[ncap->nelems];
for( /*NADA*/; app < end; app++)
{
xlen += ncx_len_NC_attr(*app,version);
}
}
return xlen;
}
/* Write a NC_attrarray to the header */
static int
v1h_put_NC_attrarray(v1hs *psp, const NC_attrarray *ncap)
{
int status;
assert(psp != NULL);
if(ncap == NULL
#if 1
/* Backward:
* This clause is for 'byte for byte'
* backward compatibility.
* Strickly speaking, it is 'bug for bug'.
*/
|| ncap->nelems == 0
#endif
)
{
/*
* Handle empty netcdf
*/
const size_t nosz = 0;
status = v1h_put_NCtype(psp, NC_UNSPECIFIED);
if(status != NC_NOERR)
return status;
status = v1h_put_size_t(psp, &nosz);
if(status != NC_NOERR)
return status;
return NC_NOERR;
}
/* else */
status = v1h_put_NCtype(psp, NC_ATTRIBUTE);
if(status != NC_NOERR)
return status;
status = v1h_put_size_t(psp, &ncap->nelems);
if(status != NC_NOERR)
return status;
{
const NC_attr **app = (const NC_attr **)ncap->value;
const NC_attr *const *const end = &app[ncap->nelems];
for( /*NADA*/; app < end; app++)
{
status = v1h_put_NC_attr(psp, *app);
if(status)
return status;
}
}
return NC_NOERR;
}
/* Read a NC_attrarray from the header */
static int
v1h_get_NC_attrarray(v1hs *gsp, NC_attrarray *ncap)
{
int status;
NCtype type = NC_UNSPECIFIED;
assert(gsp != NULL && gsp->pos != NULL);
assert(ncap != NULL);
assert(ncap->value == NULL);
status = v1h_get_NCtype(gsp, &type);
if(status != NC_NOERR)
return status;
status = v1h_get_size_t(gsp, &ncap->nelems);
if(status != NC_NOERR)
return status;
if(ncap->nelems == 0)
return NC_NOERR;
/* else */
if(type != NC_ATTRIBUTE)
return EINVAL;
ncap->value = (NC_attr **) malloc(ncap->nelems * sizeof(NC_attr *));
if(ncap->value == NULL)
return NC_ENOMEM;
ncap->nalloc = ncap->nelems;
{
NC_attr **app = ncap->value;
NC_attr *const *const end = &app[ncap->nelems];
for( /*NADA*/; app < end; app++)
{
status = v1h_get_NC_attr(gsp, app);
if(status)
{
ncap->nelems = (size_t)(app - ncap->value);
free_NC_attrarrayV(ncap);
return status;
}
}
}
return NC_NOERR;
}
/* End NC_attr */
/* Begin NC_var */
/*
* How much space will the xdr'd var take.
* Formerly
NC_xlen_var(vpp)
*/
static size_t
ncx_len_NC_var(const NC_var *varp, size_t sizeof_off_t, int version)
{
size_t sz;
assert(varp != NULL);
assert(sizeof_off_t != 0);
sz = ncx_len_NC_string(varp->name, version);
if (version == 5) {
sz += X_SIZEOF_INT64; /* ndims */
sz += ncx_len_int64(varp->ndims); /* dimids */
}
else {
sz += X_SIZEOF_SIZE_T; /* ndims */
sz += ncx_len_int(varp->ndims); /* dimids */
}
sz += ncx_len_NC_attrarray(&varp->attrs, version);
sz += X_SIZEOF_NC_TYPE; /* nc_type */
sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* vsize */
sz += sizeof_off_t; /* begin */
return(sz);
}
/* Write a NC_var to the header */
static int
v1h_put_NC_var(v1hs *psp, const NC_var *varp)
{
int status;
status = v1h_put_NC_string(psp, varp->name);
if(status != NC_NOERR)
return status;
status = v1h_put_size_t(psp, &varp->ndims);
if(status != NC_NOERR)
return status;
if (psp->version == 5) {
status = check_v1hs(psp, ncx_len_int64(varp->ndims));
if(status != NC_NOERR)
return status;
status = ncx_putn_longlong_int(&psp->pos,
varp->ndims, varp->dimids);
if(status != NC_NOERR)
return status;
}
else {
status = check_v1hs(psp, ncx_len_int(varp->ndims));
if(status != NC_NOERR)
return status;
status = ncx_putn_int_int(&psp->pos,
varp->ndims, varp->dimids);
if(status != NC_NOERR)
return status;
}
status = v1h_put_NC_attrarray(psp, &varp->attrs);
if(status != NC_NOERR)
return status;
status = v1h_put_nc_type(psp, &varp->type);
if(status != NC_NOERR)
return status;
status = v1h_put_size_t(psp, &varp->len);
if(status != NC_NOERR)
return status;
status = check_v1hs(psp, psp->version == 1 ? 4 : 8); /*begin*/
if(status != NC_NOERR)
return status;
status = ncx_put_off_t(&psp->pos, &varp->begin, psp->version == 1 ? 4 : 8);
if(status != NC_NOERR)
return status;
return NC_NOERR;
}
/* Read a NC_var from the header */
static int
v1h_get_NC_var(v1hs *gsp, NC_var **varpp)
{
NC_string *strp;
int status;
size_t ndims;
NC_var *varp;
status = v1h_get_NC_string(gsp, &strp);
if(status != NC_NOERR)
return status;
status = v1h_get_size_t(gsp, &ndims);
if(status != NC_NOERR)
goto unwind_name;
varp = new_x_NC_var(strp, ndims);
if(varp == NULL)
{
status = NC_ENOMEM;
goto unwind_name;
}
if (gsp->version == 5) {
status = check_v1hs(gsp, ncx_len_int64(ndims));
if(status != NC_NOERR)
goto unwind_alloc;
status = ncx_getn_longlong_int((const void **)(&gsp->pos),
ndims, varp->dimids);
if(status != NC_NOERR)
goto unwind_alloc;
}
else {
status = check_v1hs(gsp, ncx_len_int(ndims));
if(status != NC_NOERR)
goto unwind_alloc;
status = ncx_getn_int_int((const void **)(&gsp->pos),
ndims, varp->dimids);
if(status != NC_NOERR)
goto unwind_alloc;
}
status = v1h_get_NC_attrarray(gsp, &varp->attrs);
if(status != NC_NOERR)
goto unwind_alloc;
status = v1h_get_nc_type(gsp, &varp->type);
if(status != NC_NOERR)
goto unwind_alloc;
status = v1h_get_size_t(gsp, &varp->len);
if(status != NC_NOERR)
goto unwind_alloc;
status = check_v1hs(gsp, gsp->version == 1 ? 4 : 8);
if(status != NC_NOERR)
goto unwind_alloc;
status = ncx_get_off_t((const void **)&gsp->pos,
&varp->begin, gsp->version == 1 ? 4 : 8);
if(status != NC_NOERR)
goto unwind_alloc;
*varpp = varp;
return NC_NOERR;
unwind_alloc:
free_NC_var(varp); /* frees name */
return status;
unwind_name:
free_NC_string(strp);
return status;
}
/* How much space in the header is required for this NC_vararray? */
static size_t
ncx_len_NC_vararray(const NC_vararray *ncap, size_t sizeof_off_t, int version)
{
size_t xlen = X_SIZEOF_NCTYPE; /* type */
xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
if(ncap == NULL)
return xlen;
/* else */
{
const NC_var **vpp = (const NC_var **)ncap->value;
const NC_var *const *const end = &vpp[ncap->nelems];
for( /*NADA*/; vpp < end; vpp++)
{
xlen += ncx_len_NC_var(*vpp, sizeof_off_t, version);
}
}
return xlen;
}
/* Write a NC_vararray to the header */
static int
v1h_put_NC_vararray(v1hs *psp, const NC_vararray *ncap)
{
int status;
assert(psp != NULL);
if(ncap == NULL
#if 1
/* Backward:
* This clause is for 'byte for byte'
* backward compatibility.
* Strickly speaking, it is 'bug for bug'.
*/
|| ncap->nelems == 0
#endif
)
{
/*
* Handle empty netcdf
*/
const size_t nosz = 0;
status = v1h_put_NCtype(psp, NC_UNSPECIFIED);
if(status != NC_NOERR)
return status;
status = v1h_put_size_t(psp, &nosz);
if(status != NC_NOERR)
return status;
return NC_NOERR;
}
/* else */
status = v1h_put_NCtype(psp, NC_VARIABLE);
if(status != NC_NOERR)
return status;
status = v1h_put_size_t(psp, &ncap->nelems);
if(status != NC_NOERR)
return status;
{
const NC_var **vpp = (const NC_var **)ncap->value;
const NC_var *const *const end = &vpp[ncap->nelems];
for( /*NADA*/; vpp < end; vpp++)
{
status = v1h_put_NC_var(psp, *vpp);
if(status)
return status;
}
}
return NC_NOERR;
}
/* Read a NC_vararray from the header */
static int
v1h_get_NC_vararray(v1hs *gsp, NC_vararray *ncap)
{
int status;
NCtype type = NC_UNSPECIFIED;
assert(gsp != NULL && gsp->pos != NULL);
assert(ncap != NULL);
assert(ncap->value == NULL);
status = v1h_get_NCtype(gsp, &type);
if(status != NC_NOERR)
return status;
status = v1h_get_size_t(gsp, &ncap->nelems);
if(status != NC_NOERR)
return status;
if(ncap->nelems == 0)
return NC_NOERR;
/* else */
if(type != NC_VARIABLE)
return EINVAL;
ncap->value = (NC_var **) malloc(ncap->nelems * sizeof(NC_var *));
if(ncap->value == NULL)
return NC_ENOMEM;
ncap->nalloc = ncap->nelems;
ncap->hashmap = NC_hashmapCreate(ncap->nelems);
{
NC_var **vpp = ncap->value;
NC_var *const *const end = &vpp[ncap->nelems];
for( /*NADA*/; vpp < end; vpp++)
{
status = v1h_get_NC_var(gsp, vpp);
if(status)
{
ncap->nelems = (size_t)(vpp - ncap->value);
free_NC_vararrayV(ncap);
return status;
}
{
int varid = (size_t)(vpp - ncap->value);
NC_hashmapAddVar(ncap, varid, (*vpp)->name->cp);
}
}
}
return NC_NOERR;
}
/* End NC_var */
/* Begin NC */
/*
* Recompute the shapes of all variables
* Sets ncp->begin_var to start of first variable.
* Sets ncp->begin_rec to start of first record variable.
* Returns -1 on error. The only possible error is a reference
* to a non existent dimension, which could occur for a corrupted
* netcdf file.
*/
static int
NC_computeshapes(NC3_INFO* ncp)
{
NC_var **vpp = (NC_var **)ncp->vars.value;
NC_var *const *const end = &vpp[ncp->vars.nelems];
NC_var *first_var = NULL; /* first "non-record" var */
NC_var *first_rec = NULL; /* first "record" var */
int status;
ncp->begin_var = (off_t) ncp->xsz;
ncp->begin_rec = (off_t) ncp->xsz;
ncp->recsize = 0;
if(ncp->vars.nelems == 0)
return(0);
for( /*NADA*/; vpp < end; vpp++)
{
status = NC_var_shape(*vpp, &ncp->dims);
if(status != NC_NOERR)
return(status);
if(IS_RECVAR(*vpp))
{
if(first_rec == NULL)
first_rec = *vpp;
if((*vpp)->len == UINT32_MAX &&
fIsSet(ncp->flags, NC_64BIT_OFFSET)) /* Flag for large last record */
ncp->recsize += (*vpp)->dsizes[0] * (*vpp)->xsz;
else
ncp->recsize += (*vpp)->len;
}
else
{
if(first_var == NULL)
first_var = *vpp;
/*
* Overwritten each time thru.
* Usually overwritten in first_rec != NULL clause below.
*/
ncp->begin_rec = (*vpp)->begin + (off_t)(*vpp)->len;
}
}
if(first_rec != NULL)
{
if(ncp->begin_rec > first_rec->begin)
return(NC_ENOTNC); /* not a netCDF file or corrupted */
ncp->begin_rec = first_rec->begin;
/*
* for special case of exactly one record variable, pack value
*/
if(ncp->recsize == first_rec->len)
ncp->recsize = *first_rec->dsizes * first_rec->xsz;
}
if(first_var != NULL)
{
ncp->begin_var = first_var->begin;
}
else
{
ncp->begin_var = ncp->begin_rec;
}
if(ncp->begin_var <= 0 ||
ncp->xsz > (size_t)ncp->begin_var ||
ncp->begin_rec <= 0 ||
ncp->begin_var > ncp->begin_rec)
return(NC_ENOTNC); /* not a netCDF file or corrupted */
return(NC_NOERR);
}
/* How much space in the header is required for the NC data structure? */
size_t
ncx_len_NC(const NC3_INFO* ncp, size_t sizeof_off_t)
{
int version=1;
size_t xlen = sizeof(ncmagic);
assert(ncp != NULL);
if (fIsSet(ncp->flags, NC_64BIT_DATA)) /* CDF-5 */
version = 5;
else if (fIsSet(ncp->flags, NC_64BIT_OFFSET)) /* CDF-2 */
version = 2;
xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* numrecs */
xlen += ncx_len_NC_dimarray(&ncp->dims, version);
xlen += ncx_len_NC_attrarray(&ncp->attrs, version);
xlen += ncx_len_NC_vararray(&ncp->vars, sizeof_off_t, version);
return xlen;
}
/* Write the file header */
int
ncx_put_NC(const NC3_INFO* ncp, void **xpp, off_t offset, size_t extent)
{
int status = NC_NOERR;
v1hs ps; /* the get stream */
assert(ncp != NULL);
/* Initialize stream ps */
ps.nciop = ncp->nciop;
ps.flags = RGN_WRITE;
if (ncp->flags & NC_64BIT_DATA)
ps.version = 5;
else if (ncp->flags & NC_64BIT_OFFSET)
ps.version = 2;
else
ps.version = 1;
if(xpp == NULL)
{
/*
* Come up with a reasonable stream read size.
*/
extent = ncp->xsz;
if(extent <= ((ps.version==5)?MIN_NC5_XSZ:MIN_NC3_XSZ))
{
/* first time read */
extent = ncp->chunk;
/* Protection for when ncp->chunk is huge;
* no need to read hugely. */
if(extent > 4096)
extent = 4096;
}
else if(extent > ncp->chunk)
extent = ncp->chunk;
ps.offset = 0;
ps.extent = extent;
ps.base = NULL;
ps.pos = ps.base;
status = fault_v1hs(&ps, extent);
if(status)
return status;
}
else
{
ps.offset = offset;
ps.extent = extent;
ps.base = *xpp;
ps.pos = ps.base;
ps.end = (char *)ps.base + ps.extent;
}
if (ps.version == 5)
status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic5), ncmagic5);
else if (ps.version == 2)
status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic), ncmagic);
else
status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic1), ncmagic1);
if(status != NC_NOERR)
goto release;
{
const size_t nrecs = NC_get_numrecs(ncp);
if (ps.version == 5)
status = ncx_put_int64(&ps.pos, nrecs);
else
status = ncx_put_size_t(&ps.pos, &nrecs);
if(status != NC_NOERR)
goto release;
}
assert((char *)ps.pos < (char *)ps.end);
status = v1h_put_NC_dimarray(&ps, &ncp->dims);
if(status != NC_NOERR)
goto release;
status = v1h_put_NC_attrarray(&ps, &ncp->attrs);
if(status != NC_NOERR)
goto release;
status = v1h_put_NC_vararray(&ps, &ncp->vars);
if(status != NC_NOERR)
goto release;
release:
(void) rel_v1hs(&ps);
return status;
}
/* Make the in-memory NC structure from reading the file header */
int
nc_get_NC(NC3_INFO* ncp)
{
int status;
v1hs gs; /* the get stream */
assert(ncp != NULL);
/* Initialize stream gs */
gs.nciop = ncp->nciop;
gs.offset = 0; /* beginning of file */
gs.extent = 0;
gs.flags = 0;
gs.version = 0;
gs.base = NULL;
gs.pos = gs.base;
{
/*
* Come up with a reasonable stream read size.
*/
off_t filesize;
size_t extent = ncp->xsz;
if(extent <= ((fIsSet(ncp->flags, NC_64BIT_DATA))?MIN_NC5_XSZ:MIN_NC3_XSZ))
{
status = ncio_filesize(ncp->nciop, &filesize);
if(status)
return status;
if(filesize < sizeof(ncmagic)) { /* too small, not netcdf */
status = NC_ENOTNC;
return status;
}
/* first time read */
extent = ncp->chunk;
/* Protection for when ncp->chunk is huge;
* no need to read hugely. */
if(extent > 4096)
extent = 4096;
if(extent > filesize)
extent = filesize;
}
else if(extent > ncp->chunk)
extent = ncp->chunk;
/*
* Invalidate the I/O buffers to force a read of the header
* region.
*/
status = ncio_sync(gs.nciop);
if(status)
return status;
status = fault_v1hs(&gs, extent);
if(status)
return status;
}
/* get the header from the stream gs */
{
/* Get & check magic number */
schar magic[sizeof(ncmagic)];
(void) memset(magic, 0, sizeof(magic));
status = ncx_getn_schar_schar(
(const void **)(&gs.pos), sizeof(magic), magic);
if(status != NC_NOERR)
goto unwind_get;
if(memcmp(magic, ncmagic, sizeof(ncmagic)-1) != 0)
{
status = NC_ENOTNC;
goto unwind_get;
}
/* Check version number in last byte of magic */
if (magic[sizeof(ncmagic)-1] == 0x1) {
gs.version = 1;
} else if (magic[sizeof(ncmagic)-1] == 0x2) {
gs.version = 2;
fSet(ncp->flags, NC_64BIT_OFFSET);
/* Now we support version 2 file access on non-LFS systems -- rkr */
#if 0
if (sizeof(off_t) != 8) {
fprintf(stderr, "NETCDF WARNING: Version 2 file on 32-bit system.\n");
}
#endif
} else if (magic[sizeof(ncmagic)-1] == 0x5) {
gs.version = 5;
fSet(ncp->flags, NC_64BIT_DATA);
} else {
status = NC_ENOTNC;
goto unwind_get;
}
}
{
size_t nrecs = 0;
if (gs.version == 5) {
long long tmp = 0;
status = ncx_get_int64((const void **)(&gs.pos), &tmp);
nrecs = (size_t)tmp;
}
else
status = ncx_get_size_t((const void **)(&gs.pos), &nrecs);
if(status != NC_NOERR)
goto unwind_get;
NC_set_numrecs(ncp, nrecs);
}
#ifdef __arm__
assert((signed char *)gs.pos < (signed char *)gs.end);
#else
assert((char *)gs.pos < (char *)gs.end);
#endif
status = v1h_get_NC_dimarray(&gs, &ncp->dims);
if(status != NC_NOERR)
goto unwind_get;
status = v1h_get_NC_attrarray(&gs, &ncp->attrs);
if(status != NC_NOERR)
goto unwind_get;
status = v1h_get_NC_vararray(&gs, &ncp->vars);
if(status != NC_NOERR)
goto unwind_get;
ncp->xsz = ncx_len_NC(ncp, (gs.version == 1) ? 4 : 8);
status = NC_computeshapes(ncp);
if(status != NC_NOERR)
goto unwind_get;
unwind_get:
(void) rel_v1hs(&gs);
return status;
}