mirror of
https://github.com/Unidata/netcdf-c.git
synced 2025-01-30 16:10:44 +08:00
Merge branch 'ejh_hdf5_3' of https://github.com/NetCDF-World-Domination-Council/netcdf-c into misc-changes.wif
This commit is contained in:
commit
84fd054a29
@ -104,6 +104,9 @@ typedef enum {NCNAT, NCVAR, NCDIM, NCATT, NCTYP, NCFLD, NCGRP} NC_SORT;
|
||||
/** This is the name of the name HDF5 dimension scale attribute. */
|
||||
#define HDF5_DIMSCALE_NAME_ATT_NAME "NAME"
|
||||
|
||||
/** This is the number of netCDF atomic types. */
|
||||
#define NUM_ATOMIC_TYPES (NC_MAX_ATOMIC_TYPE + 1)
|
||||
|
||||
/* Boolean type, to make the code easier to read */
|
||||
typedef enum {NC_FALSE = 0, NC_TRUE = 1} nc_bool_t;
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
# The source files for the HDF5 dispatch layer.
|
||||
SET(libnchdf5_SOURCES nc4hdf.c nc4info.c hdf5file.c hdf5attr.c
|
||||
hdf5dim.c hdf5grp.c)
|
||||
hdf5dim.c hdf5grp.c hdf5type.c hdf5internal.c)
|
||||
|
||||
# Build the HDF4 dispatch layer as a library that will be included in
|
||||
# the netCDF library.
|
||||
|
@ -14,7 +14,7 @@ noinst_LTLIBRARIES = libnchdf5.la
|
||||
|
||||
# The source files.
|
||||
libnchdf5_la_SOURCES = nc4hdf.c nc4info.c hdf5file.c hdf5attr.c \
|
||||
hdf5dim.c hdf5grp.c
|
||||
hdf5dim.c hdf5grp.c hdf5type.c hdf5internal.c
|
||||
|
||||
# Package this for cmake build.
|
||||
EXTRA_DIST = CMakeLists.txt
|
||||
|
510
libhdf5/hdf5internal.c
Normal file
510
libhdf5/hdf5internal.c
Normal file
@ -0,0 +1,510 @@
|
||||
/* Copyright 2003-2018, University Corporation for Atmospheric
|
||||
* Research. See the COPYRIGHT file for copying and redistribution
|
||||
* conditions.
|
||||
*/
|
||||
/**
|
||||
* @file @internal Internal netcdf-4 functions.
|
||||
*
|
||||
* This file contains functions internal to the netcdf4 library. None of
|
||||
* the functions in this file are exposed in the exetnal API. These
|
||||
* functions all relate to the manipulation of netcdf-4's in-memory
|
||||
* buffer of metadata information, i.e. the linked list of NC
|
||||
* structs.
|
||||
*
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "nc4internal.h"
|
||||
#include "nc.h" /* from libsrc */
|
||||
#include "ncdispatch.h" /* from libdispatch */
|
||||
#include "ncutf8.h"
|
||||
#include "H5DSpublic.h"
|
||||
|
||||
#undef DEBUGH5
|
||||
|
||||
#ifdef DEBUGH5
|
||||
/**
|
||||
* @internal Provide a catchable error reporting function
|
||||
*
|
||||
* @param ignored Ignored.
|
||||
*
|
||||
* @return 0 for success.
|
||||
*/
|
||||
static herr_t
|
||||
h5catch(void* ignored)
|
||||
{
|
||||
H5Eprint(NULL);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* These are the default chunk cache sizes for HDF5 files created or
|
||||
* opened with netCDF-4. */
|
||||
extern size_t nc4_chunk_cache_size;
|
||||
extern size_t nc4_chunk_cache_nelems;
|
||||
extern float nc4_chunk_cache_preemption;
|
||||
|
||||
#ifdef LOGGING
|
||||
/* This is the severity level of messages which will be logged. Use
|
||||
severity 0 for errors, 1 for important log messages, 2 for less
|
||||
important, etc. */
|
||||
extern int nc_log_level;
|
||||
|
||||
#endif /* LOGGING */
|
||||
|
||||
int nc4_hdf5_initialized = 0; /**< True if initialization has happened. */
|
||||
|
||||
/**
|
||||
* @internal Provide a wrapper for H5Eset_auto
|
||||
* @param func Pointer to func.
|
||||
* @param client_data Client data.
|
||||
*
|
||||
* @return 0 for success
|
||||
*/
|
||||
static herr_t
|
||||
set_auto(void* func, void *client_data)
|
||||
{
|
||||
#ifdef DEBUGH5
|
||||
return H5Eset_auto2(H5E_DEFAULT,(H5E_auto2_t)h5catch,client_data);
|
||||
#else
|
||||
return H5Eset_auto2(H5E_DEFAULT,(H5E_auto2_t)func,client_data);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Provide a function to do any necessary initialization of
|
||||
* the HDF5 library.
|
||||
*/
|
||||
void
|
||||
nc4_hdf5_initialize(void)
|
||||
{
|
||||
if (set_auto(NULL, NULL) < 0)
|
||||
LOG((0, "Couldn't turn off HDF5 error messages!"));
|
||||
LOG((1, "HDF5 error messages have been turned off."));
|
||||
nc4_hdf5_initialized = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Given a varid, return the maximum length of a dimension
|
||||
* using dimid.
|
||||
*
|
||||
* @param grp Pointer to group info struct.
|
||||
* @param varid Variable ID.
|
||||
* @param dimid Dimension ID.
|
||||
* @param maxlen Pointer that gets the max length.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
static int
|
||||
find_var_dim_max_length(NC_GRP_INFO_T *grp, int varid, int dimid, size_t *maxlen)
|
||||
{
|
||||
hid_t datasetid = 0, spaceid = 0;
|
||||
NC_VAR_INFO_T *var;
|
||||
hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL;
|
||||
int d, dataset_ndims = 0;
|
||||
int retval = NC_NOERR;
|
||||
|
||||
*maxlen = 0;
|
||||
|
||||
/* Find this var. */
|
||||
var = (NC_VAR_INFO_T*)ncindexith(grp->vars,varid);
|
||||
if (!var) return NC_ENOTVAR;
|
||||
assert(var->hdr.id == varid);
|
||||
|
||||
/* If the var hasn't been created yet, its size is 0. */
|
||||
if (!var->created)
|
||||
{
|
||||
*maxlen = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get the number of records in the dataset. */
|
||||
if ((retval = nc4_open_var_grp2(grp, var->hdr.id, &datasetid)))
|
||||
BAIL(retval);
|
||||
if ((spaceid = H5Dget_space(datasetid)) < 0)
|
||||
BAIL(NC_EHDFERR);
|
||||
|
||||
/* If it's a scalar dataset, it has length one. */
|
||||
if (H5Sget_simple_extent_type(spaceid) == H5S_SCALAR)
|
||||
{
|
||||
*maxlen = (var->dimids && var->dimids[0] == dimid) ? 1 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check to make sure ndims is right, then get the len of each
|
||||
dim in the space. */
|
||||
if ((dataset_ndims = H5Sget_simple_extent_ndims(spaceid)) < 0)
|
||||
BAIL(NC_EHDFERR);
|
||||
if (dataset_ndims != var->ndims)
|
||||
BAIL(NC_EHDFERR);
|
||||
if (!(h5dimlen = malloc(dataset_ndims * sizeof(hsize_t))))
|
||||
BAIL(NC_ENOMEM);
|
||||
if (!(h5dimlenmax = malloc(dataset_ndims * sizeof(hsize_t))))
|
||||
BAIL(NC_ENOMEM);
|
||||
if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid,
|
||||
h5dimlen, h5dimlenmax)) < 0)
|
||||
BAIL(NC_EHDFERR);
|
||||
LOG((5, "find_var_dim_max_length: varid %d len %d max: %d",
|
||||
varid, (int)h5dimlen[0], (int)h5dimlenmax[0]));
|
||||
for (d=0; d<dataset_ndims; d++) {
|
||||
if (var->dimids[d] == dimid) {
|
||||
*maxlen = *maxlen > h5dimlen[d] ? *maxlen : h5dimlen[d];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if (spaceid > 0 && H5Sclose(spaceid) < 0)
|
||||
BAIL2(NC_EHDFERR);
|
||||
if (h5dimlen) free(h5dimlen);
|
||||
if (h5dimlenmax) free(h5dimlenmax);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Search for type with a given HDF type id.
|
||||
*
|
||||
* @param h5 File
|
||||
* @param target_hdf_typeid HDF5 type ID to find.
|
||||
*
|
||||
* @return Pointer to type info struct, or NULL if not found.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
NC_TYPE_INFO_T *
|
||||
nc4_rec_find_hdf_type(NC_HDF5_FILE_INFO_T* h5, hid_t target_hdf_typeid)
|
||||
{
|
||||
NC_TYPE_INFO_T *type;
|
||||
htri_t equal;
|
||||
int i;
|
||||
|
||||
assert(h5);
|
||||
|
||||
for(i=0;i<nclistlength(h5->alltypes);i++) {
|
||||
type = (NC_TYPE_INFO_T*)nclistget(h5->alltypes,i);
|
||||
if(type == NULL) continue;
|
||||
/* Is this the type we are searching for? */
|
||||
if ((equal = H5Tequal(type->native_hdf_typeid ? type->native_hdf_typeid : type->hdf_typeid, target_hdf_typeid)) < 0)
|
||||
return NULL;
|
||||
if (equal)
|
||||
return type;
|
||||
}
|
||||
/* Can't find it. Fate, why do you mock me? */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Find the actual length of a dim by checking the length of
|
||||
* that dim in all variables that use it, in grp or children. **len
|
||||
* must be initialized to zero before this function is called.
|
||||
*
|
||||
* @param grp Pointer to group info struct.
|
||||
* @param dimid Dimension ID.
|
||||
* @param len Pointer to pointer that gets length.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
nc4_find_dim_len(NC_GRP_INFO_T *grp, int dimid, size_t **len)
|
||||
{
|
||||
NC_GRP_INFO_T *g;
|
||||
NC_VAR_INFO_T *var;
|
||||
int retval;
|
||||
int i;
|
||||
|
||||
assert(grp && len);
|
||||
LOG((3, "nc4_find_dim_len: grp->name %s dimid %d", grp->hdr.name, dimid));
|
||||
|
||||
/* If there are any groups, call this function recursively on
|
||||
* them. */
|
||||
for(i=0;i<ncindexsize(grp->children);i++) {
|
||||
g = (NC_GRP_INFO_T*)ncindexith(grp->children,i);
|
||||
if(g == NULL) continue;
|
||||
if ((retval = nc4_find_dim_len(g, dimid, len)))
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* For all variables in this group, find the ones that use this
|
||||
* dimension, and remember the max length. */
|
||||
for (i=0; i < ncindexsize(grp->vars); i++)
|
||||
{
|
||||
size_t mylen;
|
||||
var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
|
||||
if (var == NULL) continue;
|
||||
|
||||
/* Find max length of dim in this variable... */
|
||||
if ((retval = find_var_dim_max_length(grp, var->hdr.id, dimid, &mylen)))
|
||||
return retval;
|
||||
|
||||
**len = **len > mylen ? **len : mylen;
|
||||
}
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Break a coordinate variable to separate the dimension and
|
||||
* the variable.
|
||||
*
|
||||
* This is called from nc_rename_dim() and nc_rename_var(). In some
|
||||
* renames, the coord variable must stay, but it is no longer a coord
|
||||
* variable. This function changes a coord var into an ordinary
|
||||
* variable.
|
||||
*
|
||||
* @param grp Pointer to group info struct.
|
||||
* @param coord_var Pointer to variable info struct.
|
||||
* @param dim Pointer to dimension info struct.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_ENOMEM Out of memory.
|
||||
* @author Quincey Koziol, Ed Hartnett
|
||||
*/
|
||||
int
|
||||
nc4_break_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *coord_var, NC_DIM_INFO_T *dim)
|
||||
{
|
||||
int retval = NC_NOERR;
|
||||
|
||||
/* Sanity checks */
|
||||
assert(grp && coord_var && dim && dim->coord_var == coord_var &&
|
||||
coord_var->dim[0] == dim && coord_var->dimids[0] == dim->hdr.id &&
|
||||
!dim->hdf_dimscaleid);
|
||||
LOG((3, "%s dim %s was associated with var %s, but now has different name",
|
||||
__func__, dim->hdr.name, coord_var->hdr.name));
|
||||
|
||||
/* If we're replacing an existing dimscale dataset, go to
|
||||
* every var in the file and detach this dimension scale. */
|
||||
if ((retval = rec_detach_scales(grp->nc4_info->root_grp,
|
||||
dim->hdr.id, coord_var->hdf_datasetid)))
|
||||
return retval;
|
||||
|
||||
/* Allow attached dimscales to be tracked on the [former]
|
||||
* coordinate variable */
|
||||
if (coord_var->ndims)
|
||||
{
|
||||
/* Coordinate variables shouldn't have dimscales attached. */
|
||||
assert(!coord_var->dimscale_attached);
|
||||
|
||||
/* Allocate space for tracking them */
|
||||
if (!(coord_var->dimscale_attached = calloc(coord_var->ndims,
|
||||
sizeof(nc_bool_t))))
|
||||
return NC_ENOMEM;
|
||||
}
|
||||
|
||||
/* Remove the atts that go with being a coordinate var. */
|
||||
/* if ((retval = remove_coord_atts(coord_var->hdf_datasetid))) */
|
||||
/* return retval; */
|
||||
|
||||
/* Detach dimension from variable */
|
||||
coord_var->dimscale = NC_FALSE;
|
||||
dim->coord_var = NULL;
|
||||
|
||||
/* Set state transition indicators */
|
||||
coord_var->was_coord_var = NC_TRUE;
|
||||
coord_var->became_coord_var = NC_FALSE;
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Delete an existing dimscale-only dataset.
|
||||
*
|
||||
* A dimscale-only HDF5 dataset is created when a dim is defined
|
||||
* without an accompanying coordinate variable.
|
||||
*
|
||||
* Sometimes, during renames, or late creation of variables, an
|
||||
* existing, dimscale-only dataset must be removed. This means
|
||||
* detaching all variables that use the dataset, then closing and
|
||||
* unlinking it.
|
||||
*
|
||||
* @param grp The grp of the dimscale-only dataset to be deleted, or a
|
||||
* higher group in the hierarchy (ex. root group).
|
||||
* @param dimid id of the dimension
|
||||
* @param dim Pointer to the dim with the dimscale-only dataset to be
|
||||
* deleted.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EHDFERR HDF5 error.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
delete_existing_dimscale_dataset(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T *dim)
|
||||
{
|
||||
int retval;
|
||||
|
||||
assert(grp && dim);
|
||||
LOG((2, "%s: deleting dimscale dataset %s dimid %d", __func__, dim->hdr.name,
|
||||
dimid));
|
||||
|
||||
/* Detach dimscale from any variables using it */
|
||||
if ((retval = rec_detach_scales(grp, dimid, dim->hdf_dimscaleid)) < 0)
|
||||
return retval;
|
||||
|
||||
/* Close the HDF5 dataset */
|
||||
if (H5Dclose(dim->hdf_dimscaleid) < 0)
|
||||
return NC_EHDFERR;
|
||||
dim->hdf_dimscaleid = 0;
|
||||
|
||||
/* Now delete the dataset. */
|
||||
if (H5Gunlink(grp->hdf_grpid, dim->hdr.name) < 0)
|
||||
return NC_EHDFERR;
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Reform a coordinate variable from a dimension and a
|
||||
* variable.
|
||||
*
|
||||
* @param grp Pointer to group info struct.
|
||||
* @param var Pointer to variable info struct.
|
||||
* @param dim Pointer to dimension info struct.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @author Quincey Koziol
|
||||
*/
|
||||
int
|
||||
nc4_reform_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, NC_DIM_INFO_T *dim)
|
||||
{
|
||||
int need_to_reattach_scales = 0;
|
||||
int retval = NC_NOERR;
|
||||
|
||||
assert(grp && var && dim);
|
||||
LOG((3, "%s: dim->hdr.name %s var->hdr.name %s", __func__, dim->hdr.name, var->hdr.name));
|
||||
|
||||
/* Detach dimscales from the [new] coordinate variable */
|
||||
if(var->dimscale_attached)
|
||||
{
|
||||
int dims_detached = 0;
|
||||
int finished = 0;
|
||||
int d;
|
||||
|
||||
/* Loop over all dimensions for variable */
|
||||
for (d = 0; d < var->ndims && !finished; d++)
|
||||
{
|
||||
/* Is there a dimscale attached to this axis? */
|
||||
if(var->dimscale_attached[d])
|
||||
{
|
||||
NC_GRP_INFO_T *g;
|
||||
int k;
|
||||
|
||||
for (g = grp; g && !finished; g = g->parent)
|
||||
{
|
||||
NC_DIM_INFO_T *dim1;
|
||||
for(k=0;k<ncindexsize(g->dim);k++)
|
||||
{
|
||||
if((dim1 = (NC_DIM_INFO_T*)ncindexith(g->dim,k)) == NULL) continue;
|
||||
if (var->dimids[d] == dim1->hdr.id)
|
||||
{
|
||||
hid_t dim_datasetid; /* Dataset ID for dimension */
|
||||
|
||||
/* Find dataset ID for dimension */
|
||||
if (dim1->coord_var)
|
||||
dim_datasetid = dim1->coord_var->hdf_datasetid;
|
||||
else
|
||||
dim_datasetid = dim1->hdf_dimscaleid;
|
||||
|
||||
/* dim_datasetid may be 0 in some cases when
|
||||
* renames of dims and vars are happening. In
|
||||
* this case, the scale has already been
|
||||
* detached. */
|
||||
if (dim_datasetid > 0)
|
||||
{
|
||||
LOG((3, "detaching scale from %s", var->hdr.name));
|
||||
if (H5DSdetach_scale(var->hdf_datasetid, dim_datasetid, d) < 0)
|
||||
BAIL(NC_EHDFERR);
|
||||
}
|
||||
var->dimscale_attached[d] = NC_FALSE;
|
||||
if (dims_detached++ == var->ndims)
|
||||
finished++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* next variable dimension */
|
||||
|
||||
/* Release & reset the array tracking attached dimscales */
|
||||
free(var->dimscale_attached);
|
||||
var->dimscale_attached = NULL;
|
||||
need_to_reattach_scales++;
|
||||
}
|
||||
|
||||
/* Use variable's dataset ID for the dimscale ID. */
|
||||
if (dim->hdf_dimscaleid && grp != NULL)
|
||||
{
|
||||
LOG((3, "closing and unlinking dimscale dataset %s", dim->hdr.name));
|
||||
if (H5Dclose(dim->hdf_dimscaleid) < 0)
|
||||
BAIL(NC_EHDFERR);
|
||||
dim->hdf_dimscaleid = 0;
|
||||
|
||||
/* Now delete the dimscale's dataset
|
||||
(it will be recreated later, if necessary) */
|
||||
if (H5Gunlink(grp->hdf_grpid, dim->hdr.name) < 0)
|
||||
return NC_EDIMMETA;
|
||||
}
|
||||
|
||||
/* Attach variable to dimension */
|
||||
var->dimscale = NC_TRUE;
|
||||
dim->coord_var = var;
|
||||
|
||||
/* Check if this variable used to be a coord. var */
|
||||
if (need_to_reattach_scales || (var->was_coord_var && grp != NULL))
|
||||
{
|
||||
/* Reattach the scale everywhere it is used. */
|
||||
/* (Recall that netCDF dimscales are always 1-D) */
|
||||
if ((retval = rec_reattach_scales(grp->nc4_info->root_grp,
|
||||
var->dimids[0], var->hdf_datasetid)))
|
||||
return retval;
|
||||
|
||||
/* Set state transition indicator (cancels earlier transition) */
|
||||
var->was_coord_var = NC_FALSE;
|
||||
}
|
||||
else
|
||||
/* Set state transition indicator */
|
||||
var->became_coord_var = NC_TRUE;
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef LOGGING
|
||||
/**
|
||||
* This is the same as nc_set_log_level(), but will also turn on HDF5
|
||||
* internal logging, in addition to netCDF logging.
|
||||
*
|
||||
* @param new_level The new logging level.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
hdf5_set_log_level(int new_level)
|
||||
{
|
||||
if (!nc4_hdf5_initialized)
|
||||
nc4_hdf5_initialize();
|
||||
|
||||
/* If the user wants to completely turn off logging, turn off HDF5
|
||||
logging too. Now I truely can't think of what to do if this
|
||||
fails, so just ignore the return code. */
|
||||
if (new_level == NC_TURN_OFF_LOGGING)
|
||||
{
|
||||
set_auto(NULL, NULL);
|
||||
LOG((1, "HDF5 error messages turned off!"));
|
||||
}
|
||||
|
||||
/* Do we need to turn HDF5 logging back on? */
|
||||
if (new_level > NC_TURN_OFF_LOGGING &&
|
||||
nc_log_level <= NC_TURN_OFF_LOGGING)
|
||||
{
|
||||
if (set_auto((H5E_auto_t)&H5Eprint, stderr) < 0)
|
||||
LOG((0, "H5Eset_auto failed!"));
|
||||
LOG((1, "HDF5 error messages turned on."));
|
||||
}
|
||||
|
||||
/* Now remember the new level. */
|
||||
nc_set_log_level(new_level);
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
#endif /* LOGGING */
|
524
libhdf5/hdf5type.c
Normal file
524
libhdf5/hdf5type.c
Normal file
@ -0,0 +1,524 @@
|
||||
/* Copyright 2005-2018, University Corporation for Atmospheric
|
||||
* Research. See the COPYRIGHT file for copying and redistribution
|
||||
* conditions. */
|
||||
/**
|
||||
* @file @internal This file is part of netcdf-4, a netCDF-like
|
||||
* interface for HDF5, or a HDF5 backend for netCDF, depending on your
|
||||
* point of view.
|
||||
*
|
||||
* This file handles the nc4 user-defined type functions
|
||||
* (i.e. compound and opaque types).
|
||||
*
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
#include "nc4internal.h"
|
||||
#include "nc4dispatch.h"
|
||||
|
||||
/**
|
||||
* @internal Determine if two types are equal.
|
||||
*
|
||||
* @param ncid1 First file/group ID.
|
||||
* @param typeid1 First type ID.
|
||||
* @param ncid2 Second file/group ID.
|
||||
* @param typeid2 Second type ID.
|
||||
* @param equalp Pointer that will get 1 if the two types are equal.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EBADTYPE Type not found.
|
||||
* @return ::NC_EINVAL Invalid type.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
extern int
|
||||
NC4_inq_type_equal(int ncid1, nc_type typeid1, int ncid2,
|
||||
nc_type typeid2, int *equalp)
|
||||
{
|
||||
NC_GRP_INFO_T *grpone, *grptwo;
|
||||
NC_TYPE_INFO_T *type1, *type2;
|
||||
int retval;
|
||||
|
||||
LOG((2, "nc_inq_type_equal: ncid1 0x%x typeid1 %d ncid2 0x%x typeid2 %d",
|
||||
ncid1, typeid1, ncid2, typeid2));
|
||||
|
||||
/* Check input. */
|
||||
if(equalp == NULL) return NC_NOERR;
|
||||
|
||||
if (typeid1 <= NC_NAT || typeid2 <= NC_NAT)
|
||||
return NC_EINVAL;
|
||||
|
||||
/* If one is atomic, and the other user-defined, the types are not
|
||||
* equal. */
|
||||
if ((typeid1 <= NC_STRING && typeid2 > NC_STRING) ||
|
||||
(typeid2 <= NC_STRING && typeid1 > NC_STRING))
|
||||
{
|
||||
if (equalp) *equalp = 0;
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/* If both are atomic types, the answer is easy. */
|
||||
if (typeid1 <= NUM_ATOMIC_TYPES)
|
||||
{
|
||||
if (equalp)
|
||||
{
|
||||
if (typeid1 == typeid2)
|
||||
*equalp = 1;
|
||||
else
|
||||
*equalp = 0;
|
||||
}
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/* Not atomic types - so find type1 and type2 information. */
|
||||
if ((retval = nc4_find_nc4_grp(ncid1, &grpone)))
|
||||
return retval;
|
||||
if (!(type1 = nc4_rec_find_nc_type(grpone->nc4_info,
|
||||
typeid1)))
|
||||
return NC_EBADTYPE;
|
||||
if ((retval = nc4_find_nc4_grp(ncid2, &grptwo)))
|
||||
return retval;
|
||||
if (!(type2 = nc4_rec_find_nc_type(grptwo->nc4_info,
|
||||
typeid2)))
|
||||
return NC_EBADTYPE;
|
||||
|
||||
/* Are the two types equal? */
|
||||
if (equalp)
|
||||
{
|
||||
if ((retval = H5Tequal(type1->native_hdf_typeid, type2->native_hdf_typeid)) < 0)
|
||||
return NC_EHDFERR;
|
||||
*equalp = 1 ? retval : 0;
|
||||
}
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Get the id of a type from the name.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param name Name of type.
|
||||
* @param typeidp Pointer that will get the type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_ENOMEM Out of memory.
|
||||
* @return ::NC_EINVAL Bad size.
|
||||
* @return ::NC_ENOTNC4 User types in netCDF-4 files only.
|
||||
* @return ::NC_EBADTYPE Type not found.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
extern int
|
||||
NC4_inq_typeid(int ncid, const char *name, nc_type *typeidp)
|
||||
{
|
||||
NC_GRP_INFO_T *grp;
|
||||
NC_GRP_INFO_T *grptwo;
|
||||
NC_HDF5_FILE_INFO_T *h5;
|
||||
NC_TYPE_INFO_T *type = NULL;
|
||||
char *norm_name;
|
||||
int i, retval;
|
||||
|
||||
/* Handle atomic types. */
|
||||
for (i = 0; i < NUM_ATOMIC_TYPES; i++)
|
||||
if (!strcmp(name, nc4_atomic_name[i]))
|
||||
{
|
||||
if (typeidp)
|
||||
*typeidp = i;
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/* Find info for this file and group, and set pointer to each. */
|
||||
if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
|
||||
return retval;
|
||||
assert(h5 && grp);
|
||||
|
||||
/* If the first char is a /, this is a fully-qualified
|
||||
* name. Otherwise, this had better be a local name (i.e. no / in
|
||||
* the middle). */
|
||||
if (name[0] != '/' && strstr(name, "/"))
|
||||
return NC_EINVAL;
|
||||
|
||||
/* Normalize name. */
|
||||
if (!(norm_name = (char*)malloc(strlen(name) + 1)))
|
||||
return NC_ENOMEM;
|
||||
if ((retval = nc4_normalize_name(name, norm_name))) {
|
||||
free(norm_name);
|
||||
return retval;
|
||||
}
|
||||
/* Is the type in this group? If not, search parents. */
|
||||
for (grptwo = grp; grptwo; grptwo = grptwo->parent) {
|
||||
type = (NC_TYPE_INFO_T*)ncindexlookup(grptwo->type,norm_name);
|
||||
if(type)
|
||||
{
|
||||
if (typeidp)
|
||||
*typeidp = type->hdr.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Still didn't find type? Search file recursively, starting at the
|
||||
* root group. */
|
||||
if (!type)
|
||||
if ((type = nc4_rec_find_named_type(grp->nc4_info->root_grp, norm_name)))
|
||||
if (typeidp)
|
||||
*typeidp = type->hdr.id;
|
||||
|
||||
free(norm_name);
|
||||
|
||||
/* OK, I give up already! */
|
||||
if (!type)
|
||||
return NC_EBADTYPE;
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal This internal function adds a new user defined type to
|
||||
* the metadata of a group of an open file.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param size Size in bytes of new type.
|
||||
* @param name Name of new type.
|
||||
* @param base_typeid Base type ID.
|
||||
* @param type_class NC_VLEN, NC_ENUM, or NC_STRING
|
||||
* @param typeidp Pointer that gets new type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_ENOTNC4 User types in netCDF-4 files only.
|
||||
* @return ::NC_EINVAL Bad size.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
static int
|
||||
add_user_type(int ncid, size_t size, const char *name, nc_type base_typeid,
|
||||
nc_type type_class, nc_type *typeidp)
|
||||
{
|
||||
NC_HDF5_FILE_INFO_T *h5;
|
||||
NC_GRP_INFO_T *grp;
|
||||
NC_TYPE_INFO_T *type;
|
||||
char norm_name[NC_MAX_NAME + 1];
|
||||
int retval;
|
||||
|
||||
/* Check and normalize the name. */
|
||||
if ((retval = nc4_check_name(name, norm_name)))
|
||||
return retval;
|
||||
|
||||
LOG((2, "%s: ncid 0x%x size %d name %s base_typeid %d ",
|
||||
__FUNCTION__, ncid, size, norm_name, base_typeid));
|
||||
|
||||
/* Find group metadata. */
|
||||
if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
|
||||
return retval;
|
||||
assert(h5 && grp);
|
||||
|
||||
/* Turn on define mode if it is not on. */
|
||||
if (!(h5->cmode & NC_INDEF))
|
||||
if ((retval = NC4_redef(ncid)))
|
||||
return retval;
|
||||
|
||||
/* No size is provided for vlens or enums, get it from the base type. */
|
||||
if (type_class == NC_VLEN || type_class == NC_ENUM)
|
||||
{
|
||||
if ((retval = nc4_get_typelen_mem(grp->nc4_info, base_typeid, &size)))
|
||||
return retval;
|
||||
}
|
||||
else if (size <= 0)
|
||||
return NC_EINVAL;
|
||||
|
||||
/* Check that this name is not in use as a var, grp, or type. */
|
||||
if ((retval = nc4_check_dup_name(grp, norm_name)))
|
||||
return retval;
|
||||
|
||||
/* Add to our list of types. */
|
||||
if ((retval = nc4_type_list_add(grp, size, norm_name, &type)))
|
||||
return retval;
|
||||
|
||||
/* Remember info about this type. */
|
||||
type->nc_type_class = type_class;
|
||||
if (type_class == NC_VLEN)
|
||||
type->u.v.base_nc_typeid = base_typeid;
|
||||
else if (type_class == NC_ENUM) {
|
||||
type->u.e.base_nc_typeid = base_typeid;
|
||||
type->u.e.enum_member = nclistnew();
|
||||
} else if (type_class == NC_COMPOUND)
|
||||
type->u.c.field = nclistnew();
|
||||
|
||||
/* Return the typeid to the user. */
|
||||
if (typeidp)
|
||||
*typeidp = type->hdr.id;
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Create a compound type.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param size Gets size in bytes of one element of type.
|
||||
* @param name Name of the type.
|
||||
* @param typeidp Gets the type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_def_compound(int ncid, size_t size, const char *name, nc_type *typeidp)
|
||||
{
|
||||
return add_user_type(ncid, size, name, 0, NC_COMPOUND, typeidp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Insert a named field into a compound type.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param typeid1 Type ID.
|
||||
* @param name Name of the type.
|
||||
* @param offset Offset of field.
|
||||
* @param field_typeid Field type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_insert_compound(int ncid, nc_type typeid1, const char *name, size_t offset,
|
||||
nc_type field_typeid)
|
||||
{
|
||||
return nc_insert_array_compound(ncid, typeid1, name, offset,
|
||||
field_typeid, 0, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Insert a named array into a compound type.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param typeid1 Type ID.
|
||||
* @param name Name of the array field.
|
||||
* @param offset Offset in bytes.
|
||||
* @param field_typeid Type of field.
|
||||
* @param ndims Number of dims for field.
|
||||
* @param dim_sizesp Array of dim sizes.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
extern int
|
||||
NC4_insert_array_compound(int ncid, int typeid1, const char *name,
|
||||
size_t offset, nc_type field_typeid,
|
||||
int ndims, const int *dim_sizesp)
|
||||
{
|
||||
NC_GRP_INFO_T *grp;
|
||||
NC_TYPE_INFO_T *type;
|
||||
char norm_name[NC_MAX_NAME + 1];
|
||||
int retval;
|
||||
|
||||
LOG((2, "nc_insert_array_compound: ncid 0x%x, typeid %d name %s "
|
||||
"offset %d field_typeid %d ndims %d", ncid, typeid1,
|
||||
name, offset, field_typeid, ndims));
|
||||
|
||||
/* Check and normalize the name. */
|
||||
if ((retval = nc4_check_name(name, norm_name)))
|
||||
return retval;
|
||||
|
||||
/* Find file metadata. */
|
||||
if ((retval = nc4_find_nc4_grp(ncid, &grp)))
|
||||
return retval;
|
||||
|
||||
/* Find type metadata. */
|
||||
if ((retval = nc4_find_type(grp->nc4_info, typeid1, &type)))
|
||||
return retval;
|
||||
|
||||
/* Did the user give us a good compound type typeid? */
|
||||
if (!type || type->nc_type_class != NC_COMPOUND)
|
||||
return NC_EBADTYPE;
|
||||
|
||||
/* If this type has already been written to the file, you can't
|
||||
* change it. */
|
||||
if (type->committed)
|
||||
return NC_ETYPDEFINED;
|
||||
|
||||
/* Insert new field into this type's list of fields. */
|
||||
if ((retval = nc4_field_list_add(type, norm_name, offset, 0, 0, field_typeid,
|
||||
ndims, dim_sizesp)))
|
||||
return retval;
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/* Opaque type. */
|
||||
|
||||
/**
|
||||
* @internal Create an opaque type. Provide a size and a name.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param datum_size Size in bytes of a datum.
|
||||
* @param name Name of new vlen type.
|
||||
* @param typeidp Pointer that gets new type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_def_opaque(int ncid, size_t datum_size, const char *name,
|
||||
nc_type *typeidp)
|
||||
{
|
||||
return add_user_type(ncid, datum_size, name, 0, NC_OPAQUE, typeidp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @internal Define a variable length type.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param name Name of new vlen type.
|
||||
* @param base_typeid Base type of vlen.
|
||||
* @param typeidp Pointer that gets new type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_def_vlen(int ncid, const char *name, nc_type base_typeid,
|
||||
nc_type *typeidp)
|
||||
{
|
||||
return add_user_type(ncid, 0, name, base_typeid, NC_VLEN, typeidp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Create an enum type. Provide a base type and a name. At
|
||||
* the moment only ints are accepted as base types.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param base_typeid Base type of vlen.
|
||||
* @param name Name of new vlen type.
|
||||
* @param typeidp Pointer that gets new type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_def_enum(int ncid, nc_type base_typeid, const char *name,
|
||||
nc_type *typeidp)
|
||||
{
|
||||
return add_user_type(ncid, 0, name, base_typeid, NC_ENUM, typeidp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Insert a identifier value into an enum type. The value
|
||||
* must fit within the size of the enum type, the identifier size must
|
||||
* be <= NC_MAX_NAME.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param typeid1 Type ID.
|
||||
* @param identifier Name of this enum value.
|
||||
* @param value Value of enum.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EBADTYPE Type not found.
|
||||
* @return ::NC_ETYPDEFINED Type already defined.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_insert_enum(int ncid, nc_type typeid1, const char *identifier,
|
||||
const void *value)
|
||||
{
|
||||
NC_GRP_INFO_T *grp;
|
||||
NC_TYPE_INFO_T *type;
|
||||
char norm_name[NC_MAX_NAME + 1];
|
||||
int retval;
|
||||
|
||||
LOG((2, "nc_insert_enum: ncid 0x%x, typeid %d identifier %s value %d", ncid,
|
||||
typeid1, identifier, value));
|
||||
|
||||
/* Check and normalize the name. */
|
||||
if ((retval = nc4_check_name(identifier, norm_name)))
|
||||
return retval;
|
||||
|
||||
/* Find file metadata. */
|
||||
if ((retval = nc4_find_nc4_grp(ncid, &grp)))
|
||||
return retval;
|
||||
|
||||
/* Find type metadata. */
|
||||
if ((retval = nc4_find_type(grp->nc4_info, typeid1, &type)))
|
||||
return retval;
|
||||
|
||||
/* Did the user give us a good enum typeid? */
|
||||
if (!type || type->nc_type_class != NC_ENUM)
|
||||
return NC_EBADTYPE;
|
||||
|
||||
/* If this type has already been written to the file, you can't
|
||||
* change it. */
|
||||
if (type->committed)
|
||||
return NC_ETYPDEFINED;
|
||||
|
||||
/* Insert new field into this type's list of fields. */
|
||||
if ((retval = nc4_enum_member_add(type, type->size,
|
||||
norm_name, value)))
|
||||
return retval;
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Insert one element into an already allocated vlen array
|
||||
* element.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param typeid1 Type ID.
|
||||
* @param vlen_element The VLEN element to insert.
|
||||
* @param len Length of element in bytes.
|
||||
* @param data Element data.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_put_vlen_element(int ncid, int typeid1, void *vlen_element,
|
||||
size_t len, const void *data)
|
||||
{
|
||||
nc_vlen_t *tmp = (nc_vlen_t*)vlen_element;
|
||||
tmp->len = len;
|
||||
tmp->p = (void *)data;
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Insert one element into an already allocated vlen array
|
||||
* element.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param typeid1 Type ID.
|
||||
* @param vlen_element The VLEN element to insert.
|
||||
* @param len Length of element in bytes.
|
||||
* @param data Element data.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_get_vlen_element(int ncid, int typeid1, const void *vlen_element,
|
||||
size_t *len, void *data)
|
||||
{
|
||||
const nc_vlen_t *tmp = (nc_vlen_t*)vlen_element;
|
||||
int type_size = 4;
|
||||
|
||||
*len = tmp->len;
|
||||
memcpy(data, tmp->p, tmp->len * type_size);
|
||||
return NC_NOERR;
|
||||
}
|
@ -22,24 +22,6 @@
|
||||
#include "ncutf8.h"
|
||||
#include "H5DSpublic.h"
|
||||
|
||||
#undef DEBUGH5
|
||||
|
||||
#ifdef DEBUGH5
|
||||
/**
|
||||
* @internal Provide a catchable error reporting function
|
||||
*
|
||||
* @param ignored Ignored.
|
||||
*
|
||||
* @return 0 for success.
|
||||
*/
|
||||
static herr_t
|
||||
h5catch(void* ignored)
|
||||
{
|
||||
H5Eprint(NULL);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* These are the default chunk cache sizes for HDF5 files created or
|
||||
* opened with netCDF-4. */
|
||||
extern size_t nc4_chunk_cache_size;
|
||||
@ -54,38 +36,6 @@ int nc_log_level = NC_TURN_OFF_LOGGING;
|
||||
|
||||
#endif /* LOGGING */
|
||||
|
||||
int nc4_hdf5_initialized = 0; /**< True if initialization has happened. */
|
||||
|
||||
/**
|
||||
* @internal Provide a wrapper for H5Eset_auto
|
||||
* @param func Pointer to func.
|
||||
* @param client_data Client data.
|
||||
*
|
||||
* @return 0 for success
|
||||
*/
|
||||
static herr_t
|
||||
set_auto(void* func, void *client_data)
|
||||
{
|
||||
#ifdef DEBUGH5
|
||||
return H5Eset_auto2(H5E_DEFAULT,(H5E_auto2_t)h5catch,client_data);
|
||||
#else
|
||||
return H5Eset_auto2(H5E_DEFAULT,(H5E_auto2_t)func,client_data);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Provide a function to do any necessary initialization of
|
||||
* the HDF5 library.
|
||||
*/
|
||||
void
|
||||
nc4_hdf5_initialize(void)
|
||||
{
|
||||
if (set_auto(NULL, NULL) < 0)
|
||||
LOG((0, "Couldn't turn off HDF5 error messages!"));
|
||||
LOG((1, "HDF5 error messages have been turned off."));
|
||||
nc4_hdf5_initialized = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Check and normalize and name.
|
||||
*
|
||||
@ -139,85 +89,6 @@ nc4_check_name(const char *name, char *norm_name)
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Given a varid, return the maximum length of a dimension
|
||||
* using dimid.
|
||||
*
|
||||
* @param grp Pointer to group info struct.
|
||||
* @param varid Variable ID.
|
||||
* @param dimid Dimension ID.
|
||||
* @param maxlen Pointer that gets the max length.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
static int
|
||||
find_var_dim_max_length(NC_GRP_INFO_T *grp, int varid, int dimid, size_t *maxlen)
|
||||
{
|
||||
hid_t datasetid = 0, spaceid = 0;
|
||||
NC_VAR_INFO_T *var;
|
||||
hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL;
|
||||
int d, dataset_ndims = 0;
|
||||
int retval = NC_NOERR;
|
||||
|
||||
*maxlen = 0;
|
||||
|
||||
/* Find this var. */
|
||||
var = (NC_VAR_INFO_T*)ncindexith(grp->vars,varid);
|
||||
if (!var) return NC_ENOTVAR;
|
||||
assert(var->hdr.id == varid);
|
||||
|
||||
/* If the var hasn't been created yet, its size is 0. */
|
||||
if (!var->created)
|
||||
{
|
||||
*maxlen = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get the number of records in the dataset. */
|
||||
if ((retval = nc4_open_var_grp2(grp, var->hdr.id, &datasetid)))
|
||||
BAIL(retval);
|
||||
if ((spaceid = H5Dget_space(datasetid)) < 0)
|
||||
BAIL(NC_EHDFERR);
|
||||
|
||||
/* If it's a scalar dataset, it has length one. */
|
||||
if (H5Sget_simple_extent_type(spaceid) == H5S_SCALAR)
|
||||
{
|
||||
*maxlen = (var->dimids && var->dimids[0] == dimid) ? 1 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check to make sure ndims is right, then get the len of each
|
||||
dim in the space. */
|
||||
if ((dataset_ndims = H5Sget_simple_extent_ndims(spaceid)) < 0)
|
||||
BAIL(NC_EHDFERR);
|
||||
if (dataset_ndims != var->ndims)
|
||||
BAIL(NC_EHDFERR);
|
||||
if (!(h5dimlen = malloc(dataset_ndims * sizeof(hsize_t))))
|
||||
BAIL(NC_ENOMEM);
|
||||
if (!(h5dimlenmax = malloc(dataset_ndims * sizeof(hsize_t))))
|
||||
BAIL(NC_ENOMEM);
|
||||
if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid,
|
||||
h5dimlen, h5dimlenmax)) < 0)
|
||||
BAIL(NC_EHDFERR);
|
||||
LOG((5, "find_var_dim_max_length: varid %d len %d max: %d",
|
||||
varid, (int)h5dimlen[0], (int)h5dimlenmax[0]));
|
||||
for (d=0; d<dataset_ndims; d++) {
|
||||
if (var->dimids[d] == dimid) {
|
||||
*maxlen = *maxlen > h5dimlen[d] ? *maxlen : h5dimlen[d];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if (spaceid > 0 && H5Sclose(spaceid) < 0)
|
||||
BAIL2(NC_EHDFERR);
|
||||
if (h5dimlen) free(h5dimlen);
|
||||
if (h5dimlenmax) free(h5dimlenmax);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Given an NC pointer, add the necessary stuff for a
|
||||
* netcdf-4 file.
|
||||
@ -487,37 +358,6 @@ nc4_find_var(NC_GRP_INFO_T *grp, const char *name, NC_VAR_INFO_T **var)
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Search for type with a given HDF type id.
|
||||
*
|
||||
* @param h5 File
|
||||
* @param target_hdf_typeid HDF5 type ID to find.
|
||||
*
|
||||
* @return Pointer to type info struct, or NULL if not found.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
NC_TYPE_INFO_T *
|
||||
nc4_rec_find_hdf_type(NC_HDF5_FILE_INFO_T* h5, hid_t target_hdf_typeid)
|
||||
{
|
||||
NC_TYPE_INFO_T *type;
|
||||
htri_t equal;
|
||||
int i;
|
||||
|
||||
assert(h5);
|
||||
|
||||
for(i=0;i<nclistlength(h5->alltypes);i++) {
|
||||
type = (NC_TYPE_INFO_T*)nclistget(h5->alltypes,i);
|
||||
if(type == NULL) continue;
|
||||
/* Is this the type we are searching for? */
|
||||
if ((equal = H5Tequal(type->native_hdf_typeid ? type->native_hdf_typeid : type->hdf_typeid, target_hdf_typeid)) < 0)
|
||||
return NULL;
|
||||
if (equal)
|
||||
return type;
|
||||
}
|
||||
/* Can't find it. Fate, why do you mock me? */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Locate netCDF type by name.
|
||||
*
|
||||
@ -599,56 +439,6 @@ nc4_find_type(const NC_HDF5_FILE_INFO_T *h5, nc_type typeid, NC_TYPE_INFO_T **ty
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Find the actual length of a dim by checking the length of
|
||||
* that dim in all variables that use it, in grp or children. **len
|
||||
* must be initialized to zero before this function is called.
|
||||
*
|
||||
* @param grp Pointer to group info struct.
|
||||
* @param dimid Dimension ID.
|
||||
* @param len Pointer to pointer that gets length.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
nc4_find_dim_len(NC_GRP_INFO_T *grp, int dimid, size_t **len)
|
||||
{
|
||||
NC_GRP_INFO_T *g;
|
||||
NC_VAR_INFO_T *var;
|
||||
int retval;
|
||||
int i;
|
||||
|
||||
assert(grp && len);
|
||||
LOG((3, "nc4_find_dim_len: grp->name %s dimid %d", grp->hdr.name, dimid));
|
||||
|
||||
/* If there are any groups, call this function recursively on
|
||||
* them. */
|
||||
for(i=0;i<ncindexsize(grp->children);i++) {
|
||||
g = (NC_GRP_INFO_T*)ncindexith(grp->children,i);
|
||||
if(g == NULL) continue;
|
||||
if ((retval = nc4_find_dim_len(g, dimid, len)))
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* For all variables in this group, find the ones that use this
|
||||
* dimension, and remember the max length. */
|
||||
for (i=0; i < ncindexsize(grp->vars); i++)
|
||||
{
|
||||
size_t mylen;
|
||||
var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
|
||||
if (var == NULL) continue;
|
||||
|
||||
/* Find max length of dim in this variable... */
|
||||
if ((retval = find_var_dim_max_length(grp, var->hdr.id, dimid, &mylen)))
|
||||
return retval;
|
||||
|
||||
**len = **len > mylen ? **len : mylen;
|
||||
}
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Given a group, find an att. If name is provided, use that,
|
||||
* otherwise use the attnum.
|
||||
@ -1757,230 +1547,6 @@ nc4_att_free(NC_ATT_INFO_T *att)
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Break a coordinate variable to separate the dimension and
|
||||
* the variable.
|
||||
*
|
||||
* This is called from nc_rename_dim() and nc_rename_var(). In some
|
||||
* renames, the coord variable must stay, but it is no longer a coord
|
||||
* variable. This function changes a coord var into an ordinary
|
||||
* variable.
|
||||
*
|
||||
* @param grp Pointer to group info struct.
|
||||
* @param coord_var Pointer to variable info struct.
|
||||
* @param dim Pointer to dimension info struct.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_ENOMEM Out of memory.
|
||||
* @author Quincey Koziol, Ed Hartnett
|
||||
*/
|
||||
int
|
||||
nc4_break_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *coord_var, NC_DIM_INFO_T *dim)
|
||||
{
|
||||
int retval = NC_NOERR;
|
||||
|
||||
/* Sanity checks */
|
||||
assert(grp && coord_var && dim && dim->coord_var == coord_var &&
|
||||
coord_var->dim[0] == dim && coord_var->dimids[0] == dim->hdr.id &&
|
||||
!dim->hdf_dimscaleid);
|
||||
LOG((3, "%s dim %s was associated with var %s, but now has different name",
|
||||
__func__, dim->hdr.name, coord_var->hdr.name));
|
||||
|
||||
/* If we're replacing an existing dimscale dataset, go to
|
||||
* every var in the file and detach this dimension scale. */
|
||||
if ((retval = rec_detach_scales(grp->nc4_info->root_grp,
|
||||
dim->hdr.id, coord_var->hdf_datasetid)))
|
||||
return retval;
|
||||
|
||||
/* Allow attached dimscales to be tracked on the [former]
|
||||
* coordinate variable */
|
||||
if (coord_var->ndims)
|
||||
{
|
||||
/* Coordinate variables shouldn't have dimscales attached. */
|
||||
assert(!coord_var->dimscale_attached);
|
||||
|
||||
/* Allocate space for tracking them */
|
||||
if (!(coord_var->dimscale_attached = calloc(coord_var->ndims,
|
||||
sizeof(nc_bool_t))))
|
||||
return NC_ENOMEM;
|
||||
}
|
||||
|
||||
/* Remove the atts that go with being a coordinate var. */
|
||||
/* if ((retval = remove_coord_atts(coord_var->hdf_datasetid))) */
|
||||
/* return retval; */
|
||||
|
||||
/* Detach dimension from variable */
|
||||
coord_var->dimscale = NC_FALSE;
|
||||
dim->coord_var = NULL;
|
||||
|
||||
/* Set state transition indicators */
|
||||
coord_var->was_coord_var = NC_TRUE;
|
||||
coord_var->became_coord_var = NC_FALSE;
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Delete an existing dimscale-only dataset.
|
||||
*
|
||||
* A dimscale-only HDF5 dataset is created when a dim is defined
|
||||
* without an accompanying coordinate variable.
|
||||
*
|
||||
* Sometimes, during renames, or late creation of variables, an
|
||||
* existing, dimscale-only dataset must be removed. This means
|
||||
* detaching all variables that use the dataset, then closing and
|
||||
* unlinking it.
|
||||
*
|
||||
* @param grp The grp of the dimscale-only dataset to be deleted, or a
|
||||
* higher group in the hierarchy (ex. root group).
|
||||
* @param dimid id of the dimension
|
||||
* @param dim Pointer to the dim with the dimscale-only dataset to be
|
||||
* deleted.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EHDFERR HDF5 error.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
delete_existing_dimscale_dataset(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T *dim)
|
||||
{
|
||||
int retval;
|
||||
|
||||
assert(grp && dim);
|
||||
LOG((2, "%s: deleting dimscale dataset %s dimid %d", __func__, dim->hdr.name,
|
||||
dimid));
|
||||
|
||||
/* Detach dimscale from any variables using it */
|
||||
if ((retval = rec_detach_scales(grp, dimid, dim->hdf_dimscaleid)) < 0)
|
||||
return retval;
|
||||
|
||||
/* Close the HDF5 dataset */
|
||||
if (H5Dclose(dim->hdf_dimscaleid) < 0)
|
||||
return NC_EHDFERR;
|
||||
dim->hdf_dimscaleid = 0;
|
||||
|
||||
/* Now delete the dataset. */
|
||||
if (H5Gunlink(grp->hdf_grpid, dim->hdr.name) < 0)
|
||||
return NC_EHDFERR;
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Reform a coordinate variable from a dimension and a
|
||||
* variable.
|
||||
*
|
||||
* @param grp Pointer to group info struct.
|
||||
* @param var Pointer to variable info struct.
|
||||
* @param dim Pointer to dimension info struct.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @author Quincey Koziol
|
||||
*/
|
||||
int
|
||||
nc4_reform_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, NC_DIM_INFO_T *dim)
|
||||
{
|
||||
int need_to_reattach_scales = 0;
|
||||
int retval = NC_NOERR;
|
||||
|
||||
assert(grp && var && dim);
|
||||
LOG((3, "%s: dim->hdr.name %s var->hdr.name %s", __func__, dim->hdr.name, var->hdr.name));
|
||||
|
||||
/* Detach dimscales from the [new] coordinate variable */
|
||||
if(var->dimscale_attached)
|
||||
{
|
||||
int dims_detached = 0;
|
||||
int finished = 0;
|
||||
int d;
|
||||
|
||||
/* Loop over all dimensions for variable */
|
||||
for (d = 0; d < var->ndims && !finished; d++)
|
||||
{
|
||||
/* Is there a dimscale attached to this axis? */
|
||||
if(var->dimscale_attached[d])
|
||||
{
|
||||
NC_GRP_INFO_T *g;
|
||||
int k;
|
||||
|
||||
for (g = grp; g && !finished; g = g->parent)
|
||||
{
|
||||
NC_DIM_INFO_T *dim1;
|
||||
for(k=0;k<ncindexsize(g->dim);k++)
|
||||
{
|
||||
if((dim1 = (NC_DIM_INFO_T*)ncindexith(g->dim,k)) == NULL) continue;
|
||||
if (var->dimids[d] == dim1->hdr.id)
|
||||
{
|
||||
hid_t dim_datasetid; /* Dataset ID for dimension */
|
||||
|
||||
/* Find dataset ID for dimension */
|
||||
if (dim1->coord_var)
|
||||
dim_datasetid = dim1->coord_var->hdf_datasetid;
|
||||
else
|
||||
dim_datasetid = dim1->hdf_dimscaleid;
|
||||
|
||||
/* dim_datasetid may be 0 in some cases when
|
||||
* renames of dims and vars are happening. In
|
||||
* this case, the scale has already been
|
||||
* detached. */
|
||||
if (dim_datasetid > 0)
|
||||
{
|
||||
LOG((3, "detaching scale from %s", var->hdr.name));
|
||||
if (H5DSdetach_scale(var->hdf_datasetid, dim_datasetid, d) < 0)
|
||||
BAIL(NC_EHDFERR);
|
||||
}
|
||||
var->dimscale_attached[d] = NC_FALSE;
|
||||
if (dims_detached++ == var->ndims)
|
||||
finished++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* next variable dimension */
|
||||
|
||||
/* Release & reset the array tracking attached dimscales */
|
||||
free(var->dimscale_attached);
|
||||
var->dimscale_attached = NULL;
|
||||
need_to_reattach_scales++;
|
||||
}
|
||||
|
||||
/* Use variable's dataset ID for the dimscale ID. */
|
||||
if (dim->hdf_dimscaleid && grp != NULL)
|
||||
{
|
||||
LOG((3, "closing and unlinking dimscale dataset %s", dim->hdr.name));
|
||||
if (H5Dclose(dim->hdf_dimscaleid) < 0)
|
||||
BAIL(NC_EHDFERR);
|
||||
dim->hdf_dimscaleid = 0;
|
||||
|
||||
/* Now delete the dimscale's dataset
|
||||
(it will be recreated later, if necessary) */
|
||||
if (H5Gunlink(grp->hdf_grpid, dim->hdr.name) < 0)
|
||||
return NC_EDIMMETA;
|
||||
}
|
||||
|
||||
/* Attach variable to dimension */
|
||||
var->dimscale = NC_TRUE;
|
||||
dim->coord_var = var;
|
||||
|
||||
/* Check if this variable used to be a coord. var */
|
||||
if (need_to_reattach_scales || (var->was_coord_var && grp != NULL))
|
||||
{
|
||||
/* Reattach the scale everywhere it is used. */
|
||||
/* (Recall that netCDF dimscales are always 1-D) */
|
||||
if ((retval = rec_reattach_scales(grp->nc4_info->root_grp,
|
||||
var->dimids[0], var->hdf_datasetid)))
|
||||
return retval;
|
||||
|
||||
/* Set state transition indicator (cancels earlier transition) */
|
||||
var->was_coord_var = NC_FALSE;
|
||||
}
|
||||
else
|
||||
/* Set state transition indicator */
|
||||
var->became_coord_var = NC_TRUE;
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Normalize a UTF8 name. Put the result in norm_name, which
|
||||
* can be NC_MAX_NAME + 1 in size. This function makes sure the free()
|
||||
@ -2030,23 +1596,23 @@ nc_set_log_level(int new_level)
|
||||
if(!nc4_hdf5_initialized)
|
||||
nc4_hdf5_initialize();
|
||||
|
||||
/* If the user wants to completely turn off logging, turn off HDF5
|
||||
logging too. Now I truly can't think of what to do if this
|
||||
fails, so just ignore the return code. */
|
||||
if (new_level == NC_TURN_OFF_LOGGING)
|
||||
{
|
||||
set_auto(NULL,NULL);
|
||||
LOG((1, "HDF5 error messages turned off!"));
|
||||
}
|
||||
/* /\* If the user wants to completely turn off logging, turn off HDF5 */
|
||||
/* logging too. Now I truly can't think of what to do if this */
|
||||
/* fails, so just ignore the return code. *\/ */
|
||||
/* if (new_level == NC_TURN_OFF_LOGGING) */
|
||||
/* { */
|
||||
/* set_auto(NULL,NULL); */
|
||||
/* LOG((1, "HDF5 error messages turned off!")); */
|
||||
/* } */
|
||||
|
||||
/* Do we need to turn HDF5 logging back on? */
|
||||
if (new_level > NC_TURN_OFF_LOGGING &&
|
||||
nc_log_level <= NC_TURN_OFF_LOGGING)
|
||||
{
|
||||
if (set_auto((H5E_auto_t)&H5Eprint, stderr) < 0)
|
||||
LOG((0, "H5Eset_auto failed!"));
|
||||
LOG((1, "HDF5 error messages turned on."));
|
||||
}
|
||||
/* /\* Do we need to turn HDF5 logging back on? *\/ */
|
||||
/* if (new_level > NC_TURN_OFF_LOGGING && */
|
||||
/* nc_log_level <= NC_TURN_OFF_LOGGING) */
|
||||
/* { */
|
||||
/* if (set_auto((H5E_auto_t)&H5Eprint, stderr) < 0) */
|
||||
/* LOG((0, "H5Eset_auto failed!")); */
|
||||
/* LOG((1, "HDF5 error messages turned on.")); */
|
||||
/* } */
|
||||
|
||||
/* Now remember the new level. */
|
||||
nc_log_level = new_level;
|
||||
|
@ -1,29 +1,24 @@
|
||||
/* Copyright 2005, University Corporation for Atmospheric Research. See
|
||||
* the COPYRIGHT file for copying and redistribution conditions. */
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @internal This file is part of netcdf-4, a netCDF-like interface
|
||||
* for HDF5, or a HDF5 backend for netCDF, depending on your point of
|
||||
* view.
|
||||
* @file @internal This file is part of netcdf-4, a netCDF-like
|
||||
* interface for HDF5, or a HDF5 backend for netCDF, depending on your
|
||||
* point of view.
|
||||
*
|
||||
* This file handles the nc4 user-defined type functions
|
||||
* (i.e. compound and opaque types).
|
||||
*
|
||||
* Copyright 2005, University Corporation for Atmospheric Research. See
|
||||
* the COPYRIGHT file for copying and redistribution conditions.
|
||||
*
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
#include "nc4internal.h"
|
||||
#include "nc4dispatch.h"
|
||||
|
||||
#define NUM_ATOMIC_TYPES (NC_MAX_ATOMIC_TYPE+1) /**< Number of netCDF atomic types. */
|
||||
|
||||
/** @internal Names of atomic types. */
|
||||
char* nc4_atomic_name[NUM_ATOMIC_TYPES] = {"none", "byte", "char",
|
||||
"short", "int", "float",
|
||||
"double", "ubyte",
|
||||
"ushort", "uint",
|
||||
"int64", "uint64", "string"};
|
||||
"short", "int", "float",
|
||||
"double", "ubyte",
|
||||
"ushort", "uint",
|
||||
"int64", "uint64", "string"};
|
||||
|
||||
/* The sizes of types may vary from platform to platform, but within
|
||||
* netCDF files, type sizes are fixed. */
|
||||
@ -36,161 +31,6 @@ char* nc4_atomic_name[NUM_ATOMIC_TYPES] = {"none", "byte", "char",
|
||||
#define NC_DOUBLE_LEN 8 /**< @internal Size of double. */
|
||||
#define NC_INT64_LEN 8 /**< @internal Size of int64. */
|
||||
|
||||
/**
|
||||
* @internal Determine if two types are equal.
|
||||
*
|
||||
* @param ncid1 First file/group ID.
|
||||
* @param typeid1 First type ID.
|
||||
* @param ncid2 Second file/group ID.
|
||||
* @param typeid2 Second type ID.
|
||||
* @param equalp Pointer that will get 1 if the two types are equal.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EBADTYPE Type not found.
|
||||
* @return ::NC_EINVAL Invalid type.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
extern int
|
||||
NC4_inq_type_equal(int ncid1, nc_type typeid1, int ncid2,
|
||||
nc_type typeid2, int *equalp)
|
||||
{
|
||||
NC_GRP_INFO_T *grpone, *grptwo;
|
||||
NC_TYPE_INFO_T *type1, *type2;
|
||||
int retval;
|
||||
|
||||
LOG((2, "nc_inq_type_equal: ncid1 0x%x typeid1 %d ncid2 0x%x typeid2 %d",
|
||||
ncid1, typeid1, ncid2, typeid2));
|
||||
|
||||
/* Check input. */
|
||||
if(equalp == NULL) return NC_NOERR;
|
||||
|
||||
if (typeid1 <= NC_NAT || typeid2 <= NC_NAT)
|
||||
return NC_EINVAL;
|
||||
|
||||
/* If one is atomic, and the other user-defined, the types are not
|
||||
* equal. */
|
||||
if ((typeid1 <= NC_STRING && typeid2 > NC_STRING) ||
|
||||
(typeid2 <= NC_STRING && typeid1 > NC_STRING))
|
||||
{
|
||||
if (equalp) *equalp = 0;
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/* If both are atomic types, the answer is easy. */
|
||||
if (typeid1 <= NUM_ATOMIC_TYPES)
|
||||
{
|
||||
if (equalp)
|
||||
{
|
||||
if (typeid1 == typeid2)
|
||||
*equalp = 1;
|
||||
else
|
||||
*equalp = 0;
|
||||
}
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/* Not atomic types - so find type1 and type2 information. */
|
||||
if ((retval = nc4_find_nc4_grp(ncid1, &grpone)))
|
||||
return retval;
|
||||
if (!(type1 = nc4_rec_find_nc_type(grpone->nc4_info,
|
||||
typeid1)))
|
||||
return NC_EBADTYPE;
|
||||
if ((retval = nc4_find_nc4_grp(ncid2, &grptwo)))
|
||||
return retval;
|
||||
if (!(type2 = nc4_rec_find_nc_type(grptwo->nc4_info,
|
||||
typeid2)))
|
||||
return NC_EBADTYPE;
|
||||
|
||||
/* Are the two types equal? */
|
||||
if (equalp)
|
||||
{
|
||||
if ((retval = H5Tequal(type1->native_hdf_typeid, type2->native_hdf_typeid)) < 0)
|
||||
return NC_EHDFERR;
|
||||
*equalp = 1 ? retval : 0;
|
||||
}
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Get the id of a type from the name.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param name Name of type.
|
||||
* @param typeidp Pointer that will get the type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_ENOMEM Out of memory.
|
||||
* @return ::NC_EINVAL Bad size.
|
||||
* @return ::NC_ENOTNC4 User types in netCDF-4 files only.
|
||||
* @return ::NC_EBADTYPE Type not found.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
extern int
|
||||
NC4_inq_typeid(int ncid, const char *name, nc_type *typeidp)
|
||||
{
|
||||
NC_GRP_INFO_T *grp;
|
||||
NC_GRP_INFO_T *grptwo;
|
||||
NC_HDF5_FILE_INFO_T *h5;
|
||||
NC_TYPE_INFO_T *type = NULL;
|
||||
char *norm_name;
|
||||
int i, retval;
|
||||
|
||||
/* Handle atomic types. */
|
||||
for (i = 0; i < NUM_ATOMIC_TYPES; i++)
|
||||
if (!strcmp(name, nc4_atomic_name[i]))
|
||||
{
|
||||
if (typeidp)
|
||||
*typeidp = i;
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/* Find info for this file and group, and set pointer to each. */
|
||||
if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
|
||||
return retval;
|
||||
assert(h5 && grp);
|
||||
|
||||
/* If the first char is a /, this is a fully-qualified
|
||||
* name. Otherwise, this had better be a local name (i.e. no / in
|
||||
* the middle). */
|
||||
if (name[0] != '/' && strstr(name, "/"))
|
||||
return NC_EINVAL;
|
||||
|
||||
/* Normalize name. */
|
||||
if (!(norm_name = (char*)malloc(strlen(name) + 1)))
|
||||
return NC_ENOMEM;
|
||||
if ((retval = nc4_normalize_name(name, norm_name))) {
|
||||
free(norm_name);
|
||||
return retval;
|
||||
}
|
||||
/* Is the type in this group? If not, search parents. */
|
||||
for (grptwo = grp; grptwo; grptwo = grptwo->parent) {
|
||||
type = (NC_TYPE_INFO_T*)ncindexlookup(grptwo->type,norm_name);
|
||||
if(type)
|
||||
{
|
||||
if (typeidp)
|
||||
*typeidp = type->hdr.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Still didn't find type? Search file recursively, starting at the
|
||||
* root group. */
|
||||
if (!type)
|
||||
if ((type = nc4_rec_find_named_type(grp->nc4_info->root_grp, norm_name)))
|
||||
if (typeidp)
|
||||
*typeidp = type->hdr.id;
|
||||
|
||||
free(norm_name);
|
||||
|
||||
/* OK, I give up already! */
|
||||
if (!type)
|
||||
return NC_EBADTYPE;
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Find all user-defined types for a location. This finds
|
||||
* all user-defined types in a group.
|
||||
@ -239,86 +79,6 @@ NC4_inq_typeids(int ncid, int *ntypes, int *typeids)
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal This internal function adds a new user defined type to
|
||||
* the metadata of a group of an open file.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param size Size in bytes of new type.
|
||||
* @param name Name of new type.
|
||||
* @param base_typeid Base type ID.
|
||||
* @param type_class NC_VLEN, NC_ENUM, or NC_STRING
|
||||
* @param typeidp Pointer that gets new type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_ENOTNC4 User types in netCDF-4 files only.
|
||||
* @return ::NC_EINVAL Bad size.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
static int
|
||||
add_user_type(int ncid, size_t size, const char *name, nc_type base_typeid,
|
||||
nc_type type_class, nc_type *typeidp)
|
||||
{
|
||||
NC_HDF5_FILE_INFO_T *h5;
|
||||
NC_GRP_INFO_T *grp;
|
||||
NC_TYPE_INFO_T *type;
|
||||
char norm_name[NC_MAX_NAME + 1];
|
||||
int retval;
|
||||
|
||||
/* Check and normalize the name. */
|
||||
if ((retval = nc4_check_name(name, norm_name)))
|
||||
return retval;
|
||||
|
||||
LOG((2, "%s: ncid 0x%x size %d name %s base_typeid %d ",
|
||||
__FUNCTION__, ncid, size, norm_name, base_typeid));
|
||||
|
||||
/* Find group metadata. */
|
||||
if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
|
||||
return retval;
|
||||
assert(h5 && grp);
|
||||
|
||||
/* Turn on define mode if it is not on. */
|
||||
if (!(h5->cmode & NC_INDEF))
|
||||
if ((retval = NC4_redef(ncid)))
|
||||
return retval;
|
||||
|
||||
/* No size is provided for vlens or enums, get it from the base type. */
|
||||
if (type_class == NC_VLEN || type_class == NC_ENUM)
|
||||
{
|
||||
if ((retval = nc4_get_typelen_mem(grp->nc4_info, base_typeid, &size)))
|
||||
return retval;
|
||||
}
|
||||
else if (size <= 0)
|
||||
return NC_EINVAL;
|
||||
|
||||
/* Check that this name is not in use as a var, grp, or type. */
|
||||
if ((retval = nc4_check_dup_name(grp, norm_name)))
|
||||
return retval;
|
||||
|
||||
/* Add to our list of types. */
|
||||
if ((retval = nc4_type_list_add(grp, size, norm_name, &type)))
|
||||
return retval;
|
||||
|
||||
/* Remember info about this type. */
|
||||
type->nc_type_class = type_class;
|
||||
if (type_class == NC_VLEN)
|
||||
type->u.v.base_nc_typeid = base_typeid;
|
||||
else if (type_class == NC_ENUM) {
|
||||
type->u.e.base_nc_typeid = base_typeid;
|
||||
type->u.e.enum_member = nclistnew();
|
||||
} else if (type_class == NC_COMPOUND)
|
||||
type->u.c.field = nclistnew();
|
||||
|
||||
/* Return the typeid to the user. */
|
||||
if (typeidp)
|
||||
*typeidp = type->hdr.id;
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Get the name and size of a type. For strings, 1 is
|
||||
* returned. For VLEN the base type len is returned.
|
||||
@ -381,109 +141,6 @@ NC4_inq_type(int ncid, nc_type typeid1, char *name, size_t *size)
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Create a compound type.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param size Gets size in bytes of one element of type.
|
||||
* @param name Name of the type.
|
||||
* @param typeidp Gets the type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_def_compound(int ncid, size_t size, const char *name, nc_type *typeidp)
|
||||
{
|
||||
return add_user_type(ncid, size, name, 0, NC_COMPOUND, typeidp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Insert a named field into a compound type.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param typeid1 Type ID.
|
||||
* @param name Name of the type.
|
||||
* @param offset Offset of field.
|
||||
* @param field_typeid Field type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_insert_compound(int ncid, nc_type typeid1, const char *name, size_t offset,
|
||||
nc_type field_typeid)
|
||||
{
|
||||
return nc_insert_array_compound(ncid, typeid1, name, offset,
|
||||
field_typeid, 0, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Insert a named array into a compound type.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param typeid1 Type ID.
|
||||
* @param name Name of the array field.
|
||||
* @param offset Offset in bytes.
|
||||
* @param field_typeid Type of field.
|
||||
* @param ndims Number of dims for field.
|
||||
* @param dim_sizesp Array of dim sizes.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
extern int
|
||||
NC4_insert_array_compound(int ncid, int typeid1, const char *name,
|
||||
size_t offset, nc_type field_typeid,
|
||||
int ndims, const int *dim_sizesp)
|
||||
{
|
||||
NC_GRP_INFO_T *grp;
|
||||
NC_TYPE_INFO_T *type;
|
||||
char norm_name[NC_MAX_NAME + 1];
|
||||
int retval;
|
||||
|
||||
LOG((2, "nc_insert_array_compound: ncid 0x%x, typeid %d name %s "
|
||||
"offset %d field_typeid %d ndims %d", ncid, typeid1,
|
||||
name, offset, field_typeid, ndims));
|
||||
|
||||
/* Check and normalize the name. */
|
||||
if ((retval = nc4_check_name(name, norm_name)))
|
||||
return retval;
|
||||
|
||||
/* Find file metadata. */
|
||||
if ((retval = nc4_find_nc4_grp(ncid, &grp)))
|
||||
return retval;
|
||||
|
||||
/* Find type metadata. */
|
||||
if ((retval = nc4_find_type(grp->nc4_info, typeid1, &type)))
|
||||
return retval;
|
||||
|
||||
/* Did the user give us a good compound type typeid? */
|
||||
if (!type || type->nc_type_class != NC_COMPOUND)
|
||||
return NC_EBADTYPE;
|
||||
|
||||
/* If this type has already been written to the file, you can't
|
||||
* change it. */
|
||||
if (type->committed)
|
||||
return NC_ETYPDEFINED;
|
||||
|
||||
/* Insert new field into this type's list of fields. */
|
||||
if ((retval = nc4_field_list_add(type, norm_name, offset, 0, 0, field_typeid,
|
||||
ndims, dim_sizesp)))
|
||||
return retval;
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Find info about any user defined type.
|
||||
*
|
||||
@ -705,74 +362,6 @@ NC4_inq_compound_fieldindex(int ncid, nc_type typeid1, const char *name, int *fi
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
|
||||
/* Opaque type. */
|
||||
|
||||
/**
|
||||
* @internal Create an opaque type. Provide a size and a name.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param datum_size Size in bytes of a datum.
|
||||
* @param name Name of new vlen type.
|
||||
* @param typeidp Pointer that gets new type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_def_opaque(int ncid, size_t datum_size, const char *name,
|
||||
nc_type *typeidp)
|
||||
{
|
||||
return add_user_type(ncid, datum_size, name, 0, NC_OPAQUE, typeidp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @internal Define a variable length type.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param name Name of new vlen type.
|
||||
* @param base_typeid Base type of vlen.
|
||||
* @param typeidp Pointer that gets new type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_def_vlen(int ncid, const char *name, nc_type base_typeid,
|
||||
nc_type *typeidp)
|
||||
{
|
||||
return add_user_type(ncid, 0, name, base_typeid, NC_VLEN, typeidp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Create an enum type. Provide a base type and a name. At
|
||||
* the moment only ints are accepted as base types.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param base_typeid Base type of vlen.
|
||||
* @param name Name of new vlen type.
|
||||
* @param typeidp Pointer that gets new type ID.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EMAXNAME Name is too long.
|
||||
* @return ::NC_EBADNAME Name breaks netCDF name rules.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_def_enum(int ncid, nc_type base_typeid, const char *name,
|
||||
nc_type *typeidp)
|
||||
{
|
||||
return add_user_type(ncid, 0, name, base_typeid, NC_ENUM, typeidp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @internal Get enum name from enum value. Name size will be <=
|
||||
* NC_MAX_NAME.
|
||||
@ -915,108 +504,3 @@ NC4_inq_enum_member(int ncid, nc_type typeid1, int idx, char *identifier,
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Insert a identifier value into an enum type. The value
|
||||
* must fit within the size of the enum type, the identifier size must
|
||||
* be <= NC_MAX_NAME.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param typeid1 Type ID.
|
||||
* @param identifier Name of this enum value.
|
||||
* @param value Value of enum.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @return ::NC_EBADID Bad ncid.
|
||||
* @return ::NC_EBADTYPE Type not found.
|
||||
* @return ::NC_ETYPDEFINED Type already defined.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_insert_enum(int ncid, nc_type typeid1, const char *identifier,
|
||||
const void *value)
|
||||
{
|
||||
NC_GRP_INFO_T *grp;
|
||||
NC_TYPE_INFO_T *type;
|
||||
char norm_name[NC_MAX_NAME + 1];
|
||||
int retval;
|
||||
|
||||
LOG((2, "nc_insert_enum: ncid 0x%x, typeid %d identifier %s value %d", ncid,
|
||||
typeid1, identifier, value));
|
||||
|
||||
/* Check and normalize the name. */
|
||||
if ((retval = nc4_check_name(identifier, norm_name)))
|
||||
return retval;
|
||||
|
||||
/* Find file metadata. */
|
||||
if ((retval = nc4_find_nc4_grp(ncid, &grp)))
|
||||
return retval;
|
||||
|
||||
/* Find type metadata. */
|
||||
if ((retval = nc4_find_type(grp->nc4_info, typeid1, &type)))
|
||||
return retval;
|
||||
|
||||
/* Did the user give us a good enum typeid? */
|
||||
if (!type || type->nc_type_class != NC_ENUM)
|
||||
return NC_EBADTYPE;
|
||||
|
||||
/* If this type has already been written to the file, you can't
|
||||
* change it. */
|
||||
if (type->committed)
|
||||
return NC_ETYPDEFINED;
|
||||
|
||||
/* Insert new field into this type's list of fields. */
|
||||
if ((retval = nc4_enum_member_add(type, type->size,
|
||||
norm_name, value)))
|
||||
return retval;
|
||||
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Insert one element into an already allocated vlen array
|
||||
* element.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param typeid1 Type ID.
|
||||
* @param vlen_element The VLEN element to insert.
|
||||
* @param len Length of element in bytes.
|
||||
* @param data Element data.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_put_vlen_element(int ncid, int typeid1, void *vlen_element,
|
||||
size_t len, const void *data)
|
||||
{
|
||||
nc_vlen_t *tmp = (nc_vlen_t*)vlen_element;
|
||||
tmp->len = len;
|
||||
tmp->p = (void *)data;
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Insert one element into an already allocated vlen array
|
||||
* element.
|
||||
*
|
||||
* @param ncid File and group ID.
|
||||
* @param typeid1 Type ID.
|
||||
* @param vlen_element The VLEN element to insert.
|
||||
* @param len Length of element in bytes.
|
||||
* @param data Element data.
|
||||
*
|
||||
* @return ::NC_NOERR No error.
|
||||
* @author Ed Hartnett
|
||||
*/
|
||||
int
|
||||
NC4_get_vlen_element(int ncid, int typeid1, const void *vlen_element,
|
||||
size_t *len, void *data)
|
||||
{
|
||||
const nc_vlen_t *tmp = (nc_vlen_t*)vlen_element;
|
||||
int type_size = 4;
|
||||
|
||||
*len = tmp->len;
|
||||
memcpy(data, tmp->p, tmp->len * type_size);
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user