netcdf-c/libsrc/v1hpg.c
Dennis Heimbigner 727b613459 This is the initial step in moving to the new higher performance
(I hope) metadata mechanism. This mostly just adds new pieces of
code (e.g. nclistmap) and does some minor fixes.

It should be transparent to everything else.
The next set of changes will be the big step.
2018-02-08 19:53:40 -07:00

1555 lines
34 KiB
C

/*
* Copyright 1996, University Corporation for Atmospheric Research
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "nc3internal.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)
{
const ptrdiff_t incr = (char *)gsp->pos - (char *)gsp->base;
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;
gsp->end = (char *)gsp->base + gsp->extent;
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
if((char *)gsp->pos + nextread <= (char *)gsp->end)
return NC_NOERR;
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) {
unsigned long long tmp = (unsigned long long) (*sp);
return ncx_put_uint64(&psp->pos, tmp);
}
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) {
unsigned long long tmp=0;
status = ncx_get_uint64((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 unsigned int itype = (unsigned int) *typep;
int status = check_v1hs(psp, X_SIZEOF_INT);
if(status != NC_NOERR) return status;
status = ncx_put_uint32(&psp->pos, itype);
return status;
}
/* Read a nc_type from the header */
static int
v1h_get_nc_type(v1hs *gsp, nc_type *typep)
{
unsigned int type = 0;
int status = check_v1hs(gsp, X_SIZEOF_INT);
if(status != NC_NOERR) return status;
status = ncx_get_uint32((const void**)(&gsp->pos), &type);
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 unsigned int itype = (unsigned int) type;
int status = check_v1hs(psp, X_SIZEOF_INT);
if(status != NC_NOERR) return status;
status = ncx_put_uint32(&psp->pos, itype);
return status;
}
/* Read a NCtype from the header */
static int
v1h_get_NCtype(v1hs *gsp, NCtype *typep)
{
unsigned int type = 0;
int status = check_v1hs(gsp, X_SIZEOF_INT);
if(status != NC_NOERR) return status;
status = ncx_get_uint32((const void**)(&gsp->pos), &type);
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 = 0;
size_t nchars = 0;
NC_string *ncstrp = NULL;
#if USE_STRICT_NULL_BYTE_HEADER_PADDING
size_t padding = 0;
#endif /* USE_STRICT_NULL_BYTE_HEADER_PADDING */
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;
#if USE_STRICT_NULL_BYTE_HEADER_PADDING
padding = _RNDUP(X_SIZEOF_CHAR * ncstrp->nchars, X_ALIGN)
- X_SIZEOF_CHAR * ncstrp->nchars;
if (padding > 0) {
/* CDF specification: Header padding uses null (\x00) bytes. */
char pad[X_ALIGN-1];
memset(pad, 0, X_ALIGN-1);
if (memcmp((char*)gsp->pos-padding, pad, padding) != 0) {
free_NC_string(ncstrp);
return NC_ENULLPAD;
}
}
#endif
*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))
/*----< ncmpix_len_nctype() >------------------------------------------------*/
/* return the length of external data type */
static int
ncmpix_len_nctype(nc_type type) {
switch(type) {
case NC_BYTE:
case NC_CHAR:
case NC_UBYTE: return X_SIZEOF_CHAR;
case NC_SHORT: return X_SIZEOF_SHORT;
case NC_USHORT: return X_SIZEOF_USHORT;
case NC_INT: return X_SIZEOF_INT;
case NC_UINT: return X_SIZEOF_UINT;
case NC_FLOAT: return X_SIZEOF_FLOAT;
case NC_DOUBLE: return X_SIZEOF_DOUBLE;
case NC_INT64: return X_SIZEOF_INT64;
case NC_UINT64: return X_SIZEOF_UINT64;
default: fprintf(stderr,"ncmpix_len_nctype bad type %d\n",type);
assert(0);
}
return 0;
}
/*
* 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 = 0;
const size_t perchunk = psp->extent;
size_t remaining = attrp->xsz;
void *value = attrp->xvalue;
size_t nbytes = 0, padding = 0;
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);
psp->pos = (void *)((char *)psp->pos + nbytes);
value = (void *)((char *)value + nbytes);
remaining -= nbytes;
} while(remaining != 0);
padding = attrp->xsz - ncmpix_len_nctype(attrp->type) * attrp->nelems;
if (padding > 0) {
/* CDF specification: Header padding uses null (\x00) bytes. */
memset((char*)psp->pos-padding, 0, padding);
}
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;
#if USE_STRICT_NULL_BYTE_HEADER_PADDING
size_t padding;
#endif /* USE_STRICT_NULL_BYTE_HEADER_PADDING */
do {
nget = MIN(perchunk, remaining);
status = check_v1hs(gsp, nget);
if(status != NC_NOERR)
return status;
(void) memcpy(value, gsp->pos, nget);
gsp->pos = (void*)((unsigned char *)gsp->pos + nget);
value = (void *)((signed char *)value + nget);
remaining -= nget;
} while(remaining != 0);
#if USE_STRICT_NULL_BYTE_HEADER_PADDING
padding = attrp->xsz - ncmpix_len_nctype(attrp->type) * attrp->nelems;
if (padding > 0) {
/* CDF specification: Header padding uses null (\x00) bytes. */
char pad[X_ALIGN-1];
memset(pad, 0, X_ALIGN-1);
if (memcmp((char*)gsp->pos-padding, pad, (size_t)padding) != 0)
return NC_ENULLPAD;
}
#endif
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;
size_t vsize;
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, NULL);
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, NULL);
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;
/* write vsize to header.
* CDF format specification: The vsize field is actually redundant, because
* its value may be computed from other information in the header. The
* 32-bit vsize field is not large enough to contain the size of variables
* that require more than 2^32 - 4 bytes, so 2^32 - 1 is used in the vsize
* field for such variables.
*/
vsize = varp->len;
if (varp->len > 4294967292UL && (psp->version == NC_FORMAT_CLASSIC ||
psp->version == NC_FORMAT_64BIT_OFFSET))
vsize = 4294967295UL; /* 2^32-1 */
status = v1h_put_size_t(psp, &vsize);
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;
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, NULL);
else if (ps.version == 2)
status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic), ncmagic, NULL);
else
status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic1), ncmagic1, NULL);
if(status != NC_NOERR)
goto release;
{
const size_t nrecs = NC_get_numrecs(ncp);
if (ps.version == 5) {
unsigned long long tmp = (unsigned long long) nrecs;
status = ncx_put_uint64(&ps.pos, tmp);
}
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) {
unsigned long long tmp = 0;
status = ncx_get_uint64((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);
}
assert((char *)gs.pos < (char *)gs.end);
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;
status = NC_check_vlens(ncp);
if(status != NC_NOERR)
goto unwind_get;
status = NC_check_voffs(ncp);
if(status != NC_NOERR)
goto unwind_get;
unwind_get:
(void) rel_v1hs(&gs);
return status;
}