netcdf-c/libsrc/v1hpg.c

1527 lines
32 KiB
C
Raw Normal View History

2010-06-03 21:24:43 +08:00
/*
* Copyright 1996, University Corporation for Atmospheric Research
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*/
#include "config.h"
#include "nc3internal.h"
2010-06-03 21:24:43 +08:00
#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
2010-06-03 21:24:43 +08:00
* 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};
2015-08-16 06:26:35 +08:00
static const schar ncmagic5[] = {'C', 'D', 'F', 0x05};
2010-06-03 21:24:43 +08:00
/*
* 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 */
2015-08-16 06:26:35 +08:00
int version; /* format variant: NC_FORMAT_CLASSIC, NC_FORMAT_64BIT_OFFSET or NC_FORMAT_CDF5 */
2010-06-03 21:24:43 +08:00
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,
2010-06-03 21:24:43 +08:00
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
2010-06-03 21:24:43 +08:00
status = rel_v1hs(gsp);
if(status)
return status;
gsp->offset += incr;
}
2010-06-03 21:24:43 +08:00
if(extent > gsp->extent)
gsp->extent = extent;
2010-06-03 21:24:43 +08:00
status = ncio_get(gsp->nciop,
2010-06-03 21:24:43 +08:00
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;
2010-06-03 21:24:43 +08:00
}
/*
* 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);
2010-06-03 21:24:43 +08:00
}
/* End v1hs */
/* Write a size_t to the header */
static int
v1h_put_size_t(v1hs *psp, const size_t *sp)
{
2015-08-16 06:26:35 +08:00
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)
2015-08-16 06:26:35 +08:00
return status;
if (psp->version == 5)
return ncx_put_int64(&psp->pos, *sp);
else
return ncx_put_size_t(&psp->pos, sp);
2010-06-03 21:24:43 +08:00
}
/* Read a size_t from the header */
static int
v1h_get_size_t(v1hs *gsp, size_t *sp)
{
2015-08-16 06:26:35 +08:00
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)
2010-06-03 21:24:43 +08:00
return status;
2015-08-16 06:26:35 +08:00
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);
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
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;
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
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)
2010-06-03 21:24:43 +08:00
return status;
assert(type == NC_BYTE
|| type == NC_CHAR
|| type == NC_SHORT
|| type == NC_INT
|| type == NC_FLOAT
2015-08-16 06:26:35 +08:00
|| type == NC_DOUBLE
|| type == NC_UBYTE
|| type == NC_USHORT
|| type == NC_UINT
|| type == NC_INT64
|| type == NC_UINT64
|| type == NC_STRING);
2010-06-03 21:24:43 +08:00
/* else */
*typep = (nc_type) type;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
return status;
status = ncx_put_int_int(psp->pos, &itype);
#ifdef __arm__
psp->pos = (void *)((signed char *)psp->pos + X_SIZEOF_INT);
#else
2010-06-03 21:24:43 +08:00
psp->pos = (void *)((char *)psp->pos + X_SIZEOF_INT);
#endif
return status;
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
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)
2010-06-03 21:24:43 +08:00
return status;
/* else */
*typep = (NCtype) type;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/* End NCtype */
/* Begin NC_string */
/*
* How much space will the xdr'd string take.
* Formerly
NC_xlen_string(cdfstr)
*/
static size_t
2015-08-16 06:26:35 +08:00
ncx_len_NC_string(const NC_string *ncstrp, int version)
2010-06-03 21:24:43 +08:00
{
2015-08-16 06:26:35 +08:00
size_t sz = (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_INT; /* nchars */
2010-06-03 21:24:43 +08:00
assert(ncstrp != NULL);
if(ncstrp->nchars != 0)
2010-06-03 21:24:43 +08:00
{
#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)
2010-06-03 21:24:43 +08:00
return status;
status = check_v1hs(psp, _RNDUP(ncstrp->nchars, X_ALIGN));
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
status = ncx_pad_putn_text(&psp->pos, ncstrp->nchars, ncstrp->cp);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
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
2010-06-03 21:24:43 +08:00
status = check_v1hs(gsp, _RNDUP(ncstrp->nchars, X_ALIGN));
#endif
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_alloc;
status = ncx_pad_getn_text((const void **)(&gsp->pos),
nchars, ncstrp->cp);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_alloc;
*ncstrpp = ncstrp;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
unwind_alloc:
free_NC_string(ncstrp);
return status;
2010-06-03 21:24:43 +08:00
}
/* End NC_string */
/* Begin NC_dim */
/*
* How much space will the xdr'd dim take.
* Formerly
NC_xlen_dim(dpp)
*/
static size_t
2015-08-16 06:26:35 +08:00
ncx_len_NC_dim(const NC_dim *dimp, int version)
2010-06-03 21:24:43 +08:00
{
size_t sz;
assert(dimp != NULL);
2015-08-16 06:26:35 +08:00
sz = ncx_len_NC_string(dimp->name, version);
sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T;
2010-06-03 21:24:43 +08:00
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)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_put_size_t(psp, &dimp->size);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
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)
2010-06-03 21:24:43 +08:00
{
free_NC_dim(dimp); /* frees name */
return status;
}
*dimpp = dimp;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
unwind_name:
free_NC_string(ncstrp);
return status;
}
/* How much space in the header is required for this NC_dimarray? */
static size_t
2015-08-16 06:26:35 +08:00
ncx_len_NC_dimarray(const NC_dimarray *ncap, int version)
2010-06-03 21:24:43 +08:00
{
size_t xlen = X_SIZEOF_NCTYPE; /* type */
2015-08-16 06:26:35 +08:00
xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
2010-06-03 21:24:43 +08:00
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++)
{
2015-08-16 06:26:35 +08:00
xlen += ncx_len_NC_dim(*dpp,version);
2010-06-03 21:24:43 +08:00
}
}
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)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_put_size_t(psp, &nosz);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/* else */
status = v1h_put_NCtype(psp, NC_DIMENSION);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_put_size_t(psp, &ncap->nelems);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
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;
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_get_size_t(gsp, &ncap->nelems);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
2010-06-03 21:24:43 +08:00
if(ncap->nelems == 0)
return NC_NOERR;
2010-06-03 21:24:43 +08:00
/* 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);
2010-06-03 21:24:43 +08:00
{
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);
}
2010-06-03 21:24:43 +08:00
}
}
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/* End NC_dim */
/* Begin NC_attr */
/*
* How much space will 'attrp' take in external representation?
* Formerly
NC_xlen_attr(app)
*/
static size_t
2015-08-16 06:26:35 +08:00
ncx_len_NC_attr(const NC_attr *attrp, int version)
2010-06-03 21:24:43 +08:00
{
size_t sz;
assert(attrp != NULL);
2015-08-16 06:26:35 +08:00
sz = ncx_len_NC_string(attrp->name, version);
2010-06-03 21:24:43 +08:00
sz += X_SIZEOF_NC_TYPE; /* type */
2015-08-16 06:26:35 +08:00
sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* nelems */
2010-06-03 21:24:43 +08:00
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;
2010-06-03 21:24:43 +08:00
assert(psp->extent % X_ALIGN == 0);
2010-06-03 21:24:43 +08:00
do {
nbytes = MIN(perchunk, remaining);
2010-06-03 21:24:43 +08:00
status = check_v1hs(psp, nbytes);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
2010-06-03 21:24:43 +08:00
(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);
2010-06-03 21:24:43 +08:00
value = (void *)((char *)value + nbytes);
#endif
remaining -= nbytes;
2010-06-03 21:24:43 +08:00
} while(remaining != 0);
2010-06-03 21:24:43 +08:00
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_put_nc_type(psp, &attrp->type);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_put_size_t(psp, &attrp->nelems);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_put_NC_attrV(psp, attrp);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/*
* 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;
2010-06-03 21:24:43 +08:00
do {
nget = MIN(perchunk, remaining);
2010-06-03 21:24:43 +08:00
status = check_v1hs(gsp, nget);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
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);
2010-06-03 21:24:43 +08:00
value = (void *)((char *)value + nget);
#endif
remaining -= nget;
2010-06-03 21:24:43 +08:00
} while(remaining != 0);
2010-06-03 21:24:43 +08:00
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_get_nc_type(gsp, &type);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_name;
status = v1h_get_size_t(gsp, &nelems);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_name;
attrp = new_x_NC_attr(strp, type, nelems);
if(attrp == NULL)
{
status = NC_ENOMEM;
goto unwind_name;
}
2010-06-03 21:24:43 +08:00
status = v1h_get_NC_attrV(gsp, attrp);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
{
free_NC_attr(attrp); /* frees strp */
return status;
}
*attrpp = attrp;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
unwind_name:
free_NC_string(strp);
return status;
}
/* How much space in the header is required for this NC_attrarray? */
static size_t
2015-08-16 06:26:35 +08:00
ncx_len_NC_attrarray(const NC_attrarray *ncap, int version)
2010-06-03 21:24:43 +08:00
{
size_t xlen = X_SIZEOF_NCTYPE; /* type */
2015-08-16 06:26:35 +08:00
xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
2010-06-03 21:24:43 +08:00
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++)
{
2015-08-16 06:26:35 +08:00
xlen += ncx_len_NC_attr(*app,version);
2010-06-03 21:24:43 +08:00
}
}
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)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_put_size_t(psp, &nosz);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/* else */
status = v1h_put_NCtype(psp, NC_ATTRIBUTE);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_put_size_t(psp, &ncap->nelems);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
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;
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_get_size_t(gsp, &ncap->nelems);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
2010-06-03 21:24:43 +08:00
if(ncap->nelems == 0)
return NC_NOERR;
2010-06-03 21:24:43 +08:00
/* 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;
2010-06-03 21:24:43 +08:00
}
/* End NC_attr */
/* Begin NC_var */
/*
* How much space will the xdr'd var take.
* Formerly
NC_xlen_var(vpp)
*/
static size_t
2015-08-16 06:26:35 +08:00
ncx_len_NC_var(const NC_var *varp, size_t sizeof_off_t, int version)
2010-06-03 21:24:43 +08:00
{
size_t sz;
assert(varp != NULL);
assert(sizeof_off_t != 0);
2015-08-16 06:26:35 +08:00
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 */
2010-06-03 21:24:43 +08:00
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)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_put_size_t(psp, &varp->ndims);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
2015-08-16 06:26:35 +08:00
if (psp->version == 5) {
status = check_v1hs(psp, ncx_len_int64(varp->ndims));
if(status != NC_NOERR)
2015-08-16 06:26:35 +08:00
return status;
status = ncx_putn_longlong_int(&psp->pos,
varp->ndims, varp->dimids);
if(status != NC_NOERR)
2015-08-16 06:26:35 +08:00
return status;
}
else {
status = check_v1hs(psp, ncx_len_int(varp->ndims));
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
2015-08-16 06:26:35 +08:00
status = ncx_putn_int_int(&psp->pos,
2010-06-03 21:24:43 +08:00
varp->ndims, varp->dimids);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
2015-08-16 06:26:35 +08:00
}
2010-06-03 21:24:43 +08:00
status = v1h_put_NC_attrarray(psp, &varp->attrs);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_put_nc_type(psp, &varp->type);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_put_size_t(psp, &varp->len);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
2015-08-16 06:26:35 +08:00
status = check_v1hs(psp, psp->version == 1 ? 4 : 8); /*begin*/
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
status = ncx_put_off_t(&psp->pos, &varp->begin, psp->version == 1 ? 4 : 8);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_get_size_t(gsp, &ndims);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_name;
varp = new_x_NC_var(strp, ndims);
if(varp == NULL)
{
status = NC_ENOMEM;
goto unwind_name;
}
2015-08-16 06:26:35 +08:00
if (gsp->version == 5) {
status = check_v1hs(gsp, ncx_len_int64(ndims));
if(status != NC_NOERR)
2015-08-16 06:26:35 +08:00
goto unwind_alloc;
status = ncx_getn_longlong_int((const void **)(&gsp->pos),
ndims, varp->dimids);
if(status != NC_NOERR)
2015-08-16 06:26:35 +08:00
goto unwind_alloc;
}
else {
status = check_v1hs(gsp, ncx_len_int(ndims));
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_alloc;
2015-08-16 06:26:35 +08:00
status = ncx_getn_int_int((const void **)(&gsp->pos),
2010-06-03 21:24:43 +08:00
ndims, varp->dimids);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_alloc;
2015-08-16 06:26:35 +08:00
}
2010-06-03 21:24:43 +08:00
status = v1h_get_NC_attrarray(gsp, &varp->attrs);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_alloc;
status = v1h_get_nc_type(gsp, &varp->type);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_alloc;
status = v1h_get_size_t(gsp, &varp->len);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_alloc;
status = check_v1hs(gsp, gsp->version == 1 ? 4 : 8);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_alloc;
status = ncx_get_off_t((const void **)&gsp->pos,
&varp->begin, gsp->version == 1 ? 4 : 8);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_alloc;
2010-06-03 21:24:43 +08:00
*varpp = varp;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
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
2015-08-16 06:26:35 +08:00
ncx_len_NC_vararray(const NC_vararray *ncap, size_t sizeof_off_t, int version)
2010-06-03 21:24:43 +08:00
{
size_t xlen = X_SIZEOF_NCTYPE; /* type */
2015-08-16 06:26:35 +08:00
xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
2010-06-03 21:24:43 +08:00
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++)
{
2015-08-16 06:26:35 +08:00
xlen += ncx_len_NC_var(*vpp, sizeof_off_t, version);
2010-06-03 21:24:43 +08:00
}
}
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)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_put_size_t(psp, &nosz);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/* else */
status = v1h_put_NCtype(psp, NC_VARIABLE);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
status = v1h_put_size_t(psp, &ncap->nelems);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
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;
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
return status;
2010-06-03 21:24:43 +08:00
status = v1h_get_size_t(gsp, &ncap->nelems);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return status;
2010-06-03 21:24:43 +08:00
if(ncap->nelems == 0)
return NC_NOERR;
2010-06-03 21:24:43 +08:00
/* 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);
2010-06-03 21:24:43 +08:00
{
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);
}
2010-06-03 21:24:43 +08:00
}
}
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
{
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);
2010-06-03 21:24:43 +08:00
for( /*NADA*/; vpp < end; vpp++)
{
status = NC_var_shape(*vpp, &ncp->dims);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
return(status);
if(IS_RECVAR(*vpp))
2010-06-03 21:24:43 +08:00
{
if(first_rec == NULL)
2010-06-03 21:24:43 +08:00
first_rec = *vpp;
2015-08-16 06:26:35 +08:00
if((*vpp)->len == UINT32_MAX &&
fIsSet(ncp->flags, NC_64BIT_OFFSET)) /* Flag for large last record */
ncp->recsize += (*vpp)->dsizes[0] * (*vpp)->xsz;
2012-04-02 07:42:53 +08:00
else
ncp->recsize += (*vpp)->len;
2010-06-03 21:24:43 +08:00
}
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 */
2010-06-03 21:24:43 +08:00
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);
2010-06-03 21:24:43 +08:00
}
/* 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)
2010-06-03 21:24:43 +08:00
{
2015-08-16 06:26:35 +08:00
int version=1;
2010-06-03 21:24:43 +08:00
size_t xlen = sizeof(ncmagic);
assert(ncp != NULL);
2015-08-16 06:26:35 +08:00
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);
2010-06-03 21:24:43 +08:00
return xlen;
}
/* Write the file header */
int
ncx_put_NC(const NC3_INFO* ncp, void **xpp, off_t offset, size_t extent)
2010-06-03 21:24:43 +08:00
{
int status = NC_NOERR;
2010-06-03 21:24:43 +08:00
v1hs ps; /* the get stream */
assert(ncp != NULL);
/* Initialize stream ps */
ps.nciop = ncp->nciop;
ps.flags = RGN_WRITE;
2015-08-16 06:26:35 +08:00
if (ncp->flags & NC_64BIT_DATA)
ps.version = 5;
else if (ncp->flags & NC_64BIT_OFFSET)
2010-06-03 21:24:43 +08:00
ps.version = 2;
else
2010-06-03 21:24:43 +08:00
ps.version = 1;
if(xpp == NULL)
{
/*
* Come up with a reasonable stream read size.
*/
extent = ncp->xsz;
2015-08-16 06:26:35 +08:00
if(extent <= ((ps.version==5)?MIN_NC5_XSZ:MIN_NC3_XSZ))
2010-06-03 21:24:43 +08:00
{
/* 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;
2010-06-03 21:24:43 +08:00
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;
}
2015-08-16 06:26:35 +08:00
if (ps.version == 5)
status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic5), ncmagic5);
else if (ps.version == 2)
2010-06-03 21:24:43 +08:00
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)
2010-06-03 21:24:43 +08:00
goto release;
{
const size_t nrecs = NC_get_numrecs(ncp);
2015-08-16 06:26:35 +08:00
if (ps.version == 5)
status = ncx_put_int64(&ps.pos, nrecs);
else
status = ncx_put_size_t(&ps.pos, &nrecs);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto release;
}
assert((char *)ps.pos < (char *)ps.end);
status = v1h_put_NC_dimarray(&ps, &ncp->dims);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto release;
status = v1h_put_NC_attrarray(&ps, &ncp->attrs);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto release;
status = v1h_put_NC_vararray(&ps, &ncp->vars);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
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)
2010-06-03 21:24:43 +08:00
{
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;
2015-08-16 06:26:35 +08:00
size_t extent = ncp->xsz;
2015-08-16 06:26:35 +08:00
if(extent <= ((fIsSet(ncp->flags, NC_64BIT_DATA))?MIN_NC5_XSZ:MIN_NC3_XSZ))
2010-06-03 21:24:43 +08:00
{
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;
2010-06-03 21:24:43 +08:00
/*
* Invalidate the I/O buffers to force a read of the header
* region.
*/
status = ncio_sync(gs.nciop);
2010-06-03 21:24:43 +08:00
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)
2010-06-03 21:24:43 +08:00
goto unwind_get;
2010-06-03 21:24:43 +08:00
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
2015-08-16 06:26:35 +08:00
} else if (magic[sizeof(ncmagic)-1] == 0x5) {
gs.version = 5;
fSet(ncp->flags, NC_64BIT_DATA);
2010-06-03 21:24:43 +08:00
} else {
status = NC_ENOTNC;
goto unwind_get;
}
}
2010-06-03 21:24:43 +08:00
{
size_t nrecs = 0;
2015-08-16 06:26:35 +08:00
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)
2010-06-03 21:24:43 +08:00
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
2010-06-03 21:24:43 +08:00
status = v1h_get_NC_dimarray(&gs, &ncp->dims);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_get;
status = v1h_get_NC_attrarray(&gs, &ncp->attrs);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_get;
status = v1h_get_NC_vararray(&gs, &ncp->vars);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_get;
2010-06-03 21:24:43 +08:00
ncp->xsz = ncx_len_NC(ncp, (gs.version == 1) ? 4 : 8);
status = NC_computeshapes(ncp);
if(status != NC_NOERR)
2010-06-03 21:24:43 +08:00
goto unwind_get;
unwind_get:
(void) rel_v1hs(&gs);
return status;
}