netcdf-c/libsrc4/nc4internal.c

1783 lines
46 KiB
C
Raw Normal View History

2018-01-31 23:44:33 +08:00
/* 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.
2017-12-05 03:21:14 +08:00
*
* @author Ed Hartnett, Dennis Heimbigner, Ward Fisher
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
#include "config.h"
#include "nc4internal.h"
#include "nc.h" /* from libsrc */
#include "ncdispatch.h" /* from libdispatch */
#include "ncutf8.h"
2010-06-03 21:24:43 +08:00
/* 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. */
Primary change: add dap4 support Specific changes: 1. Add dap4 code: libdap4 and dap4_test. Note that until the d4ts server problem is solved, dap4 is turned off. 2. Modify various files to support dap4 flags: configure.ac, Makefile.am, CMakeLists.txt, etc. 3. Add nc_test/test_common.sh. This centralizes the handling of the locations of various things in the build tree: e.g. where is ncgen.exe located. See nc_test/test_common.sh for details. 4. Modify .sh files to use test_common.sh 5. Obsolete separate oc2 by moving it to be part of netcdf-c. This means replacing code with netcdf-c equivalents. 5. Add --with-testserver to configure.ac to allow override of the servers to be used for --enable-dap-remote-tests. 6. There were multiple versions of nctypealignment code. Try to centralize in libdispatch/doffset.c and include/ncoffsets.h 7. Add a unit test for the ncuri code because of its complexity. 8. Move the findserver code out of libdispatch and into a separate, self contained program in ncdap_test and dap4_test. 9. Move the dispatch header files (nc{3,4}dispatch.h) to .../include because they are now shared by modules. 10. Revamp the handling of TOPSRCDIR and TOPBUILDDIR for shell scripts. 11. Make use of MREMAP if available 12. Misc. minor changes e.g. - #include <config.h> -> #include "config.h" - Add some no-install headers to /include - extern -> EXTERNL and vice versa as needed - misc header cleanup - clean up checking for misc. unix vs microsoft functions 13. Change copyright decls in some files to point to LICENSE file. 14. Add notes to RELEASENOTES.md
2017-03-09 08:01:10 +08:00
int nc_log_level = NC_TURN_OFF_LOGGING;
2010-06-03 21:24:43 +08:00
#endif /* LOGGING */
2017-12-03 22:57:21 +08:00
/**
2017-12-05 03:21:14 +08:00
* @internal Check and normalize and name.
2017-12-03 22:57:21 +08:00
*
* @param name Name to normalize.
* @param norm_name The normalized name.
*
* @return ::NC_NOERR No error.
* @return ::NC_EMAXNAME Name too long.
2018-01-18 22:36:52 +08:00
* @return ::NC_EINVAL NULL given for name.
* @return ::NC_ENOMEM Out of memory.
2017-12-03 22:57:21 +08:00
* @author Dennis Heimbigner
*/
2010-06-03 21:24:43 +08:00
int
nc4_check_name(const char *name, char *norm_name)
{
char *temp;
int retval;
2018-01-31 23:44:33 +08:00
2018-01-18 22:36:52 +08:00
assert(norm_name);
2018-01-31 23:44:33 +08:00
2018-01-18 22:36:52 +08:00
/* Check for NULL. */
if (!name)
return NC_EINVAL;
2010-06-03 21:24:43 +08:00
/* Make sure this is a valid netcdf name. This should be done
* before the name is normalized, because it gives better error
* codes for bad utf8 strings. */
if ((retval = NC_check_name(name)))
return retval;
/* Normalize the name. */
if ((retval = nc_utf8_normalize((const unsigned char *)name,
(unsigned char **)&temp)))
return retval;
/* Check length of normalized name. */
if (strlen(temp) > NC_MAX_NAME)
{
2017-12-05 03:21:14 +08:00
free(temp);
return NC_EMAXNAME;
}
/* Copy the normalized name. */
2010-06-03 21:24:43 +08:00
strcpy(norm_name, temp);
free(temp);
2010-06-03 21:24:43 +08:00
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
* @internal Given an NC pointer, add the necessary stuff for a
2017-12-05 03:21:14 +08:00
* netcdf-4 file.
*
* @param nc Pointer to file's NC struct.
* @param path The file name of the new file.
* @param mode The mode flag.
2017-12-03 22:57:21 +08:00
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett
*/
2010-06-03 21:24:43 +08:00
int
nc4_nc4f_list_add(NC *nc, const char *path, int mode)
2010-06-03 21:24:43 +08:00
{
NC_FILE_INFO_T *h5;
2010-06-03 21:24:43 +08:00
assert(nc && !NC4_DATA(nc) && path);
2010-06-03 21:24:43 +08:00
/* We need to malloc and
2010-06-03 21:24:43 +08:00
initialize the substructure NC_HDF_FILE_INFO_T. */
if (!(h5 = calloc(1, sizeof(NC_FILE_INFO_T))))
2010-06-03 21:24:43 +08:00
return NC_ENOMEM;
nc->dispatchdata = h5;
h5->controller = nc;
2010-06-03 21:24:43 +08:00
/* Hang on to cmode, and note that we're in define mode. */
h5->cmode = mode | NC_INDEF;
/* The next_typeid needs to be set beyond the end of our atomic
* types. */
h5->next_typeid = NC_FIRSTUSERTYPEID;
h5->alldims = nclistnew();
h5->alltypes = nclistnew();
h5->allgroups = nclistnew();
2010-06-03 21:24:43 +08:00
/* There's always at least one open group - the root
* group. Allocate space for one group's worth of information. Set
* its hdf id, name, and a pointer to it's file structure. */
return nc4_grp_list_add(h5, NULL, NC_GROUP_NAME, &h5->root_grp);
2010-06-03 21:24:43 +08:00
}
2017-12-03 22:57:21 +08:00
/**
* @internal Given an ncid, find the relevant group and return a
* pointer to it, return an error of this is not a netcdf-4 file (or
2017-12-05 03:21:14 +08:00
* if strict nc3 is turned on for this file.)
*
* @param ncid File and group ID.
* @param grp Pointer that gets pointer to group info struct.
2017-12-03 22:57:21 +08:00
*
* @return ::NC_NOERR No error.
2017-12-05 03:21:14 +08:00
* @return ::NC_ENOTNC4 Not a netCDF-4 file.
* @return ::NC_ESTRICTNC3 Not allowed for classic model.
2017-12-03 22:57:21 +08:00
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_find_nc4_grp(int ncid, NC_GRP_INFO_T **grp)
{
NC_FILE_INFO_T* h5;
NC *f = nc4_find_nc_file(ncid,&h5);
2010-06-03 21:24:43 +08:00
if(f == NULL) return NC_EBADID;
/* No netcdf-3 files allowed! */
if (!h5) return NC_ENOTNC4;
assert(h5->root_grp);
2010-06-03 21:24:43 +08:00
/* This function demands netcdf-4 files without strict nc3
* rules.*/
if (h5->cmode & NC_CLASSIC_MODEL) return NC_ESTRICTNC3;
2010-06-03 21:24:43 +08:00
/* If we can't find it, the grp id part of ncid is bad. */
if (!(*grp = nc4_rec_find_grp(h5, (ncid & GRP_ID_MASK))))
return NC_EBADID;
2010-06-03 21:24:43 +08:00
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
* @internal Given an ncid, find the relevant group and return a
* pointer to it, also set a pointer to the nc4_info struct of the
2017-12-05 03:21:14 +08:00
* related file. For netcdf-3 files, *h5 will be set to NULL.
*
* @param ncid File and group ID.
* @param grpp Pointer that gets pointer to group info struct.
* @param h5p Pointer to HDF5 file struct.
2017-12-03 22:57:21 +08:00
*
* @return ::NC_NOERR No error.
2017-12-05 03:21:14 +08:00
* @return ::NC_EBADID Bad ncid.
2017-12-03 22:57:21 +08:00
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_find_grp_h5(int ncid, NC_GRP_INFO_T **grpp, NC_FILE_INFO_T **h5p)
2010-06-03 21:24:43 +08:00
{
NC_FILE_INFO_T *h5;
2017-12-05 03:21:14 +08:00
NC_GRP_INFO_T *grp;
NC *f = nc4_find_nc_file(ncid,&h5);
if(f == NULL) return NC_EBADID;
if (h5) {
assert(h5->root_grp);
/* If we can't find it, the grp id part of ncid is bad. */
if (!(grp = nc4_rec_find_grp(h5, (ncid & GRP_ID_MASK))))
2017-12-05 03:21:14 +08:00
return NC_EBADID;
h5 = (grp)->nc4_info;
assert(h5);
} else {
h5 = NULL;
grp = NULL;
}
if(h5p) *h5p = h5;
if(grpp) *grpp = grp;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/**
2017-12-05 03:21:14 +08:00
* @internal Find info for this file and group, and set pointer to each.
*
* @param ncid File and group ID.
* @param nc Pointer that gets a pointer to the file's NC struct.
* @param grpp Pointer that gets a pointer to the group struct.
* @param h5p Pointer that gets HDF5 file struct.
*
2017-12-03 22:57:21 +08:00
* @return ::NC_NOERR No error.
* @return ::NC_EBADID Bad ncid.
2017-12-05 03:21:14 +08:00
* @author Ed Hartnett
*/
2010-06-03 21:24:43 +08:00
int
nc4_find_nc_grp_h5(int ncid, NC **nc, NC_GRP_INFO_T **grpp,
NC_FILE_INFO_T **h5p)
2010-06-03 21:24:43 +08:00
{
2017-12-05 03:21:14 +08:00
NC_GRP_INFO_T *grp;
NC_FILE_INFO_T* h5;
2017-12-05 03:21:14 +08:00
NC *f = nc4_find_nc_file(ncid,&h5);
if(f == NULL) return NC_EBADID;
if(nc) *nc = f;
2017-12-05 03:21:14 +08:00
if (h5) {
assert(h5->root_grp);
/* If we can't find it, the grp id part of ncid is bad. */
if (!(grp = nc4_rec_find_grp(h5, (ncid & GRP_ID_MASK))))
2017-12-05 03:21:14 +08:00
return NC_EBADID;
h5 = (grp)->nc4_info;
assert(h5);
} else {
h5 = NULL;
grp = NULL;
}
if(h5p) *h5p = h5;
if(grpp) *grpp = grp;
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
/**
* @internal Use NC_FILE_INFO_T->allgroups to locate a group id.
*
* @param h5 Pointer to file info
2017-12-03 22:11:51 +08:00
* @param target_nc_grpid Group ID to be found.
*
2017-12-03 22:57:21 +08:00
* @return Pointer to group info struct, or NULL if not found.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
NC_GRP_INFO_T *
nc4_rec_find_grp(NC_FILE_INFO_T *h5, int target_nc_grpid)
2010-06-03 21:24:43 +08:00
{
NC_GRP_INFO_T *g;
2010-06-03 21:24:43 +08:00
assert(h5);
2010-06-03 21:24:43 +08:00
/* Is this the group we are searching for? */
g = nclistget(h5->allgroups,target_nc_grpid);
return g;
2010-06-03 21:24:43 +08:00
}
/**
* @internal Given an ncid and varid, get pointers to the group and var
2017-12-05 03:21:14 +08:00
* metadata.
*
* @param nc Pointer to file's NC struct.
* @param ncid File ID.
* @param varid Variable ID.
* @param grp Pointer that gets pointer to group info.
* @param var Pointer that gets pointer to var info.
*
2017-12-03 22:57:21 +08:00
* @return ::NC_NOERR No error.
*/
2010-06-03 21:24:43 +08:00
int
nc4_find_g_var_nc(NC *nc, int ncid, int varid,
2017-12-05 03:21:14 +08:00
NC_GRP_INFO_T **grp, NC_VAR_INFO_T **var)
2010-06-03 21:24:43 +08:00
{
NC_FILE_INFO_T* h5 = NC4_DATA(nc);
2010-06-03 21:24:43 +08:00
/* Find the group info. */
assert(grp && var && h5 && h5->root_grp);
*grp = nc4_rec_find_grp(h5, (ncid & GRP_ID_MASK));
2010-06-03 21:24:43 +08:00
/* It is possible for *grp to be NULL. If it is,
return an error. */
if(*grp == NULL)
2017-12-05 03:21:14 +08:00
return NC_EBADID;
2010-06-03 21:24:43 +08:00
/* Find the var info. */
(*var) = (NC_VAR_INFO_T*)ncindexith((*grp)->vars,varid);
if((*var) == NULL)
2018-08-08 05:17:40 +08:00
return NC_ENOTVAR;
2010-06-03 21:24:43 +08:00
return NC_NOERR;
}
/**
2017-12-05 03:21:14 +08:00
* @internal Find a dim in a grp (or its parents).
*
* @param grp Pointer to group info struct.
* @param dimid Dimension ID to find.
* @param dim Pointer that gets pointer to dim info if found.
2018-04-24 06:38:08 +08:00
* @param dim_grp Pointer that gets pointer to group info of group that contains dimension.
2017-12-05 03:21:14 +08:00
*
2017-12-03 22:57:21 +08:00
* @return ::NC_NOERR No error.
* @return ::NC_EBADDIM Dimension not found.
* @author Ed Hartnett
*/
2010-06-03 21:24:43 +08:00
int
nc4_find_dim(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T **dim,
2017-12-05 03:21:14 +08:00
NC_GRP_INFO_T **dim_grp)
2010-06-03 21:24:43 +08:00
{
NC_GRP_INFO_T *g;
int found = 0;
NC_FILE_INFO_T* h5 = grp->nc4_info;
assert(h5 && grp && dim);
2010-06-03 21:24:43 +08:00
/* Find the dim info. */
(*dim) = nclistget(h5->alldims,dimid);
if((*dim) == NULL)
2018-08-08 05:17:40 +08:00
return NC_EBADDIM;
2010-06-03 21:24:43 +08:00
/* Redundant: Verify that this dim is in fact in the group or its parent */
for (found=0, g = grp; g ; g = g->parent) {
2018-08-08 05:17:40 +08:00
if(g == (*dim)->container) {found = 1; break;}
}
2010-06-03 21:24:43 +08:00
/* If we didn't find it, return an error. */
assert(found);
2010-06-03 21:24:43 +08:00
/* Give the caller the group the dimension is in. */
if (dim_grp)
*dim_grp = (*dim)->container;
2010-06-03 21:24:43 +08:00
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
2017-12-05 03:21:14 +08:00
* @internal Find a var (by name) in a grp.
*
* @param grp Pointer to group info.
* @param name Name of var to find.
* @param var Pointer that gets pointer to var info struct, if found.
2017-12-03 22:57:21 +08:00
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
int
nc4_find_var(NC_GRP_INFO_T *grp, const char *name, NC_VAR_INFO_T **var)
{
assert(grp && var && name);
/* Find the var info. */
*var = (NC_VAR_INFO_T*)ncindexlookup(grp->vars,name);
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
* @internal Locate netCDF type by name.
2017-12-03 22:57:21 +08:00
*
2017-12-05 03:21:14 +08:00
* @param start_grp Pointer to starting group info.
* @param name Name of type to find.
*
* @return Pointer to type info, or NULL if not found.
2017-12-03 22:57:21 +08:00
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
NC_TYPE_INFO_T *
nc4_rec_find_named_type(NC_GRP_INFO_T *start_grp, char *name)
2010-06-03 21:24:43 +08:00
{
NC_GRP_INFO_T *g;
NC_TYPE_INFO_T *type, *res;
int i;
2010-06-03 21:24:43 +08:00
assert(start_grp);
2010-06-03 21:24:43 +08:00
/* Does this group have the type we are searching for? */
type = (NC_TYPE_INFO_T*)ncindexlookup(start_grp->type,name);
if(type != NULL)
2018-08-08 05:17:40 +08:00
return type;
2010-06-03 21:24:43 +08:00
/* Search subgroups. */
for(i=0;i<ncindexsize(start_grp->children);i++) {
g = (NC_GRP_INFO_T*)ncindexith(start_grp->children,i);
if(g == NULL) continue;
if ((res = nc4_rec_find_named_type(g, name)))
2018-08-08 05:17:40 +08:00
return res;
}
/* Can't find it. Oh, woe is me! */
2010-06-03 21:24:43 +08:00
return NULL;
}
2017-12-03 22:57:21 +08:00
/**
2018-01-31 23:44:33 +08:00
* @internal Recursively hunt for a netCDF type id.
* Note using h5->alltypes, we no longer do recursion
2017-12-03 22:57:21 +08:00
*
* @param h5 the root file
2017-12-05 03:21:14 +08:00
* @param target_nc_typeid NetCDF type ID to find.
*
* @return Pointer to type info, or NULL if not found.
2017-12-03 22:57:21 +08:00
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
NC_TYPE_INFO_T *
nc4_rec_find_nc_type(NC_FILE_INFO_T *h5, nc_type target_nc_typeid)
2010-06-03 21:24:43 +08:00
{
assert(h5);
return nclistget(h5->alltypes, target_nc_typeid);
2010-06-03 21:24:43 +08:00
}
2017-12-03 22:57:21 +08:00
/**
* @internal Use a netCDF typeid to find a type in a type_list.
*
* @param h5 Pointer to HDF5 file info struct.
* @param typeid The netCDF type ID.
* @param type Pointer to pointer to the list of type info structs.
*
* @return ::NC_NOERR No error.
* @return ::NC_EINVAL Invalid input.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_find_type(const NC_FILE_INFO_T *h5, nc_type typeid, NC_TYPE_INFO_T **type)
2010-06-03 21:24:43 +08:00
{
if (typeid < 0 || !type)
return NC_EINVAL;
*type = NULL;
/* Atomic types don't have associated NC_TYPE_INFO_T struct, just
* return NOERR. */
if (typeid <= NC_STRING)
return NC_NOERR;
/* Find the type. */
*type = (NC_TYPE_INFO_T*)nclistget(h5->alltypes,typeid);
if((*type) == NULL)
2010-06-03 21:24:43 +08:00
return NC_EBADTYPID;
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
* @internal Given a group, find an att. If name is provided, use that,
* otherwise use the attnum.
2017-12-03 22:57:21 +08:00
*
* @param grp Pointer to group info struct.
* @param varid Variable ID.
* @param name Name to of attribute.
* @param attnum Number of attribute.
* @param att Pointer to pointer that gets attribute info struct.
*
* @return ::NC_NOERR No error.
* @return ::NC_ENOTVAR Variable not found.
* @return ::NC_ENOTATT Attribute not found.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_find_grp_att(NC_GRP_INFO_T *grp, int varid, const char *name, int attnum,
2017-12-05 03:21:14 +08:00
NC_ATT_INFO_T **att)
2010-06-03 21:24:43 +08:00
{
NC_VAR_INFO_T *var;
NCindex* attlist = NULL;
2018-06-19 21:39:42 +08:00
int retval;
2010-06-03 21:24:43 +08:00
assert(grp && grp->hdr.name);
LOG((4, "nc4_find_grp_att: grp->name %s varid %d name %s attnum %d",
grp->hdr.name, varid, name, attnum));
2010-06-03 21:24:43 +08:00
/* Get either the global or a variable attribute list. */
if (varid == NC_GLOBAL)
2018-06-19 21:39:42 +08:00
{
2010-06-03 21:24:43 +08:00
attlist = grp->att;
2018-06-19 21:39:42 +08:00
/* Do we need to read the atts? */
if (grp->atts_not_read)
2018-07-22 00:43:36 +08:00
if ((retval = nc4_read_atts(grp, NULL)))
2018-06-19 21:39:42 +08:00
return retval;
}
2010-06-03 21:24:43 +08:00
else
{
var = (NC_VAR_INFO_T*)ncindexith(grp->vars,varid);
if (!var) return NC_ENOTVAR;
/* Do we need to read the var attributes? */
if (var->atts_not_read)
2018-07-22 00:43:36 +08:00
if ((retval = nc4_read_atts(grp, var)))
return retval;
attlist = var->att;
assert(var->hdr.id == varid);
2010-06-03 21:24:43 +08:00
}
/* Now find the attribute by name or number. If a name is provided,
* ignore the attnum. */
if(attlist) {
NC_ATT_INFO_T* a;
if(name != NULL)
2018-08-08 05:17:40 +08:00
a = (NC_ATT_INFO_T*)ncindexlookup(attlist,name);
else
2018-08-08 05:17:40 +08:00
a = (NC_ATT_INFO_T*)ncindexith(attlist,attnum);
if(a != NULL) {
2018-08-08 05:17:40 +08:00
*att = a;
return NC_NOERR;
2017-12-05 03:21:14 +08:00
}
}
2010-06-03 21:24:43 +08:00
/* If we get here, we couldn't find the attribute. */
return NC_ENOTATT;
}
2017-12-03 22:57:21 +08:00
/**
* @internal Given an ncid, varid, and name or attnum, find and return
* pointer to NC_ATT_INFO_T metadata.
*
* @param ncid File and group ID.
* @param varid Variable ID.
* @param name Name to of attribute.
* @param attnum Number of attribute.
* @param att Pointer to pointer that gets attribute info struct.
2017-12-03 22:57:21 +08:00
*
* @return ::NC_NOERR No error.
* @return ::NC_ENOTVAR Variable not found.
* @return ::NC_ENOTATT Attribute not found.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_find_nc_att(int ncid, int varid, const char *name, int attnum,
2017-12-05 03:21:14 +08:00
NC_ATT_INFO_T **att)
2010-06-03 21:24:43 +08:00
{
NC_GRP_INFO_T *grp;
NC_FILE_INFO_T *h5;
2010-06-03 21:24:43 +08:00
int retval;
LOG((4, "nc4_find_nc_att: ncid 0x%x varid %d name %s attnum %d",
2017-12-05 03:21:14 +08:00
ncid, varid, name, attnum));
2010-06-03 21:24:43 +08:00
/* Find info for this file and group, and set pointer to each. */
if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
return retval;
assert(grp && h5);
return nc4_find_grp_att(grp,varid,name,attnum,att);
2010-06-03 21:24:43 +08:00
}
2017-12-03 22:57:21 +08:00
/**
* @internal Given an id, walk the list and find the appropriate NC.
*
* @param ext_ncid File/group ID to find.
* @param h5p Pointer to pointer that gets the HDF5 file info struct.
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett, Dennis Heimbigner
2017-12-05 03:21:14 +08:00
*/
NC*
nc4_find_nc_file(int ext_ncid, NC_FILE_INFO_T** h5p)
2010-06-03 21:24:43 +08:00
{
NC* nc;
int stat;
stat = NC_check_id(ext_ncid,&nc);
if(stat != NC_NOERR)
2017-12-05 03:21:14 +08:00
nc = NULL;
if(nc)
if(h5p) *h5p = (NC_FILE_INFO_T*)nc->dispatchdata;
return nc;
2010-06-03 21:24:43 +08:00
}
/**
* @internal Add NC_OBJ to allXXX lists in a file
*
* @param file Pointer to the containing file
* @param obj Pointer to object to add.
*
* @author Dennis Heimbigner
*/
static void
obj_track(NC_FILE_INFO_T* file, NC_OBJ* obj)
{
2018-08-08 05:17:40 +08:00
NClist* list = NULL;
/* record the object in the file */
switch (obj->sort) {
case NCDIM: list = file->alldims; break;
case NCTYP: list = file->alltypes; break;
case NCGRP: list = file->allgroups; break;
default:
assert(NC_FALSE);
}
/* Insert at the appropriate point in the list */
nclistset(list,obj->id,obj);
}
2010-06-03 21:24:43 +08:00
2017-12-03 22:57:21 +08:00
/**
2018-07-29 18:57:25 +08:00
* @internal Create a new variable and insert into relevant
* lists. Dimensionality info need not be known.
*
* @param grp the containing group
* @param name the name for the new variable
* @param var Pointer in which to return a pointer to the new var.
2017-12-03 22:57:21 +08:00
*
* @param var Pointer to pointer that gets variable info struct.
*
* @return ::NC_NOERR No error.
* @return ::NC_ENOMEM Out of memory.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
2018-07-29 18:57:25 +08:00
nc4_var_list_add2(NC_GRP_INFO_T *grp, const char *name, NC_VAR_INFO_T **var)
2010-06-03 21:24:43 +08:00
{
2018-07-29 18:57:25 +08:00
NC_VAR_INFO_T *new_var;
/* Allocate storage for new variable. */
if (!(new_var = calloc(1, sizeof(NC_VAR_INFO_T))))
2018-07-29 18:57:25 +08:00
return NC_ENOMEM;
new_var->hdr.sort = NCVAR;
/* These are the HDF5-1.8.4 defaults. */
new_var->chunk_cache_size = nc4_chunk_cache_size;
new_var->chunk_cache_nelems = nc4_chunk_cache_nelems;
new_var->chunk_cache_preemption = nc4_chunk_cache_preemption;
/* Now fill in the values in the var info structure. */
new_var->hdr.id = ncindexsize(grp->vars);
if (!(new_var->hdr.name = strdup(name)))
2018-07-29 18:57:25 +08:00
return NC_ENOMEM;
new_var->hdr.hashkey = NC_hashmapkey(new_var->hdr.name,
strlen(new_var->hdr.name));
2018-07-29 18:57:25 +08:00
/* Create an indexed list for the attributes. */
new_var->att = ncindexnew(0);
/* Officially track it */
2018-07-29 18:57:25 +08:00
ncindexadd(grp->vars, (NC_OBJ *)new_var);
/* Set the var pointer, if one was given */
if (var)
*var = new_var;
2018-07-29 18:57:25 +08:00
return NC_NOERR;
}
/**
* @internal Set the number of dims in an NC_VAR_INFO_T struct.
*
* @param var Pointer to the var.
* @param ndims Number of dimensions for this var.
*
* @param var Pointer to pointer that gets variable info struct.
*
* @return ::NC_NOERR No error.
* @return ::NC_ENOMEM Out of memory.
* @author Ed Hartnett
*/
int
nc4_var_set_ndims(NC_VAR_INFO_T *var, int ndims)
{
/* Remember the number of dimensions. */
var->ndims = ndims;
/* Allocate space for dimension information. */
if (ndims)
{
if (!(var->dim = calloc(ndims, sizeof(NC_DIM_INFO_T *))))
return NC_ENOMEM;
if (!(var->dimids = calloc(ndims, sizeof(int))))
return NC_ENOMEM;
/* Initialize dimids to illegal values (-1). See the comment
in nc4hdf.c#nc4_rec_match_dimscales. */
memset(var->dimids, -1, ndims * sizeof(int));
}
2018-07-29 18:57:25 +08:00
return NC_NOERR;
}
/**
* @internal Create a new variable and insert int relevant list.
*
* @param grp the containing group
* @param name the name for the new variable
* @param ndims the rank of the new variable
* @param var Pointer in which to return a pointer to the new var.
*
* @param var Pointer to pointer that gets variable info struct.
*
* @return ::NC_NOERR No error.
* @return ::NC_ENOMEM Out of memory.
* @author Ed Hartnett
*/
int
nc4_var_list_add(NC_GRP_INFO_T* grp, const char* name, int ndims,
NC_VAR_INFO_T **var)
{
int retval;
if ((retval = nc4_var_list_add2(grp, name, var)))
return retval;
if ((retval = nc4_var_set_ndims(*var, ndims)))
return retval;
2018-07-29 18:57:25 +08:00
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
2017-12-03 22:57:21 +08:00
/**
2018-07-29 18:57:25 +08:00
* @internal Add a dimension to the dimension list for a group.
2017-12-03 22:57:21 +08:00
*
* @param grp container for the dim
* @param name for the dim
* @param len for the dim
* @param assignedid override dimid if >= 0
2017-12-03 22:57:21 +08:00
* @param dim Pointer to pointer that gets the new dim info struct.
*
* @return ::NC_NOERR No error.
2018-07-29 18:57:25 +08:00
* @return ::NC_ENOMEM Out of memory.
2017-12-03 22:57:21 +08:00
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
2018-07-29 18:57:25 +08:00
nc4_dim_list_add(NC_GRP_INFO_T *grp, const char *name, size_t len,
int assignedid, NC_DIM_INFO_T **dim)
2010-06-03 21:24:43 +08:00
{
2018-07-29 18:57:25 +08:00
NC_DIM_INFO_T *new_dim;
assert(grp && name);
2018-07-29 18:57:25 +08:00
/* Allocate memory for dim metadata. */
if (!(new_dim = calloc(1, sizeof(NC_DIM_INFO_T))))
2018-07-29 18:57:25 +08:00
return NC_ENOMEM;
new_dim->hdr.sort = NCDIM;
2010-06-03 21:24:43 +08:00
2018-07-29 18:57:25 +08:00
/* Assign the dimension ID. */
if (assignedid >= 0)
new_dim->hdr.id = assignedid;
else
2018-07-29 18:57:25 +08:00
new_dim->hdr.id = grp->nc4_info->next_dimid++;
2018-07-29 18:57:25 +08:00
/* Remember the name and create a hash. */
if (!(new_dim->hdr.name = strdup(name)))
2018-07-29 18:57:25 +08:00
return NC_ENOMEM;
new_dim->hdr.hashkey = NC_hashmapkey(new_dim->hdr.name,
strlen(new_dim->hdr.name));
/* Is dimension unlimited? */
new_dim->len = len;
if (len == NC_UNLIMITED)
new_dim->unlimited = NC_TRUE;
2018-07-29 18:57:25 +08:00
/* Remember the containing group. */
new_dim->container = grp;
2018-07-29 18:57:25 +08:00
/* Add object to dimension list for this group. */
ncindexadd(grp->dim, (NC_OBJ *)new_dim);
2018-07-29 18:57:25 +08:00
obj_track(grp->nc4_info, (NC_OBJ *)new_dim);
/* Set the dim pointer, if one was given */
if (dim)
*dim = new_dim;
2018-07-29 18:57:25 +08:00
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
2017-12-03 22:57:21 +08:00
/**
* @internal Add to the end of an att list.
*
* @param list NCindex of att info structs.
* @param name name of the new attribute
2017-12-03 22:57:21 +08:00
* @param att Pointer to pointer that gets the new att info struct.
*
* @return ::NC_NOERR No error.
* @return ::NC_ENOMEM Out of memory.
2017-12-03 22:57:21 +08:00
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_att_list_add(NCindex *list, const char *name, NC_ATT_INFO_T **att)
2010-06-03 21:24:43 +08:00
{
NC_ATT_INFO_T *new_att;
LOG((3, "%s: name %s ", __func__, name));
if (!(new_att = calloc(1, sizeof(NC_ATT_INFO_T))))
2010-06-03 21:24:43 +08:00
return NC_ENOMEM;
new_att->hdr.sort = NCATT;
/* Fill in the information we know. */
new_att->hdr.id = ncindexsize(list);
if (!(new_att->hdr.name = strdup(name)))
return NC_ENOMEM;
/* Create a hash of the name. */
new_att->hdr.hashkey = NC_hashmapkey(name, strlen(name));
/* Add object to list as specified by its number */
ncindexadd(list, (NC_OBJ *)new_att);
/* Set the attribute pointer, if one was given */
if (att)
*att = new_att;
2010-06-03 21:24:43 +08:00
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
2017-12-03 22:57:21 +08:00
/**
* @internal Add a group to a group list.
2017-12-03 22:57:21 +08:00
*
* @param h5 Pointer to the file info.
* @param parent Pointer to the parent group. Will be NULL when adding
* the root group.
2017-12-03 22:57:21 +08:00
* @param name Name of the group.
* @param grp Pointer to pointer that gets new group info struct.
*
* @return ::NC_NOERR No error.
* @return ::NC_ENOMEM Out of memory.
* @author Ed Hartnett, Dennis Heimbigner
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_grp_list_add(NC_FILE_INFO_T *h5, NC_GRP_INFO_T *parent, char *name,
NC_GRP_INFO_T **grp)
2010-06-03 21:24:43 +08:00
{
NC_GRP_INFO_T *new_grp;
2010-06-03 21:24:43 +08:00
/* Check inputs. */
assert(h5 && name);
LOG((3, "%s: name %s ", __func__, name));
2010-06-03 21:24:43 +08:00
/* Get the memory to store this groups info. */
if (!(new_grp = calloc(1, sizeof(NC_GRP_INFO_T))))
2010-06-03 21:24:43 +08:00
return NC_ENOMEM;
/* Fill in this group's information. */
new_grp->hdr.sort = NCGRP;
new_grp->nc4_info = h5;
new_grp->parent = parent;
/* Assign the group ID. The root group will get id 0. */
new_grp->hdr.id = h5->next_nc_grpid++;
assert(parent || !new_grp->hdr.id);
/* Handle the group name. */
if (!(new_grp->hdr.name = strdup(name)))
2010-06-03 21:24:43 +08:00
{
free(new_grp);
return NC_ENOMEM;
2010-06-03 21:24:43 +08:00
}
new_grp->hdr.hashkey = NC_hashmapkey(new_grp->hdr.name,
strlen(new_grp->hdr.name));
2010-06-03 21:24:43 +08:00
/* Set up new indexed lists for stuff this group can contain. */
new_grp->children = ncindexnew(0);
new_grp->dim = ncindexnew(0);
new_grp->att = ncindexnew(0);
new_grp->type = ncindexnew(0);
new_grp->vars = ncindexnew(0);
/* Add object to lists */
if (parent)
ncindexadd(parent->children, (NC_OBJ *)new_grp);
obj_track(h5, (NC_OBJ *)new_grp);
/* Set the group pointer, if one was given */
if (grp)
2017-12-05 03:21:14 +08:00
*grp = new_grp;
2010-06-03 21:24:43 +08:00
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
* @internal Names for groups, variables, and types must not be the
* same. This function checks that a proposed name is not already in
2010-06-03 21:24:43 +08:00
* use. Normalzation of UTF8 strings should happen before this
2017-12-03 22:57:21 +08:00
* function is called.
*
* @param grp Pointer to group info struct.
* @param name Name to check.
*
* @return ::NC_NOERR No error.
* @return ::NC_ENAMEINUSE Name is in use.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_check_dup_name(NC_GRP_INFO_T *grp, char *name)
{
NC_TYPE_INFO_T *type;
NC_GRP_INFO_T *g;
NC_VAR_INFO_T *var;
2010-06-03 21:24:43 +08:00
/* Any types of this name? */
type = (NC_TYPE_INFO_T*)ncindexlookup(grp->type,name);
if(type != NULL)
2018-08-08 05:17:40 +08:00
return NC_ENAMEINUSE;
2010-06-03 21:24:43 +08:00
/* Any child groups of this name? */
g = (NC_GRP_INFO_T*)ncindexlookup(grp->children,name);
if(g != NULL)
2018-08-08 05:17:40 +08:00
return NC_ENAMEINUSE;
2010-06-03 21:24:43 +08:00
/* Any variables of this name? */
var = (NC_VAR_INFO_T*)ncindexlookup(grp->vars,name);
if(var != NULL)
2018-08-08 05:17:40 +08:00
return NC_ENAMEINUSE;
2010-06-03 21:24:43 +08:00
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
* @internal Create a type, but do not add to various lists nor
* increment its ref count
2017-12-03 22:57:21 +08:00
*
* @param grp Pointer to group info struct.
* @param size Size of type in bytes.
* @param name Name of type.
* @param assignedid if >= 0 then override the default type id.
* @param type Pointer that gets pointer to new type info struct.
2017-12-03 22:57:21 +08:00
*
* @return ::NC_NOERR No error.
* @return ::NC_ENOMEM Out of memory.
* @author Ed Hartnett, Ward Fisher
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_type_new(NC_GRP_INFO_T *grp, size_t size, const char *name, int assignedid,
NC_TYPE_INFO_T **type)
2010-06-03 21:24:43 +08:00
{
NC_TYPE_INFO_T *new_type;
/* Check inputs. */
assert(type);
2010-06-03 21:24:43 +08:00
2018-08-08 05:17:40 +08:00
/* Allocate memory for the type */
if (!(new_type = calloc(1, sizeof(NC_TYPE_INFO_T))))
return NC_ENOMEM;
new_type->hdr.sort = NCTYP;
2010-06-03 21:24:43 +08:00
2018-08-08 05:17:40 +08:00
/* Remember info about this type. */
new_type->hdr.id = assignedid;
new_type->size = size;
if (!(new_type->hdr.name = strdup(name))) {
free(new_type);
return NC_ENOMEM;
}
new_type->hdr.hashkey = NC_hashmapkey(name, strlen(name));
/* Return a pointer to the new type. */
*type = new_type;
2018-08-08 05:17:40 +08:00
return NC_NOERR;
}
/**
* @internal Add to the end of a type list.
*
* @param grp Pointer to group info struct.
* @param size Size of type in bytes.
* @param name Name of type.
* @param type Pointer that gets pointer to new type info
* struct.
*
* @return ::NC_NOERR No error.
* @return ::NC_ENOMEM Out of memory.
* @author Ed Hartnett
*/
int
nc4_type_list_add(NC_GRP_INFO_T *grp, size_t size, const char *name,
NC_TYPE_INFO_T **type)
{
NC_TYPE_INFO_T *new_type;
int retval;
/* Check inputs. */
assert(type);
/* Create the new TYPE_INFO struct. */
if ((retval = nc4_type_new(grp, size, name, grp->nc4_info->next_typeid,
&new_type)))
return retval;
grp->nc4_info->next_typeid++;
/* Increment the ref. count on the type */
new_type->rc++;
/* Add object to lists */
ncindexadd(grp->type, (NC_OBJ *)new_type);
obj_track(grp->nc4_info,(NC_OBJ*)new_type);
/* Return a pointer to the new type. */
*type = new_type;
2010-06-03 21:24:43 +08:00
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
2017-12-05 03:21:14 +08:00
* @internal Add to the end of a compound field list.
2017-12-03 22:57:21 +08:00
*
* @param parent parent type
2017-12-03 22:57:21 +08:00
* @param name Name of the field.
* @param offset Offset in bytes.
* @param field_hdf_typeid The HDF5 type ID of the field.
* @param native_typeid The HDF5 native type ID of the field.
* @param xtype The netCDF type of the field.
* @param ndims The number of dimensions of the field.
* @param dim_sizesp An array of dim sizes for the field.
2017-12-05 03:21:14 +08:00
*
2017-12-03 22:57:21 +08:00
* @return ::NC_NOERR No error.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_field_list_add(NC_TYPE_INFO_T *parent, const char *name,
2017-12-05 03:21:14 +08:00
size_t offset, hid_t field_hdf_typeid, hid_t native_typeid,
nc_type xtype, int ndims, const int *dim_sizesp)
2010-06-03 21:24:43 +08:00
{
NC_FIELD_INFO_T *field;
2010-06-03 21:24:43 +08:00
/* Name has already been checked and UTF8 normalized. */
if (!name)
return NC_EINVAL;
/* Allocate storage for this field information. */
if (!(field = calloc(1, sizeof(NC_FIELD_INFO_T))))
return NC_ENOMEM;
field->hdr.sort = NCFLD;
2010-06-03 21:24:43 +08:00
/* Store the information about this field. */
if (!(field->hdr.name = strdup(name)))
{
free(field);
return NC_ENOMEM;
}
field->hdr.hashkey = NC_hashmapkey(field->hdr.name,strlen(field->hdr.name));
2010-06-03 21:24:43 +08:00
field->hdf_typeid = field_hdf_typeid;
field->native_hdf_typeid = native_typeid;
field->nc_typeid = xtype;
2010-06-03 21:24:43 +08:00
field->offset = offset;
field->ndims = ndims;
if (ndims)
{
int i;
if (!(field->dim_size = malloc(ndims * sizeof(int))))
{
free(field->hdr.name);
free(field);
2017-12-05 03:21:14 +08:00
return NC_ENOMEM;
}
for (i = 0; i < ndims; i++)
2017-12-05 03:21:14 +08:00
field->dim_size[i] = dim_sizesp[i];
}
2010-06-03 21:24:43 +08:00
/* Add object to lists */
field->hdr.id = nclistlength(parent->u.c.field);
nclistpush(parent->u.c.field,field);
2010-06-03 21:24:43 +08:00
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
2017-12-05 03:21:14 +08:00
* @internal Add a member to an enum type.
2017-12-03 22:57:21 +08:00
*
* @param parent Containing NC_TYPE_INFO_T object
2017-12-03 22:57:21 +08:00
* @param size Size in bytes of new member.
* @param name Name of the member.
* @param value Value to associate with member.
*
* @return ::NC_NOERR No error.
* @return ::NC_ENOMEM Out of memory.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_enum_member_add(NC_TYPE_INFO_T *parent, size_t size,
2017-12-05 03:21:14 +08:00
const char *name, const void *value)
2010-06-03 21:24:43 +08:00
{
NC_ENUM_MEMBER_INFO_T *member;
2010-06-03 21:24:43 +08:00
/* Name has already been checked. */
assert(name && size > 0 && value);
LOG((4, "%s: size %d name %s", __func__, size, name));
2010-06-03 21:24:43 +08:00
/* Allocate storage for this field information. */
2013-08-18 01:53:29 +08:00
if (!(member = calloc(1, sizeof(NC_ENUM_MEMBER_INFO_T))))
2010-06-03 21:24:43 +08:00
return NC_ENOMEM;
if (!(member->value = malloc(size))) {
2013-08-18 01:53:29 +08:00
free(member);
return NC_ENOMEM;
}
if (!(member->name = strdup(name))) {
2013-08-18 01:53:29 +08:00
free(member->value);
free(member);
return NC_ENOMEM;
}
2010-06-03 21:24:43 +08:00
/* Store the value for this member. */
2010-06-03 21:24:43 +08:00
memcpy(member->value, value, size);
/* Add object to list */
nclistpush(parent->u.e.enum_member,member);
2010-06-03 21:24:43 +08:00
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
* @internal Free up a field
2017-12-03 22:57:21 +08:00
*
* @param field Pointer to field info of field to delete.
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
static void
field_free(NC_FIELD_INFO_T *field)
{
/* Free some stuff. */
if (field->hdr.name)
free(field->hdr.name);
if (field->dim_size)
free(field->dim_size);
/* Nc_Free the memory. */
free(field);
}
2017-12-03 22:57:21 +08:00
/**
2017-12-05 03:21:14 +08:00
* @internal Free allocated space for type information.
2017-12-03 22:57:21 +08:00
*
* @param type Pointer to type info struct.
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
int
nc4_type_free(NC_TYPE_INFO_T *type)
{
int i;
/* Decrement the ref. count on the type */
assert(type->rc);
type->rc--;
/* Release the type, if the ref. count drops to zero */
if (0 == type->rc)
{
/* Close any open user-defined HDF5 typeids. */
if (type->hdf_typeid && H5Tclose(type->hdf_typeid) < 0)
return NC_EHDFERR;
if (type->native_hdf_typeid && H5Tclose(type->native_hdf_typeid) < 0)
return NC_EHDFERR;
/* Free the name. */
if (type->hdr.name)
free(type->hdr.name);
/* Class-specific cleanup */
switch (type->nc_type_class)
{
2017-12-05 03:21:14 +08:00
case NC_COMPOUND:
{
NC_FIELD_INFO_T *field;
2017-12-05 03:21:14 +08:00
/* Delete all the fields in this type (there will be some if its a
* compound). */
2018-08-08 05:17:40 +08:00
for(i=0;i<nclistlength(type->u.c.field);i++) {
field = nclistget(type->u.c.field,i);
field_free(field);
2017-12-05 03:21:14 +08:00
}
2018-08-08 05:17:40 +08:00
nclistfree(type->u.c.field);
type->u.c.field = NULL; /* belt and suspenders */
2017-12-05 03:21:14 +08:00
}
break;
2017-12-05 03:21:14 +08:00
case NC_ENUM:
{
NC_ENUM_MEMBER_INFO_T *enum_member;
/* Delete all the enum_members, if any. */
2018-08-08 05:17:40 +08:00
for(i=0;i<nclistlength(type->u.e.enum_member);i++) {
enum_member = nclistget(type->u.e.enum_member,i);
2017-12-05 03:21:14 +08:00
free(enum_member->value);
free(enum_member->name);
free(enum_member);
}
2018-08-08 05:17:40 +08:00
nclistfree(type->u.e.enum_member);
type->u.e.enum_member = NULL; /* belt and suspenders */
2017-12-05 03:21:14 +08:00
if (H5Tclose(type->u.e.base_hdf_typeid) < 0)
return NC_EHDFERR;
}
break;
case NC_VLEN:
if (H5Tclose(type->u.v.base_hdf_typeid) < 0)
return NC_EHDFERR;
default:
break;
}
/* Release the memory. */
free(type);
}
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
2017-12-05 03:21:14 +08:00
* @internal Delete a var, and free the memory.
2017-12-03 22:57:21 +08:00
*
* @param var Pointer to the var info struct of var to delete.
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
int
nc4_var_free(NC_VAR_INFO_T *var)
2010-06-03 21:24:43 +08:00
{
NC_ATT_INFO_T *att;
int ret = NC_NOERR;
int i;
2010-06-03 21:24:43 +08:00
if(var == NULL)
2017-12-05 03:21:14 +08:00
return NC_NOERR;
2010-06-03 21:24:43 +08:00
/* First delete all the attributes attached to this var. */
for(i=0;i<ncindexsize(var->att);i++) {
2018-07-29 18:57:25 +08:00
att = (NC_ATT_INFO_T*)ncindexith(var->att,i);
if(att == NULL) continue; /* might be a gap */
if ((ret = nc4_att_free(att)))
2017-12-05 03:21:14 +08:00
return ret;
2010-06-03 21:24:43 +08:00
}
ncindexfree(var->att);
var->att = NULL;
2010-06-03 21:24:43 +08:00
/* Free some things that may be allocated. */
if (var->chunksizes)
2017-12-05 03:21:14 +08:00
{free(var->chunksizes);var->chunksizes = NULL;}
if (var->hdf5_name)
2017-12-05 03:21:14 +08:00
{free(var->hdf5_name); var->hdf5_name = NULL;}
if (var->hdr.name)
{free(var->hdr.name); var->hdr.name = NULL;}
if (var->dimids)
2017-12-05 03:21:14 +08:00
{free(var->dimids); var->dimids = NULL;}
if (var->dim)
2017-12-05 03:21:14 +08:00
{free(var->dim); var->dim = NULL;}
2010-06-03 21:24:43 +08:00
/* Delete any fill value allocation. This must be done before the
* type_info is freed. */
if (var->fill_value)
{
2010-11-30 06:23:16 +08:00
if (var->hdf_datasetid)
{
if (var->type_info)
{
if (var->type_info->nc_type_class == NC_VLEN)
nc_free_vlen((nc_vlen_t *)var->fill_value);
else if (var->type_info->nc_type_class == NC_STRING && *(char **)var->fill_value)
free(*(char **)var->fill_value);
}
2010-11-30 06:23:16 +08:00
}
2010-06-03 21:24:43 +08:00
free(var->fill_value);
var->fill_value = NULL;
2010-06-03 21:24:43 +08:00
}
/* Release type information */
if (var->type_info)
2010-06-03 21:24:43 +08:00
{
int retval;
if ((retval = nc4_type_free(var->type_info)))
2017-12-05 03:21:14 +08:00
return retval;
var->type_info = NULL;
2010-06-03 21:24:43 +08:00
}
2010-06-03 21:24:43 +08:00
/* Delete any HDF5 dimscale objid information. */
if (var->dimscale_hdf5_objids)
free(var->dimscale_hdf5_objids);
/* Delete information about the attachment status of dimscales. */
if (var->dimscale_attached)
free(var->dimscale_attached);
2017-11-15 00:15:13 +08:00
/* Release parameter information. */
if (var->params)
free(var->params);
2010-06-03 21:24:43 +08:00
/* Delete the var. */
free(var);
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
* @internal Delete a var, and free the memory.
2017-12-03 22:57:21 +08:00
*
* @param grp the containing group
* @param var Pointer to the var info struct of var to delete.
2017-12-03 22:57:21 +08:00
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
int
nc4_var_list_del(NC_GRP_INFO_T* grp, NC_VAR_INFO_T *var)
2010-06-03 21:24:43 +08:00
{
int i;
if(var == NULL)
return NC_NOERR;
/* Remove from lists */
if(grp) {
i = ncindexfind(grp->vars,(NC_OBJ*)var);
if(i >= 0)
ncindexidel(grp->vars, i);
}
return nc4_var_free(var);
2010-06-03 21:24:43 +08:00
}
2017-12-03 22:57:21 +08:00
/**
* @internal Free a dim
2017-12-03 22:57:21 +08:00
*
* @param dim Pointer to dim info struct of type to delete.
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett, Ward Fisher
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_dim_free(NC_DIM_INFO_T *dim)
2010-06-03 21:24:43 +08:00
{
/* Free memory allocated for names. */
2018-08-08 05:17:40 +08:00
if(dim) {
if (dim->hdr.name)
free(dim->hdr.name);
2018-08-08 05:17:40 +08:00
free(dim);
}
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
2017-12-03 22:57:21 +08:00
/**
* @internal Free a dim and unlist it
2017-12-03 22:57:21 +08:00
*
* @param grp Pointer to dim's containing group
* @param dim Pointer to dim info struct of type to delete.
2017-12-03 22:57:21 +08:00
*
* @return ::NC_NOERR No error.
* @author Dennis Heimbigner
2017-12-05 03:21:14 +08:00
*/
int
nc4_dim_list_del(NC_GRP_INFO_T* grp, NC_DIM_INFO_T *dim)
2010-06-03 21:24:43 +08:00
{
if(grp && dim) {
2018-08-08 05:17:40 +08:00
int pos = ncindexfind(grp->dim,(NC_OBJ*)dim);
if(pos >= 0)
ncindexidel(grp->dim,pos);
}
return nc4_dim_free(dim);
2010-06-03 21:24:43 +08:00
}
2017-12-03 22:57:21 +08:00
/**
* @internal Recursively delete the data for a group (and everything
* it contains) in our internal metadata store.
*
* @param grp Pointer to group info struct.
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
int
nc4_rec_grp_del(NC_GRP_INFO_T *grp)
2010-06-03 21:24:43 +08:00
{
NC_GRP_INFO_T *g;
NC_VAR_INFO_T *var;
NC_ATT_INFO_T *att;
NC_DIM_INFO_T *dim;
2010-06-03 21:24:43 +08:00
int retval;
int i;
2016-08-24 23:36:54 +08:00
2010-06-03 21:24:43 +08:00
assert(grp);
LOG((3, "%s: grp->name %s", __func__, grp->hdr.name));
/* WARNING: for all these deletes, the nc4_xxx_del
functions will modify the index, so we need to
not assume any state about them.
*/
2010-06-03 21:24:43 +08:00
/* Recursively call this function for each child, if any, stopping
* if there is an error. */
for(i=0;i<ncindexsize(grp->children);i++) {
2018-08-08 05:17:40 +08:00
g = (NC_GRP_INFO_T*)ncindexith(grp->children,i);
if(g == NULL) continue;
if ((retval = nc4_rec_grp_del(g)))
2017-12-05 03:21:14 +08:00
return retval;
2010-06-03 21:24:43 +08:00
}
ncindexfree(grp->children);
grp->children = NULL;
2010-06-03 21:24:43 +08:00
/* Delete all the list contents for vars, dims, and atts, in this
2010-06-03 21:24:43 +08:00
* group. */
for(i=0;i<ncindexsize(grp->att);i++) {
2018-08-08 05:17:40 +08:00
att = (NC_ATT_INFO_T*)ncindexith(grp->att,i);
if(att == NULL) continue;
LOG((4, "%s: deleting att %s", __func__, att->hdr.name));
if ((retval = nc4_att_free(att))) /* free but leave in parent list */
2017-12-05 03:21:14 +08:00
return retval;
2010-06-03 21:24:43 +08:00
}
ncindexfree(grp->att);
grp->att = NULL;
2010-06-03 21:24:43 +08:00
/* Delete all vars. */
for(i=0;i<ncindexsize(grp->vars);i++) {
var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
if(var == NULL) continue;
LOG((4, "%s: deleting var %s", __func__, var->hdr.name));
2010-06-03 21:24:43 +08:00
/* Close HDF5 dataset associated with this var, unless it's a
* scale. */
if (var->hdf_datasetid)
{
LOG((3, "closing dataset %lld", var->hdf_datasetid));
if (H5Dclose(var->hdf_datasetid) < 0)
return NC_EHDFERR;
}
if ((retval = nc4_var_free(var))) /* free but leave in parent list */
2017-12-05 03:21:14 +08:00
return retval;
}
ncindexfree(grp->vars);
grp->vars = NULL;
2010-06-03 21:24:43 +08:00
/* Delete all dims. */
for(i=0;i<ncindexsize(grp->dim);i++) {
dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
if(dim == NULL) continue;
LOG((4, "%s: deleting dim %s", __func__, dim->hdr.name));
/* If this is a dim without a coordinate variable, then close
* the HDF5 DIM_WITHOUT_VARIABLE dataset associated with this
* dim. */
2010-06-03 21:24:43 +08:00
if (dim->hdf_dimscaleid && H5Dclose(dim->hdf_dimscaleid) < 0)
2017-12-05 03:21:14 +08:00
return NC_EHDFERR;
if ((retval = nc4_dim_free(dim))) /* free but leave in parent list */
2017-12-05 03:21:14 +08:00
return retval;
2010-06-03 21:24:43 +08:00
}
ncindexfree(grp->dim);
grp->dim = NULL;
2010-06-03 21:24:43 +08:00
/* Delete all types. */
/* Is this code correct? I think it should do repeated passes
over h5->alltypes using the ref count to decide what to delete */
for(i=0;i<ncindexsize(grp->type);i++) {
NC_TYPE_INFO_T* type = (NC_TYPE_INFO_T*)ncindexith(grp->type,i);
if(type == NULL) continue;
LOG((4, "%s: deleting type %s", __func__, type->hdr.name));
if ((retval = nc4_type_free(type))) /* free but leave in parent list */
2017-12-05 03:21:14 +08:00
return retval;
2010-06-03 21:24:43 +08:00
}
ncindexfree(grp->type);
grp->type = NULL;
2010-06-03 21:24:43 +08:00
/* Tell HDF5 we're closing this group. */
LOG((4, "%s: closing group %s", __func__, grp->hdr.name));
if (grp->hdf_grpid && H5Gclose(grp->hdf_grpid) < 0)
2010-06-03 21:24:43 +08:00
return NC_EHDFERR;
/* Free up this group */
/* Free the name. */
free(grp->hdr.name);
free(grp);
2010-06-03 21:24:43 +08:00
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
* @internal Remove a NC_ATT_INFO_T from an index.
* This will nc_free the memory too.
2017-12-03 22:57:21 +08:00
*
* @param list Pointer to pointer of list.
* @param att Pointer to attribute info struct.
*
* @return ::NC_NOERR No error.
* @author Dennis Heimbigner
*/
int
nc4_att_list_del(NCindex *list, NC_ATT_INFO_T *att)
{
assert(att && list);
ncindexidel(list, ((NC_OBJ *)att)->id);
return nc4_att_free(att);
}
/**
* @internal Free memory of an attribute object
*
* @param att Pointer to attribute info struct.
*
* @return ::NC_NOERR No error.
2017-12-03 22:57:21 +08:00
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_att_free(NC_ATT_INFO_T *att)
2010-06-03 21:24:43 +08:00
{
int i;
LOG((3, "%s: name %s ", __func__, att->hdr.name));
2010-06-03 21:24:43 +08:00
/* Free memory that was malloced to hold data for this
* attribute. */
if (att->data)
free(att->data);
2010-07-01 21:08:28 +08:00
/* Free the name. */
if (att->hdr.name) {
free(att->hdr.name);
att->hdr.name = NULL;
}
2010-07-01 21:08:28 +08:00
2010-06-03 21:24:43 +08:00
/* Close the HDF5 typeid. */
if (att->native_hdf_typeid && H5Tclose(att->native_hdf_typeid) < 0)
2010-06-03 21:24:43 +08:00
return NC_EHDFERR;
/* If this is a string array attribute, delete all members of the
* string array, then delete the array of pointers to strings. (The
* array was filled with pointers by HDF5 when the att was read,
* and memory for each string was allocated by HDF5. That's why I
* use free and not nc_free, because the netCDF library didn't
* allocate the memory that is being freed.) */
if (att->stdata)
{
for (i = 0; i < att->len; i++)
if(att->stdata[i])
2017-12-05 03:21:14 +08:00
free(att->stdata[i]);
2010-06-03 21:24:43 +08:00
free(att->stdata);
}
/* If this att has vlen data, release it. */
if (att->vldata)
{
for (i = 0; i < att->len; i++)
2017-12-05 03:21:14 +08:00
nc_free_vlen(&att->vldata[i]);
2010-06-03 21:24:43 +08:00
free(att->vldata);
}
free(att);
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
* @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()
* gets called on the return from utf8proc_NFC, and also ensures that
* the name is not too long.
*
* @param name Name to normalize.
* @param norm_name The normalized name.
*
* @return ::NC_NOERR No error.
* @return ::NC_EMAXNAME Name too long.
* @author Dennis Heimbigner
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
nc4_normalize_name(const char *name, char *norm_name)
{
char *temp_name;
int stat = nc_utf8_normalize((const unsigned char *)name,(unsigned char **)&temp_name);
if(stat != NC_NOERR)
return stat;
2010-06-03 21:24:43 +08:00
if (strlen(temp_name) > NC_MAX_NAME)
{
free(temp_name);
return NC_EMAXNAME;
}
strcpy(norm_name, temp_name);
free(temp_name);
return NC_NOERR;
}
#ifdef ENABLE_SET_LOG_LEVEL
2017-12-03 22:57:21 +08:00
/**
* @internal Use this to set the global log level. Set it to
* NC_TURN_OFF_LOGGING (-1) to turn off all logging. Set it to 0 to
* show only errors, and to higher numbers to show more and more
* logging details. If logging is not enabled with --enable-logging at
* configure when building netCDF, this function will do nothing.
* Note that it is possible to set the log level using the environment
* variable named _NETCDF_LOG_LEVEL_ (e.g. _export NETCDF_LOG_LEVEL=4_).
2017-12-03 22:57:21 +08:00
*
* @param new_level The new logging level.
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
int
2010-06-03 21:24:43 +08:00
nc_set_log_level(int new_level)
{
#ifdef LOGGING
if(!nc4_hdf5_initialized)
2017-12-05 03:21:14 +08:00
nc4_hdf5_initialize();
2010-06-03 21:24:43 +08:00
/* Now remember the new level. */
nc_log_level = new_level;
LOG((4, "log_level changed to %d", nc_log_level));
#endif /*LOGGING */
2010-06-03 21:24:43 +08:00
return 0;
}
#endif /* ENABLE_SET_LOG_LEVEL */
2010-06-03 21:24:43 +08:00
#ifdef LOGGING
2010-06-03 21:24:43 +08:00
#define MAX_NESTS 10
2017-12-03 22:57:21 +08:00
/**
2017-12-05 03:21:14 +08:00
* @internal Recursively print the metadata of a group.
2017-12-03 22:57:21 +08:00
*
* @param grp Pointer to group info struct.
* @param tab_count Number of tabs.
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
static int
rec_print_metadata(NC_GRP_INFO_T *grp, int tab_count)
2010-06-03 21:24:43 +08:00
{
NC_GRP_INFO_T *g;
NC_ATT_INFO_T *att;
NC_VAR_INFO_T *var;
NC_DIM_INFO_T *dim;
NC_TYPE_INFO_T *type;
NC_FIELD_INFO_T *field;
char tabs[MAX_NESTS+1] = "";
char *dims_string = NULL;
2010-06-03 21:24:43 +08:00
char temp_string[10];
int t, retval, d, i;
2010-06-03 21:24:43 +08:00
/* Come up with a number of tabs relative to the group. */
for (t = 0; t < tab_count && t < MAX_NESTS; t++)
tabs[t] = '\t';
tabs[t] = '\0';
2010-06-03 21:24:43 +08:00
LOG((2, "%s GROUP - %s nc_grpid: %d nvars: %d natts: %d",
tabs, grp->hdr.name, grp->hdr.id, ncindexsize(grp->vars), ncindexsize(grp->att)));
for(i=0;i<ncindexsize(grp->att);i++) {
att = (NC_ATT_INFO_T*)ncindexith(grp->att,i);
if(att == NULL) continue;
2010-06-03 21:24:43 +08:00
LOG((2, "%s GROUP ATTRIBUTE - attnum: %d name: %s type: %d len: %d",
tabs, att->hdr.id, att->hdr.name, att->nc_typeid, att->len));
}
2010-06-03 21:24:43 +08:00
for(i=0;i<ncindexsize(grp->dim);i++) {
dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
if(dim == NULL) continue;
2010-06-03 21:24:43 +08:00
LOG((2, "%s DIMENSION - dimid: %d name: %s len: %d unlimited: %d",
tabs, dim->hdr.id, dim->hdr.name, dim->len, dim->unlimited));
}
for(i=0;i<ncindexsize(grp->vars);i++)
2010-06-03 21:24:43 +08:00
{
int j;
var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
if (var == NULL) continue;
if(var->ndims > 0)
{
dims_string = (char*)malloc(sizeof(char)*(var->ndims*4));
strcpy(dims_string, "");
for (d = 0; d < var->ndims; d++)
2017-12-05 03:21:14 +08:00
{
sprintf(temp_string, " %d", var->dimids[d]);
strcat(dims_string, temp_string);
}
}
LOG((2, "%s VARIABLE - varid: %d name: %s ndims: %d dimscale: %d dimids:%s",
tabs, var->hdr.id, var->hdr.name, var->ndims, (int)var->dimscale,
(dims_string ? dims_string : " -")));
for(j=0;j<ncindexsize(var->att);j++) {
att = (NC_ATT_INFO_T*)ncindexith(var->att,j);
2018-08-08 05:17:40 +08:00
if(att == NULL) continue;
2017-12-05 03:21:14 +08:00
LOG((2, "%s VAR ATTRIBUTE - attnum: %d name: %s type: %d len: %d",
tabs, att->hdr.id, att->hdr.name, att->nc_typeid, att->len));
}
if(dims_string)
{
free(dims_string);
dims_string = NULL;
}
2010-06-03 21:24:43 +08:00
}
for(i=0;i<ncindexsize(grp->type);i++)
2010-06-03 21:24:43 +08:00
{
if((type = (NC_TYPE_INFO_T*)ncindexith(grp->type,i)) == NULL) continue;
LOG((2, "%s TYPE - nc_typeid: %d hdf_typeid: 0x%x committed: %d "
"name: %s num_fields: %d", tabs, type->hdr.id,
type->hdf_typeid, type->size, (int)type->committed, type->hdr.name));
2010-06-03 21:24:43 +08:00
/* Is this a compound type? */
if (type->nc_type_class == NC_COMPOUND)
2010-06-03 21:24:43 +08:00
{
2018-08-08 05:17:40 +08:00
int j;
2017-12-05 03:21:14 +08:00
LOG((3, "compound type"));
2018-08-08 05:17:40 +08:00
for(j=0;j<nclistlength(type->u.c.field);j++) {
field = (NC_FIELD_INFO_T*)nclistget(type->u.c.field,j);
LOG((4, "field %s offset %d nctype %d ndims %d", field->hdr.name,
2017-12-05 03:21:14 +08:00
field->offset, field->nc_typeid, field->ndims));
2018-08-08 05:17:40 +08:00
}
2010-06-03 21:24:43 +08:00
}
else if (type->nc_type_class == NC_VLEN)
{
2017-12-05 03:21:14 +08:00
LOG((3, "VLEN type"));
LOG((4, "base_nc_type: %d", type->u.v.base_nc_typeid));
}
else if (type->nc_type_class == NC_OPAQUE)
2017-12-05 03:21:14 +08:00
LOG((3, "Opaque type"));
else if (type->nc_type_class == NC_ENUM)
{
2017-12-05 03:21:14 +08:00
LOG((3, "Enum type"));
LOG((4, "base_nc_type: %d", type->u.e.base_nc_typeid));
}
2010-06-03 21:24:43 +08:00
else
{
2017-12-05 03:21:14 +08:00
LOG((0, "Unknown class: %d", type->nc_type_class));
return NC_EBADTYPE;
2010-06-03 21:24:43 +08:00
}
}
2010-06-03 21:24:43 +08:00
/* Call self for each child of this group. */
for(i=0;i<ncindexsize(grp->children);i++)
2010-06-03 21:24:43 +08:00
{
if((g = (NC_GRP_INFO_T*)ncindexith(grp->children,i)) == NULL) continue;
if ((retval = rec_print_metadata(g, tab_count + 1)))
2018-08-08 05:17:40 +08:00
return retval;
2010-06-03 21:24:43 +08:00
}
return NC_NOERR;
}
2017-12-03 22:57:21 +08:00
/**
* @internal Print out the internal metadata for a file. This is
* useful to check that netCDF is working! Nonetheless, this function
2017-12-05 03:21:14 +08:00
* will print nothing if logging is not set to at least two.
2017-12-03 22:57:21 +08:00
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
log_metadata_nc(NC *nc)
2010-06-03 21:24:43 +08:00
{
NC_FILE_INFO_T *h5 = NC4_DATA(nc);
2010-06-03 21:24:43 +08:00
LOG((2, "*** NetCDF-4 Internal Metadata: int_ncid 0x%x ext_ncid 0x%x",
2017-12-05 03:21:14 +08:00
nc->int_ncid, nc->ext_ncid));
2010-06-03 21:24:43 +08:00
if (!h5)
{
LOG((2, "This is a netCDF-3 file."));
return NC_NOERR;
}
LOG((2, "FILE - path: %s cmode: 0x%x parallel: %d redef: %d "
"fill_mode: %d no_write: %d next_nc_grpid: %d", nc->path,
2017-12-05 03:21:14 +08:00
h5->cmode, (int)h5->parallel, (int)h5->redef, h5->fill_mode, (int)h5->no_write,
h5->next_nc_grpid));
if(nc_log_level >= 2)
2018-08-08 05:17:40 +08:00
return rec_print_metadata(h5->root_grp, 0);
return NC_NOERR;
2010-06-03 21:24:43 +08:00
}
#endif /*LOGGING */
2017-12-03 22:57:21 +08:00
/**
* @internal Show the in-memory metadata for a netcdf file.
*
2017-12-05 03:21:14 +08:00
* @param ncid File and group ID.
*
2017-12-03 22:57:21 +08:00
* @return ::NC_NOERR No error.
* @author Ed Hartnett
2017-12-05 03:21:14 +08:00
*/
2010-06-03 21:24:43 +08:00
int
NC4_show_metadata(int ncid)
{
int retval = NC_NOERR;
#ifdef LOGGING
NC *nc;
2010-06-03 21:24:43 +08:00
int old_log_level = nc_log_level;
2010-06-03 21:24:43 +08:00
/* Find file metadata. */
if (!(nc = nc4_find_nc_file(ncid,NULL)))
2010-06-03 21:24:43 +08:00
return NC_EBADID;
/* Log level must be 2 to see metadata. */
nc_log_level = 2;
retval = log_metadata_nc(nc);
nc_log_level = old_log_level;
#endif /*LOGGING*/
return retval;
}