diff --git a/ThirdParty/netcdf/update.sh b/ThirdParty/netcdf/update.sh index 18e4a36524cdeb8becfc65fcc4f22d298e888816..d87536605626896bfbe8a028aa40e40b73a0c432 100755 --- a/ThirdParty/netcdf/update.sh +++ b/ThirdParty/netcdf/update.sh @@ -8,7 +8,7 @@ readonly name="netcdf" readonly ownership="netcdf Upstream <kwrobot@kitware.com>" readonly subtree="ThirdParty/$name/vtk$name" readonly repo="https://gitlab.kitware.com/third-party/netcdf.git" -readonly tag="for/vtk-20190607.1-4.7.0" +readonly tag="for/vtk-20190618-4.7.0" readonly paths=" include/*.h include/netcdf_meta.h.in @@ -18,6 +18,7 @@ liblib/nc_initialize.c libsrc/*.h libsrc/*.c libsrc4/*.c +libhdf5/*.c .gitattributes CMakeLists.vtk.txt diff --git a/ThirdParty/netcdf/vtknetcdf/CMakeLists.txt b/ThirdParty/netcdf/vtknetcdf/CMakeLists.txt index 9f3d6911cbf445ff8ad692bbd5669b1a6459e999..07a71bb723af2ca23136e431951df35301b4c2fd 100644 --- a/ThirdParty/netcdf/vtknetcdf/CMakeLists.txt +++ b/ThirdParty/netcdf/vtknetcdf/CMakeLists.txt @@ -13,6 +13,7 @@ set(sources libdispatch/dattinq.c libdispatch/dattput.c libdispatch/dauth.c + libdispatch/daux.c libdispatch/dcompound.c libdispatch/dcopy.c libdispatch/ddim.c @@ -23,10 +24,13 @@ set(sources libdispatch/dgroup.c libdispatch/dinfermodel.c libdispatch/dinternal.c + libdispatch/dnotnc3.c + libdispatch/dnotnc4.c libdispatch/doffsets.c libdispatch/dopaque.c libdispatch/dparallel.c libdispatch/drc.c + libdispatch/dreadonly.c libdispatch/dstring.c libdispatch/dtype.c libdispatch/dutf8.c @@ -37,8 +41,8 @@ set(sources libdispatch/dvarput.c libdispatch/dvlen.c libdispatch/dwinpath.c - libdispatch/nc.c libdispatch/ncbytes.c + libdispatch/nc.c libdispatch/nchashmap.c libdispatch/nclist.c libdispatch/nclistmgr.c @@ -72,7 +76,24 @@ set(sources libsrc4/nc4type.c libsrc4/nc4var.c libsrc4/ncfunc.c - libsrc4/ncindex.c) + libsrc4/ncindex.c + + libhdf5/nc4hdf.c + libhdf5/nc4info.c + libhdf5/hdf5file.c + libhdf5/hdf5attr.c + libhdf5/hdf5dim.c + libhdf5/hdf5grp.c + libhdf5/hdf5type.c + libhdf5/hdf5internal.c + libhdf5/hdf5create.c + libhdf5/hdf5open.c + libhdf5/hdf5var.c + libhdf5/nc4mem.c + libhdf5/nc4memcb.c + libhdf5/hdf5cache.c + libhdf5/hdf5dispatch.c + ) set(CHUNK_CACHE_NELEMS 1009) set(CHUNK_CACHE_PREEMPTION 0.75) @@ -91,6 +112,7 @@ set(USE_DISKLESS 1) set(USE_FFIO 0) set(USE_FSYNC 0) set(USE_HDF4 0) +set(USE_HDF5 1) set(USE_MMAP 0) set(USE_NETCDF4 1) set(USE_NETCDF_2 0) @@ -148,27 +170,28 @@ check_symbol_exists("strlcat" "string.h" HAVE_STRLCAT) check_symbol_exists("st_blocksize" "sys/stat.h" HAVE_STRUCT_STAT_ST_BLKSIZE) check_symbol_exists("sysconf" "unistd.h" HAVE_SYSCONF) -set(CMAKE_REQUIRED_INCLUDES_save "${CMAKE_REQUIRED_INCLUDES}") -list(APPEND CMAKE_REQUIRED_INCLUDES - "${CMAKE_CURRENT_BINARY_DIR}/../../hdf5") +set(HAVE_HDF5_H 1) +set(H5_USE_16_API 1) # Enable HDF5 1.6.x Compatibility(Required) if (VTK_MODULE_USE_EXTERNAL_hdf5) + set(CMAKE_REQUIRED_INCLUDES_save "${CMAKE_REQUIRED_INCLUDES}") + list(APPEND CMAKE_REQUIRED_INCLUDES + "${CMAKE_CURRENT_BINARY_DIR}/../../hdf5") # The symbol detections can still fail for an external HDF5 that isn't in a # standard location. We need generate-time knowledge to get the real include # directories. These checks should really be replaced at some point with some # compile-time logic if at all possible (version checks, preprocessor # detections, etc.). + check_symbol_exists("H5free_memory" "vtk_hdf5.h" HDF5_HAS_H5FREE) + check_symbol_exists("H5Pset_all_coll_metadata_ops" "vtk_hdf5.h" H5PSET_ALL_COLL_METADATA_OPS) + check_symbol_exists("H5Pset_libver_bounds" "vtk_hdf5.h" HDF5_HAS_LIBVER_BOUNDS) + check_symbol_exists("HDF5_PARALLEL" "vtk_hdf5.h" HDF5_PARALLEL) + set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES_save}") else () - list(APPEND CMAKE_REQUIRED_INCLUDES - "${CMAKE_CURRENT_BINARY_DIR}/../../hdf5/vtkhdf5" - "${CMAKE_CURRENT_SOURCE_DIR}/../../hdf5" - "${CMAKE_CURRENT_SOURCE_DIR}/../../hdf5/vtkhdf5/src") + set(HDF5_HAS_H5FREE 1) + set(H5PSET_ALL_COLL_METADATA_OPS 1) + set(HDF5_HAS_LIBVER_BOUNDS 1) + set(HDF5_PARALLEL 0) endif () -set(HAVE_HDF5_H 1) -check_symbol_exists("H5free_memory" "vtk_hdf5.h" HDF5_HAS_H5FREE) -check_symbol_exists("H5Pset_all_coll_metadata_ops" "vtk_hdf5.h" H5PSET_ALL_COLL_METADATA_OPS) -check_symbol_exists("H5Pset_libver_bounds" "vtk_hdf5.h" HDF5_HAS_LIBVER_BOUNDS) -check_symbol_exists("HDF5_PARALLEL" "vtk_hdf5.h" HDF5_PARALLEL) -set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES_save}") include(CheckIncludeFiles) check_include_file("alloca.h" HAVE_ALLOCA_H) diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/H5FDhttp.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/H5FDhttp.c new file mode 100644 index 0000000000000000000000000000000000000000..ef0a45ed4ef9264bcf453dce08a8564459699b13 --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/H5FDhttp.c @@ -0,0 +1,890 @@ +/********************************************************************* +* Copyright 2018, UCAR/Unidata +* See netcdf/COPYRIGHT file for copying and redistribution conditions. +* ********************************************************************/ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* Programmer: Dennis Heimbigner dmh@ucar.edu + * + * Purpose: Access remote datasets using byte range requests. + * Derived from the HDF5 H5FDstdio.c file. + * + * NOTE: This driver is not as well tested as the standard SEC2 driver + * and is not intended for production use! + */ + +#include "config.h" + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include <hdf5.h> +#include <curl/curl.h> + +#ifdef H5_HAVE_FLOCK +/* Needed for lock type definitions (e.g., LOCK_EX) */ +#include <sys/file.h> +#endif /* H5_HAVE_FLOCK */ + +#ifdef H5_HAVE_UNISTD_H +#include <unistd.h> +#endif + +/* +Define a simple #ifdef test for the version of H5FD_class_t we are using +*/ +#if H5_VERS_MAJOR == 1 +#if H5_VERS_MINOR < 10 +#define H5FDCLASS1 1 +#endif +#else +#error "Cannot determine version of H5FD_class_t" +#endif + +#ifdef H5_HAVE_WIN32_API +/* The following two defines must be before any windows headers are included */ +#define WIN32_LEAN_AND_MEAN /* Exclude rarely-used stuff from Windows headers */ +#define NOGDI /* Exclude Graphic Display Interface macros */ + +#include <windows.h> +#include <io.h> + +#endif /* H5_HAVE_WIN32_API */ + +#include "netcdf.h" +#include "ncbytes.h" +#include "nchttp.h" + +#include "H5FDhttp.h" + +typedef off_t file_offset_t; + +/* The driver identification number, initialized at runtime */ +static hid_t H5FD_HTTP_g = 0; + +/* File operations */ +typedef enum { + H5FD_HTTP_OP_UNKNOWN=0, + H5FD_HTTP_OP_READ=1, + H5FD_HTTP_OP_WRITE=2, + H5FD_HTTP_OP_SEEK=3 +} H5FD_http_file_op; + +/* The description of a file belonging to this driver. The 'eoa' and 'eof' + * determine the amount of hdf5 address space in use and the high-water mark + * of the file (the current size of the underlying Unix file). The 'pos' + * value is used to eliminate file position updates when they would be a + * no-op. Unfortunately we've found systems that use separate file position + * indicators for reading and writing so the lseek can only be eliminated if + * the current operation is the same as the previous operation. When opening + * a file the 'eof' will be set to the current file size, 'eoa' will be set + * to zero, 'pos' will be set to H5F_ADDR_UNDEF (as it is when an error + * occurs), and 'op' will be set to H5F_OP_UNKNOWN. + */ +typedef struct H5FD_http_t { + H5FD_t pub; /* public stuff, must be first */ + haddr_t eoa; /* end of allocated region */ + haddr_t eof; /* end of file; current file size */ + haddr_t pos; /* current file I/O position */ + unsigned write_access; /* Flag to indicate the file was opened with write access */ + H5FD_http_file_op op; /* last operation */ + CURL* curl; /* Curl handle */ + char* url; /* The URL (minus any fragment) for the dataset */ +} H5FD_http_t; + + +/* These macros check for overflow of various quantities. These macros + * assume that file_offset_t is signed and haddr_t and size_t are unsigned. + * + * ADDR_OVERFLOW: Checks whether a file address of type `haddr_t' + * is too large to be represented by the second argument + * of the file seek function. + * + * SIZE_OVERFLOW: Checks whether a buffer size of type `hsize_t' is too + * large to be represented by the `size_t' type. + * + * REGION_OVERFLOW: Checks whether an address and size pair describe data + * which can be addressed entirely by the second + * argument of the file seek function. + */ +/* adding for windows NT filesystem support. */ +#define MAXADDR (((haddr_t)1<<(8*sizeof(file_offset_t)-1))-1) +#define ADDR_OVERFLOW(A) (HADDR_UNDEF==(A) || ((A) & ~(haddr_t)MAXADDR)) +#define SIZE_OVERFLOW(Z) ((Z) & ~(hsize_t)MAXADDR) +#define REGION_OVERFLOW(A,Z) (ADDR_OVERFLOW(A) || SIZE_OVERFLOW(Z) || \ + HADDR_UNDEF==(A)+(Z) || (file_offset_t)((A)+(Z))<(file_offset_t)(A)) + +/* Prototypes */ +static H5FD_t *H5FD_http_open(const char *name, unsigned flags, + hid_t fapl_id, haddr_t maxaddr); +static herr_t H5FD_http_close(H5FD_t *lf); +static int H5FD_http_cmp(const H5FD_t *_f1, const H5FD_t *_f2); +static herr_t H5FD_http_query(const H5FD_t *_f1, unsigned long *flags); +static haddr_t H5FD_http_alloc(H5FD_t *_file, H5FD_mem_t type, hid_t dxpl_id, hsize_t size); +static haddr_t H5FD_http_get_eoa(const H5FD_t *_file, H5FD_mem_t type); +static herr_t H5FD_http_set_eoa(H5FD_t *_file, H5FD_mem_t type, haddr_t addr); +static herr_t H5FD_http_get_handle(H5FD_t *_file, hid_t fapl, void** file_handle); +static herr_t H5FD_http_read(H5FD_t *lf, H5FD_mem_t type, hid_t fapl_id, haddr_t addr, + size_t size, void *buf); +static herr_t H5FD_http_write(H5FD_t *lf, H5FD_mem_t type, hid_t fapl_id, haddr_t addr, + size_t size, const void *buf); + +/* The H5FD_class_t structure has different versions */ +#ifdef H5FDCLASS1 +static haddr_t H5FD_http_get_eof(const H5FD_t *_file); +static herr_t H5FD_http_flush(H5FD_t *_file, hid_t dxpl_id, unsigned closing); +static herr_t H5FD_http_lock(H5FD_t *_file, unsigned char* old, unsigned lock_type, hbool_t last); +static herr_t H5FD_http_unlock(H5FD_t *file, unsigned char *oid, hbool_t last); +#else +static herr_t H5FD_http_term(void); +static haddr_t H5FD_http_get_eof(const H5FD_t *_file, H5FD_mem_t type); +static herr_t H5FD_http_flush(H5FD_t *_file, hid_t dxpl_id, hbool_t closing); +static herr_t H5FD_http_lock(H5FD_t *_file, hbool_t rw); +static herr_t H5FD_http_unlock(H5FD_t *_file); +#endif + +/* Beware, not same as H5FD_HTTP_g */ +static const H5FD_class_t H5FD_http_g = { + "http", /* name */ + MAXADDR, /* maxaddr */ + H5F_CLOSE_WEAK, /* fc_degree */ +#ifndef H5FDCLASS1 + H5FD_http_term, /* terminate */ +#endif + NULL, /* sb_size */ + NULL, /* sb_encode */ + NULL, /* sb_decode */ + 0, /* fapl_size */ + NULL, /* fapl_get */ + NULL, /* fapl_copy */ + NULL, /* fapl_free */ + 0, /* dxpl_size */ + NULL, /* dxpl_copy */ + NULL, /* dxpl_free */ + H5FD_http_open, /* open */ + H5FD_http_close, /* close */ + H5FD_http_cmp, /* cmp */ + H5FD_http_query, /* query */ + NULL, /* get_type_map */ + H5FD_http_alloc, /* alloc */ + NULL, /* free */ + H5FD_http_get_eoa, /* get_eoa */ + H5FD_http_set_eoa, /* set_eoa */ + H5FD_http_get_eof, /* get_eof */ + H5FD_http_get_handle, /* get_handle */ + H5FD_http_read, /* read */ + H5FD_http_write, /* write */ + H5FD_http_flush, /* flush */ + NULL, /* truncate */ + H5FD_http_lock, /* lock */ + H5FD_http_unlock, /* unlock */ + H5FD_FLMAP_DICHOTOMY /* fl_map */ +}; + + +/*------------------------------------------------------------------------- + * Function: H5FD_http_init + * + * Purpose: Initialize this driver by registering the driver with the + * library. + * + * Return: Success: The driver ID for the driver. + * + * Failure: Negative. + * + * Programmer: Robb Matzke + * Thursday, July 29, 1999 + * + *------------------------------------------------------------------------- + */ +EXTERNL hid_t +H5FD_http_init(void) +{ + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + if (H5I_VFL!=H5Iget_type(H5FD_HTTP_g)) + H5FD_HTTP_g = H5FDregister(&H5FD_http_g); + return H5FD_HTTP_g; +} /* end H5FD_http_init() */ + + +/*--------------------------------------------------------------------------- + * Function: H5FD_http_term + * + * Purpose: Shut down the VFD + * + * Returns: Non-negative on success or negative on failure + * + * Programmer: Quincey Koziol + * Friday, Jan 30, 2004 + * + *--------------------------------------------------------------------------- + */ +#ifndef H5FDCLASS1 +static herr_t +H5FD_http_term(void) +{ + /* Reset VFL ID */ + H5FD_HTTP_g = 0; + + return 0; +} /* end H5FD_http_term() */ +#endif + + +/*------------------------------------------------------------------------- + * Function: H5Pset_fapl_http + * + * Purpose: Modify the file access property list to use the H5FD_HTTP + * driver defined in this source file. There are no driver + * specific properties. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Robb Matzke + * Thursday, February 19, 1998 + * + *------------------------------------------------------------------------- + */ +EXTERNL herr_t +H5Pset_fapl_http(hid_t fapl_id) +{ + static const char *func = "H5FDset_fapl_http"; /*for error reporting*/ + + /*NO TRACE*/ + + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + if(0 == H5Pisa_class(fapl_id, H5P_FILE_ACCESS)) + H5Epush_ret(func, H5E_ERR_CLS, H5E_PLIST, H5E_BADTYPE, "not a file access property list", -1) + + return H5Pset_driver(fapl_id, H5FD_HTTP, NULL); +} /* end H5Pset_fapl_http() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_http_open + * + * Purpose: Opens a remote Object as an HDF5 file. + * + * Errors: + * IO CANTOPENFILE File doesn't exist and CREAT wasn't + * specified. + * + * Return: + * Success: A pointer to a new file data structure. The + * public fields will be initialized by the + * caller, which is always H5FD_open(). + * + * Failure: NULL + * + * Programmer: Dennis Heimbigner + * + *------------------------------------------------------------------------- + */ +static H5FD_t * +H5FD_http_open( const char *name, unsigned flags, hid_t /*UNUSED*/ fapl_id, + haddr_t maxaddr) +{ + unsigned write_access = 0; /* File opened with write access? */ + H5FD_http_t *file = NULL; + static const char *func = "H5FD_http_open"; /* Function Name for error reporting */ + CURL* curl = NULL; + long long len = -1; + int ncstat = NC_NOERR; + + /* Sanity check on file offsets */ + assert(sizeof(file_offset_t) >= sizeof(size_t)); + + /* Quiet compiler */ + fapl_id = fapl_id; + + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + /* Check arguments */ + if (!name || !*name) + H5Epush_ret(func, H5E_ERR_CLS, H5E_ARGS, H5E_BADVALUE, "invalid URL", NULL) + if (0 == maxaddr || HADDR_UNDEF == maxaddr) + H5Epush_ret(func, H5E_ERR_CLS, H5E_ARGS, H5E_BADRANGE, "bogus maxaddr", NULL) + if (ADDR_OVERFLOW(maxaddr)) + H5Epush_ret(func, H5E_ERR_CLS, H5E_ARGS, H5E_OVERFLOW, "maxaddr too large", NULL) + + /* Always read-only */ + write_access = 0; + + /* Open file in read-only mode, to check for existence and get length */ + if((ncstat = nc_http_open(name,&curl,&len))) { + H5Epush_ret(func, H5E_ERR_CLS, H5E_IO, H5E_CANTOPENFILE, "cannot access object", NULL) + } + + /* Build the return value */ + if(NULL == (file = (H5FD_http_t *)H5allocate_memory(sizeof(H5FD_http_t),0))) { + nc_http_close(curl); + H5Epush_ret(func, H5E_ERR_CLS, H5E_RESOURCE, H5E_NOSPACE, "memory allocation failed", NULL) + } /* end if */ + memset(file,0,sizeof(H5FD_http_t)); + + file->op = H5FD_HTTP_OP_SEEK; + file->pos = HADDR_UNDEF; + file->write_access = write_access; /* Note the write_access for later */ + file->eof = (haddr_t)len; + file->curl = curl; curl = NULL; + file->url = H5allocate_memory(strlen(name+1),0); + if(file->url == NULL) { + nc_http_close(curl); + H5Epush_ret(func, H5E_ERR_CLS, H5E_RESOURCE, H5E_NOSPACE, "memory allocation failed", NULL) + } + memcpy(file->url,name,strlen(name)+1); + + return((H5FD_t*)file); +} /* end H5FD_HTTP_OPen() */ + + +/*------------------------------------------------------------------------- + * Function: H5F_http_close + * + * Purpose: Closes a file. + * + * Errors: + * IO CLOSEERROR Fclose failed. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Dennis Heimbigner + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_http_close(H5FD_t *_file) +{ + H5FD_http_t *file = (H5FD_http_t*)_file; +#if 0 + static const char *func = "H5FD_http_close"; /* Function Name for error reporting */ +#endif + + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + /* Close the underlying curl handle*/ + if(file->curl) nc_http_close(file->curl); + if(file->url) H5free_memory(file->url); + + H5free_memory(file); + + return 0; +} /* end H5FD_http_close() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_http_cmp + * + * Purpose: Compares two files belonging to this driver using an + * arbitrary (but consistent) ordering. + * + * Return: + * Success: A value like strcmp() + * + * Failure: never fails (arguments were checked by the caller). + * + * Programmer: Robb Matzke + * Thursday, July 29, 1999 + * + *------------------------------------------------------------------------- + */ +static int +H5FD_http_cmp(const H5FD_t *_f1, const H5FD_t *_f2) +{ + const H5FD_http_t *f1 = (const H5FD_http_t*)_f1; + const H5FD_http_t *f2 = (const H5FD_http_t*)_f2; + + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + if(strcmp(f1->url,f2->url) < 0) return -1; + if(strcmp(f1->url,f2->url) > 0) return 1; + return 0; +} /* H5FD_http_cmp() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_http_query + * + * Purpose: Set the flags that this VFL driver is capable of supporting. + * (listed in H5FDpublic.h) + * + * Return: Success: non-negative + * + * Failure: negative + * + * Programmer: Quincey Koziol + * Friday, August 25, 2000 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_http_query(const H5FD_t *_f, unsigned long /*OUT*/ *flags) +{ + /* Quiet the compiler */ + _f=_f; + + /* Set the VFL feature flags that this driver supports. + * + * Note that this VFD does not support SWMR due to the unpredictable + * nature of the buffering layer. + */ + if(flags) { + *flags = 0; + *flags |= H5FD_FEAT_AGGREGATE_METADATA; /* OK to aggregate metadata allocations */ + *flags |= H5FD_FEAT_ACCUMULATE_METADATA; /* OK to accumulate metadata for faster writes */ + *flags |= H5FD_FEAT_DATA_SIEVE; /* OK to perform data sieving for faster raw data reads & writes */ + *flags |= H5FD_FEAT_AGGREGATE_SMALLDATA; /* OK to aggregate "small" raw data allocations */ +#ifndef H5FDCLASS1 + *flags |= H5FD_FEAT_DEFAULT_VFD_COMPATIBLE; /* VFD creates a file which can be opened with the default VFD */ +#endif + } + + return 0; +} /* end H5FD_http_query() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_http_alloc + * + * Purpose: Allocates file memory. If fseeko isn't available, makes + * sure the file size isn't bigger than 2GB because the + * parameter OFFSET of fseek is of the type LONG INT, limiting + * the file size to 2GB. + * + * Return: + * Success: Address of new memory + * + * Failure: HADDR_UNDEF + * + * Programmer: Raymond Lu + * 30 March 2007 + * + *------------------------------------------------------------------------- + */ +static haddr_t +H5FD_http_alloc(H5FD_t *_file, H5FD_mem_t /*UNUSED*/ type, hid_t /*UNUSED*/ dxpl_id, hsize_t size) +{ + H5FD_http_t *file = (H5FD_http_t*)_file; + haddr_t addr; + + /* Quiet compiler */ + type = type; + dxpl_id = dxpl_id; + + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + /* Compute the address for the block to allocate */ + addr = file->eoa; + + file->eoa = addr + size; + + return addr; +} /* end H5FD_http_alloc() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_http_get_eoa + * + * Purpose: Gets the end-of-address marker for the file. The EOA marker + * is the first address past the last byte allocated in the + * format address space. + * + * Return: Success: The end-of-address marker. + * + * Failure: HADDR_UNDEF + * + * Programmer: Robb Matzke + * Monday, August 2, 1999 + * + *------------------------------------------------------------------------- + */ +static haddr_t +H5FD_http_get_eoa(const H5FD_t *_file, H5FD_mem_t /*UNUSED*/ type) +{ + const H5FD_http_t *file = (const H5FD_http_t *)_file; + + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + /* Quiet compiler */ + type = type; + + return file->eoa; +} /* end H5FD_http_get_eoa() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_http_set_eoa + * + * Purpose: Set the end-of-address marker for the file. This function is + * called shortly after an existing HDF5 file is opened in order + * to tell the driver where the end of the HDF5 data is located. + * + * Return: Success: 0 + * + * Failure: Does not fail + * + * Programmer: Robb Matzke + * Thursday, July 29, 1999 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_http_set_eoa(H5FD_t *_file, H5FD_mem_t /*UNUSED*/ type, haddr_t addr) +{ + H5FD_http_t *file = (H5FD_http_t*)_file; + + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + /* Quiet the compiler */ + type = type; + + file->eoa = addr; + + return 0; +} + + +/*------------------------------------------------------------------------- + * Function: H5FD_http_get_eof + * + * Purpose: Returns the end-of-file marker, which is the greater of + * either the Unix end-of-file or the HDF5 end-of-address + * markers. + * + * Return: Success: End of file address, the first address past + * the end of the "file", either the Unix file + * or the HDF5 file. + * + * Failure: HADDR_UNDEF + * + * Programmer: Robb Matzke + * Thursday, July 29, 1999 + * + *------------------------------------------------------------------------- + */ + +static haddr_t +#ifdef H5FDCLASS1 +H5FD_http_get_eof(const H5FD_t *_file) +#else +H5FD_http_get_eof(const H5FD_t *_file, H5FD_mem_t /*UNUSED*/ type) +#endif +{ + const H5FD_http_t *file = (const H5FD_http_t *)_file; + +#ifndef H5FDCLASS1 + /* Quiet the compiler */ + type = type; +#endif + + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + return(file->eof); +} /* end H5FD_http_get_eof() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_http_get_handle + * + * Purpose: Returns the file handle of file driver. + * + * Returns: Non-negative if succeed or negative if fails. + * + * Programmer: Raymond Lu + * Sept. 16, 2002 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_http_get_handle(H5FD_t *_file, hid_t /*UNUSED*/ fapl, void **file_handle) +{ + H5FD_http_t *file = (H5FD_http_t *)_file; + static const char *func = "H5FD_http_get_handle"; /* Function Name for error reporting */ + + /* Quiet the compiler */ + fapl = fapl; + + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + *file_handle = file->curl; + if(*file_handle == NULL) + H5Epush_ret(func, H5E_ERR_CLS, H5E_IO, H5E_WRITEERROR, "get handle failed", -1) + + return 0; +} /* end H5FD_http_get_handle() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_http_read + * + * Purpose: Reads SIZE bytes beginning at address ADDR in file LF and + * places them in buffer BUF. Reading past the logical or + * physical end of file returns zeros instead of failing. + * + * Errors: + * IO READERROR fread failed. + * IO SEEKERROR fseek failed. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Robb Matzke + * Wednesday, October 22, 1997 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_http_read(H5FD_t *_file, H5FD_mem_t /*UNUSED*/ type, hid_t /*UNUSED*/ dxpl_id, + haddr_t addr, size_t size, void /*OUT*/ *buf) +{ + H5FD_http_t *file = (H5FD_http_t*)_file; + static const char *func = "H5FD_http_read"; /* Function Name for error reporting */ + int ncstat = NC_NOERR; + + /* Quiet the compiler */ + type = type; + dxpl_id = dxpl_id; + + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + /* Check for overflow */ + if (HADDR_UNDEF==addr) + H5Epush_ret (func, H5E_ERR_CLS, H5E_IO, H5E_OVERFLOW, "file address overflowed", -1) + if (REGION_OVERFLOW(addr, size)) + H5Epush_ret (func, H5E_ERR_CLS, H5E_IO, H5E_OVERFLOW, "file address overflowed", -1) + + /* Check easy cases */ + if (0 == size) + return 0; + if ((haddr_t)addr >= file->eof) { + memset(buf, 0, size); + return 0; + } + + /* Seek to the correct file position. */ + if (!(file->op == H5FD_HTTP_OP_READ || file->op == H5FD_HTTP_OP_SEEK) || + file->pos != addr) { +#if 0 + if (file_fseek(file->fp, (file_offset_t)addr, SEEK_SET) < 0) { + file->op = H5FD_HTTP_OP_UNKNOWN; + file->pos = HADDR_UNDEF; + H5Epush_ret(func, H5E_ERR_CLS, H5E_IO, H5E_SEEKERROR, "fseek failed", -1) + } +#endif + file->pos = addr; + } + + /* Read zeros past the logical end of file (physical is handled below) */ + if (addr + size > file->eof) { + size_t nbytes = (size_t) (addr + size - file->eof); + memset((unsigned char *)buf + size - nbytes, 0, nbytes); + size -= nbytes; + } + + { + NCbytes* bbuf = ncbytesnew(); + if((ncstat = nc_http_read(file->curl,file->url,addr,size,bbuf))) { + file->op = H5FD_HTTP_OP_UNKNOWN; + file->pos = HADDR_UNDEF; + ncbytesfree(bbuf); bbuf = NULL; + H5Epush_ret(func, H5E_ERR_CLS, H5E_IO, H5E_READERROR, "HTTP byte-range read failed", -1) + } /* end if */ + + /* Check that proper number of bytes was read */ + if(ncbyteslength(bbuf) != size) { + ncbytesfree(bbuf); bbuf = NULL; + H5Epush_ret(func, H5E_ERR_CLS, H5E_IO, H5E_READERROR, "HTTP byte-range read mismatch ", -1) + } + + /* Extract the data from buf */ + memcpy(buf,ncbytescontents(bbuf),size); + ncbytesfree(bbuf); + } + + /* Update the file position data. */ + file->op = H5FD_HTTP_OP_READ; + file->pos = addr; + + return 0; +} + + +/*------------------------------------------------------------------------- + * Function: H5FD_http_write + * + * Purpose: Writes SIZE bytes from the beginning of BUF into file LF at + * file address ADDR. + * + * Errors: + * IO SEEKERROR fseek failed. + * IO WRITEERROR fwrite failed. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Dennis Heimbigner + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_http_write(H5FD_t *_file, H5FD_mem_t /*UNUSED*/ type, hid_t /*UNUSED*/ dxpl_id, + haddr_t addr, size_t size, const void *buf) +{ + static const char *func = "H5FD_http_write"; /* Function Name for error reporting */ + + /* Quiet the compiler */ + dxpl_id = dxpl_id; + type = type; + + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + /* Always Fails */ + H5Epush_ret (func, H5E_ERR_CLS, H5E_IO, H5E_WRITEERROR, "file is read-only", -1) + + return 0; +} + + +/*------------------------------------------------------------------------- + * Function: H5FD_http_flush + * + * Purpose: Makes sure that all data is on disk. + * + * Errors: + * IO SEEKERROR fseek failed. + * IO WRITEERROR fflush or fwrite failed. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Robb Matzke + * Wednesday, October 22, 1997 + * + *------------------------------------------------------------------------- + */ +static herr_t +#ifdef H5FDCLASS1 +H5FD_http_flush(H5FD_t *_file, hid_t dxpl_id, unsigned closing) +#else +H5FD_http_flush(H5FD_t *_file, hid_t /*UNUSED*/ dxpl_id, hbool_t closing) +#endif +{ + +#ifndef H5FDCLASS1 + /* Quiet the compiler */ + dxpl_id = dxpl_id; +#endif + + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + return 0; +} /* end H5FD_http_flush() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_http_lock + * + * Purpose: Lock a file via flock + * NOTE: This function is a no-op if flock() is not present. + * + * Errors: + * IO FCNTL flock failed. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Vailin Choi; March 2015 + * + *------------------------------------------------------------------------- + */ +static herr_t +#ifdef H5FDCLASS1 +H5FD_http_lock(H5FD_t *_file, unsigned char* old, unsigned lock_type, hbool_t last) +#else +H5FD_http_lock(H5FD_t *_file, hbool_t rw) +#endif +{ + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + +#ifdef H5FDCLASS1 + /* Quiet the compiler */ + lock_type = lock_type; + last = last; +#else + rw = rw; +#endif + + return 0; +} /* end H5FD_http_lock() */ + +/*------------------------------------------------------------------------- + * Function: H5F_http_unlock + * + * Purpose: Unlock a file via flock + * NOTE: This function is a no-op if flock() is not present. + * + * Errors: + * IO FCNTL flock failed. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Vailin Choi; March 2015 + * + *------------------------------------------------------------------------- + */ +static herr_t +#ifdef H5FDCLASS1 +H5FD_http_unlock(H5FD_t *file, /*UNUSED*/unsigned char *oid, /*UNUSED*/ hbool_t last) +#else +H5FD_http_unlock(H5FD_t *_file) +#endif +{ + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + /* Quiet the compiler */ +#ifdef H5FDCLASS1 + oid = oid; + last = last; +#endif + + return 0; +} /* end H5FD_http_unlock() */ + + +#ifdef _H5private_H +/* + * This is not related to the functionality of the driver code. + * It is added here to trigger warning if HDF5 private definitions are included + * by mistake. The code should use only HDF5 public API and definitions. + */ +#error "Do not use HDF5 private definitions" +#endif diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5attr.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5attr.c new file mode 100644 index 0000000000000000000000000000000000000000..70356a7234c214fd1c26a35c2afdf7a47b77802e --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5attr.c @@ -0,0 +1,940 @@ +/* Copyright 2003-2018, University Corporation for Atmospheric + * Research. See COPYRIGHT file for copying and redistribution + * conditions. */ +/** + * @file + * @internal This file handles HDF5 attributes. + * + * @author Ed Hartnett + */ + +#include "config.h" +#include "hdf5internal.h" + +/** + * @internal Get the attribute list for either a varid or NC_GLOBAL + * + * @param grp Group + * @param varid Variable ID | NC_BLOGAL + * @param varp Pointer that gets pointer to NC_VAR_INFO_T + * instance. Ignored if NULL. + * @param attlist Pointer that gets pointer to attribute list. + * + * @return NC_NOERR No error. + * @author Dennis Heimbigner, Ed Hartnett + */ +static int +getattlist(NC_GRP_INFO_T *grp, int varid, NC_VAR_INFO_T **varp, + NCindex **attlist) +{ + int retval; + + assert(grp && attlist); + + if (varid == NC_GLOBAL) + { + /* Do we need to read the atts? */ + if (!grp->atts_read) + if ((retval = nc4_read_atts(grp, NULL))) + return retval; + + if (varp) + *varp = NULL; + *attlist = grp->att; + } + else + { + NC_VAR_INFO_T *var; + + if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid))) + return NC_ENOTVAR; + assert(var->hdr.id == varid); + + /* Do we need to read the atts? */ + if (!var->atts_read) + if ((retval = nc4_read_atts(grp, var))) + return retval; + + if (varp) + *varp = var; + *attlist = var->att; + } + return NC_NOERR; +} + +/** + * @internal Get one of three special attributes, NCPROPS, + * ISNETCDF4ATT, and SUPERBLOCKATT. These atts are not all really in + * the file, they are constructed on the fly. + * + * @param h5 Pointer to HDF5 file info struct. + * @param name Name of attribute. + * @param filetypep Pointer that gets type of the attribute data in + * file. + * @param mem_type Type of attribute data in memory. + * @param lenp Pointer that gets length of attribute array. + * @param attnump Pointer that gets the attribute number. + * @param data Attribute data. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_ERANGE Data conversion out of range. + * @author Dennis Heimbigner + */ +int +nc4_get_att_special(NC_FILE_INFO_T* h5, const char* name, + nc_type* filetypep, nc_type mem_type, size_t* lenp, + int* attnump, void* data) +{ + /* Fail if asking for att id */ + if(attnump) + return NC_EATTMETA; + + if(strcmp(name,NCPROPS)==0) { + int len; + if(h5->provenance.ncproperties == NULL) + return NC_ENOTATT; + if(mem_type == NC_NAT) mem_type = NC_CHAR; + if(mem_type != NC_CHAR) + return NC_ECHAR; + if(filetypep) *filetypep = NC_CHAR; + len = strlen(h5->provenance.ncproperties); + if(lenp) *lenp = len; + if(data) strncpy((char*)data,h5->provenance.ncproperties,len+1); + } else if(strcmp(name,ISNETCDF4ATT)==0 + || strcmp(name,SUPERBLOCKATT)==0) { + unsigned long long iv = 0; + if(filetypep) *filetypep = NC_INT; + if(lenp) *lenp = 1; + if(strcmp(name,SUPERBLOCKATT)==0) + iv = (unsigned long long)h5->provenance.superblockversion; + else /* strcmp(name,ISNETCDF4ATT)==0 */ + iv = NC4_isnetcdf4(h5); + if(mem_type == NC_NAT) mem_type = NC_INT; + if(data) + switch (mem_type) { + case NC_BYTE: *((char*)data) = (char)iv; break; + case NC_SHORT: *((short*)data) = (short)iv; break; + case NC_INT: *((int*)data) = (int)iv; break; + case NC_UBYTE: *((unsigned char*)data) = (unsigned char)iv; break; + case NC_USHORT: *((unsigned short*)data) = (unsigned short)iv; break; + case NC_UINT: *((unsigned int*)data) = (unsigned int)iv; break; + case NC_INT64: *((long long*)data) = (long long)iv; break; + case NC_UINT64: *((unsigned long long*)data) = (unsigned long long)iv; break; + default: + return NC_ERANGE; + } + } + return NC_NOERR; +} + +/** + * @internal I think all atts should be named the exact same thing, to + * avoid confusion! + * + * @param ncid File and group ID. + * @param varid Variable ID. + * @param name Name of attribute. + * @param newname New name for attribute. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_EMAXNAME New name too long. + * @return ::NC_EPERM File is read-only. + * @return ::NC_ENAMEINUSE New name already in use. + * @return ::NC_ENOTINDEFINE Classic model file not in define mode. + * @return ::NC_EHDFERR HDF error. + * @return ::NC_ENOMEM Out of memory. + * @return ::NC_EINTERNAL Could not rebuild list. + * @author Ed Hartnett + */ +int +NC4_HDF5_rename_att(int ncid, int varid, const char *name, const char *newname) +{ + NC_GRP_INFO_T *grp; + NC_FILE_INFO_T *h5; + NC_VAR_INFO_T *var = NULL; + NC_ATT_INFO_T *att; + NCindex *list; + char norm_newname[NC_MAX_NAME + 1], norm_name[NC_MAX_NAME + 1]; + hid_t datasetid = 0; + int retval = NC_NOERR; + + if (!name || !newname) + return NC_EINVAL; + + LOG((2, "nc_rename_att: ncid 0x%x varid %d name %s newname %s", + ncid, varid, name, newname)); + + /* If the new name is too long, that's an error. */ + if (strlen(newname) > NC_MAX_NAME) + return NC_EMAXNAME; + + /* Find info for this file, group, and h5 info. */ + if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) + return retval; + assert(h5 && grp); + + /* If the file is read-only, return an error. */ + if (h5->no_write) + return NC_EPERM; + + /* Check and normalize the name. */ + if ((retval = nc4_check_name(newname, norm_newname))) + return retval; + + /* Get the list of attributes. */ + if ((retval = getattlist(grp, varid, &var, &list))) + return retval; + + /* Is new name in use? */ + att = (NC_ATT_INFO_T*)ncindexlookup(list,norm_newname); + if(att != NULL) + return NC_ENAMEINUSE; + + /* Normalize name and find the attribute. */ + if ((retval = nc4_normalize_name(name, norm_name))) + return retval; + + att = (NC_ATT_INFO_T*)ncindexlookup(list,norm_name); + if (!att) + return NC_ENOTATT; + + /* If we're not in define mode, new name must be of equal or + less size, if complying with strict NC3 rules. */ + if (!(h5->flags & NC_INDEF) && strlen(norm_newname) > strlen(att->hdr.name) && + (h5->cmode & NC_CLASSIC_MODEL)) + return NC_ENOTINDEFINE; + + /* Delete the original attribute, if it exists in the HDF5 file. */ + if (att->created) + { + if (varid == NC_GLOBAL) + { + if (H5Adelete(((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid, + att->hdr.name) < 0) + return NC_EHDFERR; + } + else + { + if ((retval = nc4_open_var_grp2(grp, varid, &datasetid))) + return retval; + if (H5Adelete(datasetid, att->hdr.name) < 0) + return NC_EHDFERR; + } + att->created = NC_FALSE; + } + + /* Copy the new name into our metadata. */ + if(att->hdr.name) free(att->hdr.name); + if (!(att->hdr.name = strdup(norm_newname))) + return NC_ENOMEM; + att->hdr.hashkey = NC_hashmapkey(att->hdr.name,strlen(att->hdr.name)); /* Fix hash key */ + + att->dirty = NC_TRUE; + + /* Rehash the attribute list so that the new name is used */ + if(!ncindexrebuild(list)) + return NC_EINTERNAL; + + /* Mark attributes on variable dirty, so they get written */ + if(var) + var->attr_dirty = NC_TRUE; + + return retval; +} + +/** + * @internal Delete an att. Rub it out. Push the button on + * it. Liquidate it. Bump it off. Take it for a one-way + * ride. Terminate it. + * + * @param ncid File and group ID. + * @param varid Variable ID. + * @param name Name of attribute to delete. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_ENOTATT Attribute not found. + * @return ::NC_EINVAL No name provided. + * @return ::NC_EPERM File is read only. + * @return ::NC_ENOTINDEFINE Classic model not in define mode. + * @return ::NC_EINTERNAL Could not rebuild list. + * @author Ed Hartnett, Dennis Heimbigner + */ +int +NC4_HDF5_del_att(int ncid, int varid, const char *name) +{ + NC_GRP_INFO_T *grp; + NC_VAR_INFO_T *var; + NC_FILE_INFO_T *h5; + NC_ATT_INFO_T *att; + NCindex* attlist = NULL; + hid_t locid = 0; + int i; + size_t deletedid; + int retval; + + /* Name must be provided. */ + if (!name) + return NC_EINVAL; + + LOG((2, "nc_del_att: ncid 0x%x varid %d name %s", ncid, varid, name)); + + /* Find info for this file, group, and h5 info. */ + if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) + return retval; + assert(h5 && grp); + + /* If the file is read-only, return an error. */ + if (h5->no_write) + return NC_EPERM; + + /* If file is not in define mode, return error for classic model + * files, otherwise switch to define mode. */ + if (!(h5->flags & NC_INDEF)) + { + if (h5->cmode & NC_CLASSIC_MODEL) + return NC_ENOTINDEFINE; + if ((retval = NC4_redef(ncid))) + return retval; + } + + /* Get either the global or a variable attribute list. */ + if ((retval = getattlist(grp, varid, &var, &attlist))) + return retval; + + /* Determine the location id in the HDF5 file. */ + if (varid == NC_GLOBAL) + locid = ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid; + else if (var->created) + locid = ((NC_HDF5_VAR_INFO_T *)(var->format_var_info))->hdf_datasetid; + + /* Now find the attribute by name. */ + if (!(att = (NC_ATT_INFO_T*)ncindexlookup(attlist, name))) + return NC_ENOTATT; + + /* Delete it from the HDF5 file, if it's been created. */ + if (att->created) + { + assert(locid); + if (H5Adelete(locid, att->hdr.name) < 0) + return NC_EATTMETA; + } + + deletedid = att->hdr.id; + + /* Remove this attribute in this list */ + if ((retval = nc4_att_list_del(attlist, att))) + return retval; + + /* Renumber all attributes with higher indices. */ + for (i = 0; i < ncindexsize(attlist); i++) + { + NC_ATT_INFO_T *a; + if (!(a = (NC_ATT_INFO_T *)ncindexith(attlist, i))) + continue; + if (a->hdr.id > deletedid) + a->hdr.id--; + } + + /* Rebuild the index. */ + if (!ncindexrebuild(attlist)) + return NC_EINTERNAL; + + return NC_NOERR; +} + +/** + * @internal This will return the length of a netcdf atomic data type + * in bytes. + * + * @param type A netcdf atomic type. + * + * @return Type size in bytes, or -1 if type not found. + * @author Ed Hartnett + */ +static int +nc4typelen(nc_type type) +{ + switch(type){ + case NC_BYTE: + case NC_CHAR: + case NC_UBYTE: + return 1; + case NC_USHORT: + case NC_SHORT: + return 2; + case NC_FLOAT: + case NC_INT: + case NC_UINT: + return 4; + case NC_DOUBLE: + case NC_INT64: + case NC_UINT64: + return 8; + } + return -1; +} + +/** + * @internal + * Write an attribute to a netCDF-4/HDF5 file, converting + * data type if necessary. + * + * @param ncid File and group ID. + * @param varid Variable ID. + * @param name Name of attribute. + * @param file_type Type of the attribute data in file. + * @param len Number of elements in attribute array. + * @param data Attribute data. + * @param mem_type Type of data in memory. + * @param force write even if the attribute is special + * + * @return ::NC_NOERR No error. + * @return ::NC_EINVAL Invalid parameters. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_ENOTVAR Variable not found. + * @return ::NC_EBADNAME Name contains illegal characters. + * @return ::NC_ENAMEINUSE Name already in use. + * @author Ed Hartnett, Dennis Heimbigner + */ +int +nc4_put_att(NC_GRP_INFO_T* grp, int varid, const char *name, nc_type file_type, + size_t len, const void *data, nc_type mem_type, int force) +{ + NC* nc; + NC_FILE_INFO_T *h5; + NC_VAR_INFO_T *var = NULL; + NCindex* attlist = NULL; + NC_ATT_INFO_T* att; + char norm_name[NC_MAX_NAME + 1]; + nc_bool_t new_att = NC_FALSE; + int retval = NC_NOERR, range_error = 0; + size_t type_size; + int i; + int ret; + int ncid; + + h5 = grp->nc4_info; + nc = h5->controller; + assert(nc && grp && h5); + + ncid = nc->ext_ncid | grp->hdr.id; + + /* Find att, if it exists. (Must check varid first or nc_test will + * break.) This also does lazy att reads if needed. */ + if ((ret = getattlist(grp, varid, &var, &attlist))) + return ret; + + /* The length needs to be positive (cast needed for braindead + systems with signed size_t). */ + if((unsigned long) len > X_INT_MAX) + return NC_EINVAL; + + /* Check name before LOG statement. */ + if (!name || strlen(name) > NC_MAX_NAME) + return NC_EBADNAME; + + LOG((1, "%s: ncid 0x%x varid %d name %s file_type %d mem_type %d len %d", + __func__,ncid, varid, name, file_type, mem_type, len)); + + /* If len is not zero, then there must be some data. */ + if (len && !data) + return NC_EINVAL; + + /* If the file is read-only, return an error. */ + if (h5->no_write) + return NC_EPERM; + + /* Check and normalize the name. */ + if ((retval = nc4_check_name(name, norm_name))) + return retval; + + /* Check that a reserved att name is not being used improperly */ + const NC_reservedatt* ra = NC_findreserved(name); + if(ra != NULL && !force) { + /* case 1: grp=root, varid==NC_GLOBAL, flags & READONLYFLAG */ + if (nc->ext_ncid == ncid && varid == NC_GLOBAL && grp->parent == NULL + && (ra->flags & READONLYFLAG)) + return NC_ENAMEINUSE; + /* case 2: grp=NA, varid!=NC_GLOBAL, flags & DIMSCALEFLAG */ + if (varid != NC_GLOBAL && (ra->flags & DIMSCALEFLAG)) + return NC_ENAMEINUSE; + } + + /* See if there is already an attribute with this name. */ + att = (NC_ATT_INFO_T*)ncindexlookup(attlist,norm_name); + + if (!att) + { + /* If this is a new att, require define mode. */ + if (!(h5->flags & NC_INDEF)) + { + if (h5->cmode & NC_CLASSIC_MODEL) + return NC_ENOTINDEFINE; + if ((retval = NC4_redef(ncid))) + BAIL(retval); + } + new_att = NC_TRUE; + } + else + { + /* For an existing att, if we're not in define mode, the len + must not be greater than the existing len for classic model. */ + if (!(h5->flags & NC_INDEF) && + len * nc4typelen(file_type) > (size_t)att->len * nc4typelen(att->nc_typeid)) + { + if (h5->cmode & NC_CLASSIC_MODEL) + return NC_ENOTINDEFINE; + if ((retval = NC4_redef(ncid))) + BAIL(retval); + } + } + + /* We must have two valid types to continue. */ + if (file_type == NC_NAT || mem_type == NC_NAT) + return NC_EBADTYPE; + + /* Get information about this type. */ + if ((retval = nc4_get_typelen_mem(h5, file_type, &type_size))) + return retval; + + /* No character conversions are allowed. */ + if (file_type != mem_type && + (file_type == NC_CHAR || mem_type == NC_CHAR || + file_type == NC_STRING || mem_type == NC_STRING)) + return NC_ECHAR; + + /* For classic mode file, only allow atts with classic types to be + * created. */ + if (h5->cmode & NC_CLASSIC_MODEL && file_type > NC_DOUBLE) + return NC_ESTRICTNC3; + + /* Add to the end of the attribute list, if this att doesn't + already exist. */ + if (new_att) + { + LOG((3, "adding attribute %s to the list...", norm_name)); + if ((ret = nc4_att_list_add(attlist, norm_name, &att))) + BAIL(ret); + + /* Allocate storage for the HDF5 specific att info. */ + if (!(att->format_att_info = calloc(1, sizeof(NC_HDF5_ATT_INFO_T)))) + BAIL(NC_ENOMEM); + } + + /* Now fill in the metadata. */ + att->dirty = NC_TRUE; + att->nc_typeid = file_type; + + /* If this att has vlen or string data, release it before we lose the length value. */ + if (att->stdata) + { + for (i = 0; i < att->len; i++) + if(att->stdata[i]) + free(att->stdata[i]); + free(att->stdata); + att->stdata = NULL; + } + if (att->vldata) + { + for (i = 0; i < att->len; i++) + nc_free_vlen(&att->vldata[i]); /* FIX: see warning of nc_free_vlen */ + free(att->vldata); + att->vldata = NULL; + } + + att->len = len; + + /* If this is the _FillValue attribute, then we will also have to + * copy the value to the fill_vlue pointer of the NC_VAR_INFO_T + * struct for this var. (But ignore a global _FillValue + * attribute). */ + if (!strcmp(att->hdr.name, _FillValue) && varid != NC_GLOBAL) + { + int size; + + /* Fill value must be same type and have exactly one value */ + if (att->nc_typeid != var->type_info->hdr.id) + return NC_EBADTYPE; + if (att->len != 1) + return NC_EINVAL; + + /* If we already wrote to the dataset, then return an error. */ + if (var->written_to) + return NC_ELATEFILL; + + /* Get the length of the veriable data type. */ + if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->hdr.id, + &type_size))) + return retval; + + /* Already set a fill value? Now I'll have to free the old + * one. Make up your damn mind, would you? */ + if (var->fill_value) + { + if (var->type_info->nc_type_class == NC_VLEN) + { + if ((retval = nc_free_vlen(var->fill_value))) + return retval; + } + else if (var->type_info->nc_type_class == NC_STRING) + { + if (*(char **)var->fill_value) + free(*(char **)var->fill_value); + } + free(var->fill_value); + } + + /* Determine the size of the fill value in bytes. */ + if (var->type_info->nc_type_class == NC_VLEN) + size = sizeof(hvl_t); + else if (var->type_info->nc_type_class == NC_STRING) + size = sizeof(char *); + else + size = type_size; + + /* Allocate space for the fill value. */ + if (!(var->fill_value = calloc(1, size))) + return NC_ENOMEM; + + /* Copy the fill_value. */ + LOG((4, "Copying fill value into metadata for variable %s", var->hdr.name)); + if (var->type_info->nc_type_class == NC_VLEN) + { + nc_vlen_t *in_vlen = (nc_vlen_t *)data, *fv_vlen = (nc_vlen_t *)(var->fill_value); + NC_TYPE_INFO_T* basetype; + size_t basetypesize = 0; + + /* get the basetype and its size */ + basetype = var->type_info; + if ((retval = nc4_get_typelen_mem(grp->nc4_info, basetype->hdr.id, &basetypesize))) + return retval; + /* shallow clone the content of the vlen; shallow because it has only a temporary existence */ + fv_vlen->len = in_vlen->len; + if (!(fv_vlen->p = malloc(basetypesize * in_vlen->len))) + return NC_ENOMEM; + memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * basetypesize); + } + else if (var->type_info->nc_type_class == NC_STRING) + { + if (*(char **)data) + { + if (!(*(char **)(var->fill_value) = malloc(strlen(*(char **)data) + 1))) + return NC_ENOMEM; + strcpy(*(char **)var->fill_value, *(char **)data); + } + else + *(char **)var->fill_value = NULL; + } + else + memcpy(var->fill_value, data, type_size); + + /* Indicate that the fill value was changed, if the variable has already + * been created in the file, so the dataset gets deleted and re-created. */ + if (var->created) + var->fill_val_changed = NC_TRUE; + } + + /* Copy the attribute data, if there is any. VLENs and string + * arrays have to be handled specially. */ + if (att->len) + { + nc_type type_class; /* Class of attribute's type */ + + /* Get class for this type. */ + if ((retval = nc4_get_typeclass(h5, file_type, &type_class))) + return retval; + + assert(data); + if (type_class == NC_VLEN) + { + const hvl_t *vldata1; + NC_TYPE_INFO_T *vltype; + size_t base_typelen; + + /* Get the type object for the attribute's type */ + if ((retval = nc4_find_type(h5, file_type, &vltype))) + BAIL(retval); + + /* Retrieve the size of the base type */ + if ((retval = nc4_get_typelen_mem(h5, vltype->u.v.base_nc_typeid, &base_typelen))) + BAIL(retval); + + vldata1 = data; + if (!(att->vldata = (nc_vlen_t*)malloc(att->len * sizeof(hvl_t)))) + BAIL(NC_ENOMEM); + for (i = 0; i < att->len; i++) + { + att->vldata[i].len = vldata1[i].len; + /* Warning, this only works for cases described for nc_free_vlen() */ + if (!(att->vldata[i].p = malloc(base_typelen * att->vldata[i].len))) + BAIL(NC_ENOMEM); + memcpy(att->vldata[i].p, vldata1[i].p, base_typelen * att->vldata[i].len); + } + } + else if (type_class == NC_STRING) + { + LOG((4, "copying array of NC_STRING")); + if (!(att->stdata = malloc(sizeof(char *) * att->len))) { + BAIL(NC_ENOMEM); + } + + /* If we are overwriting an existing attribute, + specifically an NC_CHAR, we need to clean up + the pre-existing att->data. */ + if (!new_att && att->data) { + free(att->data); + att->data = NULL; + } + + for (i = 0; i < att->len; i++) + { + if(NULL != ((char **)data)[i]) { + LOG((5, "copying string %d of size %d", i, strlen(((char **)data)[i]) + 1)); + if (!(att->stdata[i] = strdup(((char **)data)[i]))) + BAIL(NC_ENOMEM); + } + else + att->stdata[i] = ((char **)data)[i]; + } + } + else + { + /* [Re]allocate memory for the attribute data */ + if (!new_att) + free (att->data); + if (!(att->data = malloc(att->len * type_size))) + BAIL(NC_ENOMEM); + + /* Just copy the data, for non-atomic types */ + if (type_class == NC_OPAQUE || type_class == NC_COMPOUND || type_class == NC_ENUM) + memcpy(att->data, data, len * type_size); + else + { + /* Data types are like religions, in that one can convert. */ + if ((retval = nc4_convert_type(data, att->data, mem_type, file_type, + len, &range_error, NULL, + (h5->cmode & NC_CLASSIC_MODEL)))) + BAIL(retval); + } + } + } + att->dirty = NC_TRUE; + att->created = NC_FALSE; + + /* Mark attributes on variable dirty, so they get written */ + if(var) + var->attr_dirty = NC_TRUE; + +exit: + /* If there was an error return it, otherwise return any potential + range error value. If none, return NC_NOERR as usual.*/ + if (retval) + return retval; + if (range_error) + return NC_ERANGE; + return NC_NOERR; +} + +/** + * @internal Write an attribute to a netCDF-4/HDF5 file, converting + * data type if necessary. + * + * @param ncid File and group ID. + * @param varid Variable ID. + * @param name Name of attribute. + * @param file_type Type of the attribute data in file. + * @param len Number of elements in attribute array. + * @param data Attribute data. + * @param mem_type Type of data in memory. + * + * @return ::NC_NOERR No error. + * @return ::NC_EINVAL Invalid parameters. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_ENOTVAR Variable not found. + * @return ::NC_EBADNAME Name contains illegal characters. + * @return ::NC_ENAMEINUSE Name already in use. + * @author Ed Hartnett, Dennis Heimbigner + */ +int +NC4_HDF5_put_att(int ncid, int varid, const char *name, nc_type file_type, + size_t len, const void *data, nc_type mem_type) +{ + NC_FILE_INFO_T *h5; + NC_GRP_INFO_T *grp; + int ret; + + /* Find info for this file, group, and h5 info. */ + if ((ret = nc4_find_nc_grp_h5(ncid, NULL, &grp, &h5))) + return ret; + assert(grp && h5); + + return nc4_put_att(grp, varid, name, file_type, len, data, mem_type, 0); +} + +/** + * @internal Learn about an att. All the nc4 nc_inq_ functions just + * call nc4_get_att to get the metadata on an attribute. + * + * @param ncid File and group ID. + * @param varid Variable ID. + * @param name Name of attribute. + * @param xtypep Pointer that gets type of attribute. + * @param lenp Pointer that gets length of attribute data array. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @author Ed Hartnett + */ +int +NC4_HDF5_inq_att(int ncid, int varid, const char *name, nc_type *xtypep, + size_t *lenp) +{ + NC_FILE_INFO_T *h5; + NC_GRP_INFO_T *grp; + NC_VAR_INFO_T *var = NULL; + char norm_name[NC_MAX_NAME + 1]; + int retval; + + LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); + + /* Find the file, group, and var info, and do lazy att read if + * needed. */ + if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, name, 0, 1, norm_name, + &h5, &grp, &var, NULL))) + return retval; + + /* If this is one of the reserved atts, use nc_get_att_special. */ + if (!var) + { + const NC_reservedatt *ra = NC_findreserved(norm_name); + if (ra && ra->flags & NAMEONLYFLAG) + return nc4_get_att_special(h5, norm_name, xtypep, NC_NAT, lenp, NULL, + NULL); + } + + return nc4_get_att_ptrs(h5, grp, var, norm_name, xtypep, NC_NAT, + lenp, NULL, NULL); +} + +/** + * @internal Learn an attnum, given a name. + * + * @param ncid File and group ID. + * @param varid Variable ID. + * @param name Name of attribute. + * @param attnump Pointer that gets the attribute index number. + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +int +NC4_HDF5_inq_attid(int ncid, int varid, const char *name, int *attnump) +{ + NC_FILE_INFO_T *h5; + NC_GRP_INFO_T *grp; + NC_VAR_INFO_T *var = NULL; + char norm_name[NC_MAX_NAME + 1]; + int retval; + + LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); + + /* Find the file, group, and var info, and do lazy att read if + * needed. */ + if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, name, 0, 1, norm_name, + &h5, &grp, &var, NULL))) + return retval; + + /* If this is one of the reserved atts, use nc_get_att_special. */ + if (!var) + { + const NC_reservedatt *ra = NC_findreserved(norm_name); + if (ra && ra->flags & NAMEONLYFLAG) + return nc4_get_att_special(h5, norm_name, NULL, NC_NAT, NULL, attnump, + NULL); + } + + return nc4_get_att_ptrs(h5, grp, var, norm_name, NULL, NC_NAT, + NULL, attnump, NULL); +} + +/** + * @internal Given an attnum, find the att's name. + * + * @param ncid File and group ID. + * @param varid Variable ID. + * @param attnum The index number of the attribute. + * @param name Pointer that gets name of attrribute. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @author Ed Hartnett + */ +int +NC4_HDF5_inq_attname(int ncid, int varid, int attnum, char *name) +{ + NC_ATT_INFO_T *att; + int retval; + + LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); + + /* Find the file, group, and var info, and do lazy att read if + * needed. */ + if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, NULL, attnum, 0, NULL, + NULL, NULL, NULL, &att))) + return retval; + assert(att); + + /* Get the name. */ + if (name) + strcpy(name, att->hdr.name); + + return NC_NOERR; +} + +/** + * @internal Get an attribute. + * + * @param ncid File and group ID. + * @param varid Variable ID. + * @param name Name of attribute. + * @param value Pointer that gets attribute data. + * @param memtype The type the data should be converted to as it is + * read. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @author Ed Hartnett + */ +int +NC4_HDF5_get_att(int ncid, int varid, const char *name, void *value, + nc_type memtype) +{ + NC_FILE_INFO_T *h5; + NC_GRP_INFO_T *grp; + NC_VAR_INFO_T *var = NULL; + char norm_name[NC_MAX_NAME + 1]; + int retval; + + LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); + + /* Find the file, group, and var info, and do lazy att read if + * needed. */ + if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, name, 0, 1, norm_name, + &h5, &grp, &var, NULL))) + return retval; + + /* If this is one of the reserved atts, use nc_get_att_special. */ + if (!var) + { + const NC_reservedatt *ra = NC_findreserved(norm_name); + if (ra && ra->flags & NAMEONLYFLAG) + return nc4_get_att_special(h5, norm_name, NULL, NC_NAT, NULL, NULL, + value); + } + + return nc4_get_att_ptrs(h5, grp, var, norm_name, NULL, memtype, + NULL, NULL, value); +} diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5cache.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5cache.c new file mode 100644 index 0000000000000000000000000000000000000000..5eca998a88bc1fabbeeb7ae724ddc8d3d2a792cf --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5cache.c @@ -0,0 +1,113 @@ +/* Copyright 2018, University Corporation for Atmospheric + * Research. See COPYRIGHT file for copying and redistribution + * conditions. */ +/** + * @file @internal The netCDF-4 functions which control HDF5 + * caching. These caching controls allow the user to change the cache + * sizes of HDF5 before opening files. + * + * @author Ed Hartnett + */ + +#include "config.h" +#include "hdf5internal.h" + +/* 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; + +/** + * Set chunk cache size. Only affects files opened/created *after* it + * is called. + * + * @param size Size in bytes to set cache. + * @param nelems Number of elements to hold in cache. + * @param preemption Premption stragety (between 0 and 1). + * + * @return ::NC_NOERR No error. + * @return ::NC_EINVAL Bad preemption. + * @author Ed Hartnett + */ +int +nc_set_chunk_cache(size_t size, size_t nelems, float preemption) +{ + if (preemption < 0 || preemption > 1) + return NC_EINVAL; + nc4_chunk_cache_size = size; + nc4_chunk_cache_nelems = nelems; + nc4_chunk_cache_preemption = preemption; + return NC_NOERR; +} + +/** + * Get chunk cache size. Only affects files opened/created *after* it + * is called. + * + * @param sizep Pointer that gets size in bytes to set cache. + * @param nelemsp Pointer that gets number of elements to hold in cache. + * @param preemptionp Pointer that gets premption stragety (between 0 and 1). + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +int +nc_get_chunk_cache(size_t *sizep, size_t *nelemsp, float *preemptionp) +{ + if (sizep) + *sizep = nc4_chunk_cache_size; + + if (nelemsp) + *nelemsp = nc4_chunk_cache_nelems; + + if (preemptionp) + *preemptionp = nc4_chunk_cache_preemption; + return NC_NOERR; +} + +/** + * @internal Set the chunk cache. Required for fortran to avoid size_t + * issues. + * + * @param size Cache size. + * @param nelems Number of elements. + * @param preemption Preemption * 100. + * + * @return NC_NOERR No error. + * @author Ed Hartnett + */ +int +nc_set_chunk_cache_ints(int size, int nelems, int preemption) +{ + if (size <= 0 || nelems <= 0 || preemption < 0 || preemption > 100) + return NC_EINVAL; + nc4_chunk_cache_size = size; + nc4_chunk_cache_nelems = nelems; + nc4_chunk_cache_preemption = (float)preemption / 100; + return NC_NOERR; +} + +/** + * @internal Get the chunk cache settings. Required for fortran to + * avoid size_t issues. + * + * @param sizep Pointer that gets cache size. + * @param nelemsp Pointer that gets number of elements. + * @param preemptionp Pointer that gets preemption * 100. + * + * @return NC_NOERR No error. + * @author Ed Hartnett + */ +int +nc_get_chunk_cache_ints(int *sizep, int *nelemsp, int *preemptionp) +{ + if (sizep) + *sizep = (int)nc4_chunk_cache_size; + if (nelemsp) + *nelemsp = (int)nc4_chunk_cache_nelems; + if (preemptionp) + *preemptionp = (int)(nc4_chunk_cache_preemption * 100); + + return NC_NOERR; +} diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5create.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5create.c new file mode 100644 index 0000000000000000000000000000000000000000..268c8a321bd6e529ad884ddb5ea6978973849762 --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5create.c @@ -0,0 +1,305 @@ +/* Copyright 2003-2018, University Corporation for Atmospheric + * Research. See COPYRIGHT file for copying and redistribution + * conditions. */ +/** + * @file + * @internal The netCDF-4 file functions relating to file creation. + * + * @author Ed Hartnett + */ + +#include "config.h" +#include "hdf5internal.h" + +/* From hdf5file.c. */ +extern size_t nc4_chunk_cache_size; +extern size_t nc4_chunk_cache_nelems; +extern float nc4_chunk_cache_preemption; + +/** @internal These flags may not be set for create. */ +static const int ILLEGAL_CREATE_FLAGS = (NC_NOWRITE|NC_MMAP|NC_64BIT_OFFSET|NC_CDF5); + +/* From nc4mem.c */ +extern int NC4_create_image_file(NC_FILE_INFO_T* h5, size_t); + +/** + * @internal Create a netCDF-4/HDF5 file. + * + * @param path The file name of the new file. + * @param cmode The creation mode flag. + * @param initialsz The proposed initial file size (advisory) + * @param parameters extra parameter info (like MPI communicator) + * @param nc Pointer to an instance of NC. + * + * @return ::NC_NOERR No error. + * @return ::NC_EINVAL Invalid input (check cmode). + * @return ::NC_EEXIST File exists and NC_NOCLOBBER used. + * @return ::NC_EHDFERR HDF5 returned error. + * @ingroup netcdf4 + * @author Ed Hartnett, Dennis Heimbigner + */ +static int +nc4_create_file(const char *path, int cmode, size_t initialsz, + void* parameters, NC *nc) +{ + hid_t fcpl_id, fapl_id = -1; + unsigned flags; + FILE *fp; + int retval = NC_NOERR; + NC_FILE_INFO_T* nc4_info = NULL; + NC_HDF5_FILE_INFO_T *hdf5_info; + NC_HDF5_GRP_INFO_T *hdf5_grp; + +#ifdef USE_PARALLEL4 + NC_MPI_INFO *mpiinfo = NULL; + MPI_Comm comm; + MPI_Info info; + int comm_duped = 0; /* Whether the MPI Communicator was duplicated */ + int info_duped = 0; /* Whether the MPI Info object was duplicated */ +#endif /* !USE_PARALLEL4 */ + + assert(nc && path); + LOG((3, "%s: path %s mode 0x%x", __func__, path, cmode)); + + /* Add necessary structs to hold netcdf-4 file data. */ + if ((retval = nc4_nc4f_list_add(nc, path, (NC_WRITE | cmode)))) + BAIL(retval); + nc4_info = NC4_DATA(nc); + assert(nc4_info && nc4_info->root_grp); + nc4_info->root_grp->atts_read = 1; + + /* Add struct to hold HDF5-specific file metadata. */ + if (!(nc4_info->format_file_info = calloc(1, sizeof(NC_HDF5_FILE_INFO_T)))) + BAIL(NC_ENOMEM); + hdf5_info = (NC_HDF5_FILE_INFO_T *)nc4_info->format_file_info; + + /* Add struct to hold HDF5-specific group info. */ + if (!(nc4_info->root_grp->format_grp_info = calloc(1, sizeof(NC_HDF5_GRP_INFO_T)))) + return NC_ENOMEM; + hdf5_grp = (NC_HDF5_GRP_INFO_T *)nc4_info->root_grp->format_grp_info; + + nc4_info->mem.inmemory = (cmode & NC_INMEMORY) == NC_INMEMORY; + nc4_info->mem.diskless = (cmode & NC_DISKLESS) == NC_DISKLESS; + nc4_info->mem.persist = (cmode & NC_PERSIST) == NC_PERSIST; + nc4_info->mem.created = 1; + nc4_info->mem.initialsize = initialsz; + + /* diskless => !inmemory */ + if(nc4_info->mem.inmemory && nc4_info->mem.diskless) + BAIL(NC_EINTERNAL); + + if(nc4_info->mem.inmemory && parameters) + nc4_info->mem.memio = *(NC_memio*)parameters; +#ifdef USE_PARALLEL4 + else if(parameters) { + mpiinfo = (NC_MPI_INFO *)parameters; + comm = mpiinfo->comm; + info = mpiinfo->info; + } +#endif + if(nc4_info->mem.diskless) + flags = H5F_ACC_TRUNC; + else if(cmode & NC_NOCLOBBER) + flags = H5F_ACC_EXCL; + else + flags = H5F_ACC_TRUNC; + + /* If this file already exists, and NC_NOCLOBBER is specified, + return an error (unless diskless|inmemory) */ + if (!nc4_info->mem.diskless && !nc4_info->mem.inmemory) { + if ((cmode & NC_NOCLOBBER) && (fp = fopen(path, "r"))) { + fclose(fp); + BAIL(NC_EEXIST); + } + } + + /* Need this access plist to control how HDF5 handles open objects + * on file close. Setting H5F_CLOSE_SEMI will cause H5Fclose to + * fail if there are any open objects in the file. */ + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + BAIL(NC_EHDFERR); + if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_SEMI)) + BAIL(NC_EHDFERR); + +#ifdef USE_PARALLEL4 + /* If this is a parallel file create, set up the file creation + property list. */ + if (mpiinfo != NULL) { + nc4_info->parallel = NC_TRUE; + LOG((4, "creating parallel file with MPI/IO")); + if (H5Pset_fapl_mpio(fapl_id, comm, info) < 0) + BAIL(NC_EPARINIT); + + /* Keep copies of the MPI Comm & Info objects */ + if (MPI_SUCCESS != MPI_Comm_dup(comm, &nc4_info->comm)) + BAIL(NC_EMPI); + comm_duped++; + if (MPI_INFO_NULL != info) + { + if (MPI_SUCCESS != MPI_Info_dup(info, &nc4_info->info)) + BAIL(NC_EMPI); + info_duped++; + } + else + { + /* No dup, just copy it. */ + nc4_info->info = info; + } + } +#else /* only set cache for non-parallel... */ + if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size, + nc4_chunk_cache_preemption) < 0) + BAIL(NC_EHDFERR); + LOG((4, "%s: set HDF raw chunk cache to size %d nelems %d preemption %f", + __func__, nc4_chunk_cache_size, nc4_chunk_cache_nelems, + nc4_chunk_cache_preemption)); +#endif /* USE_PARALLEL4 */ + +#ifdef HDF5_HAS_LIBVER_BOUNDS +#if H5_VERSION_GE(1,10,2) + if (H5Pset_libver_bounds(fapl_id, H5F_LIBVER_EARLIEST, H5F_LIBVER_V18) < 0) +#else + if (H5Pset_libver_bounds(fapl_id, H5F_LIBVER_EARLIEST, + H5F_LIBVER_LATEST) < 0) +#endif + BAIL(NC_EHDFERR); +#endif + + /* Create the property list. */ + if ((fcpl_id = H5Pcreate(H5P_FILE_CREATE)) < 0) + BAIL(NC_EHDFERR); + + /* RJ: this suppose to be FALSE that is defined in H5 private.h as 0 */ + if (H5Pset_obj_track_times(fcpl_id,0)<0) + BAIL(NC_EHDFERR); + + /* Set latest_format in access propertly list and + * H5P_CRT_ORDER_TRACKED in the creation property list. This turns + * on HDF5 creation ordering. */ + if (H5Pset_link_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED | + H5P_CRT_ORDER_INDEXED)) < 0) + BAIL(NC_EHDFERR); + if (H5Pset_attr_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED | + H5P_CRT_ORDER_INDEXED)) < 0) + BAIL(NC_EHDFERR); + +#ifdef HDF5_HAS_COLL_METADATA_OPS + /* If HDF5 supports collective metadata operations, turn them + * on. This is only relevant for parallel I/O builds of HDF5. */ + if (H5Pset_all_coll_metadata_ops(fapl_id, 1) < 0) + BAIL(NC_EHDFERR); + if (H5Pset_coll_metadata_write(fapl_id, 1) < 0) + BAIL(NC_EHDFERR); +#endif + + if(nc4_info->mem.inmemory) { + retval = NC4_create_image_file(nc4_info,initialsz); + if(retval) + BAIL(retval); + } + else + if(nc4_info->mem.diskless) { + size_t alloc_incr; /* Buffer allocation increment */ + size_t min_incr = 65536; /* Minimum buffer increment */ + double buf_prcnt = 0.1f; /* Percentage of buffer size to set as increment */ + /* set allocation increment to a percentage of the supplied buffer size, or + * a pre-defined minimum increment value, whichever is larger + */ + if ((buf_prcnt * initialsz) > min_incr) + alloc_incr = (size_t)(buf_prcnt * initialsz); + else + alloc_incr = min_incr; + /* Configure FAPL to use the core file driver */ + if (H5Pset_fapl_core(fapl_id, alloc_incr, (nc4_info->mem.persist?1:0)) < 0) + BAIL(NC_EHDFERR); + if ((hdf5_info->hdfid = H5Fcreate(path, flags, fcpl_id, fapl_id)) < 0) + BAIL(EACCES); + } + else /* Normal file */ + { + /* Create the HDF5 file. */ + if ((hdf5_info->hdfid = H5Fcreate(path, flags, fcpl_id, fapl_id)) < 0) + BAIL(EACCES); + } + + /* Open the root group. */ + if ((hdf5_grp->hdf_grpid = H5Gopen2(hdf5_info->hdfid, "/", H5P_DEFAULT)) < 0) + BAIL(NC_EFILEMETA); + + /* Release the property lists. */ + if (H5Pclose(fapl_id) < 0 || H5Pclose(fcpl_id) < 0) + BAIL(NC_EHDFERR); + + /* Define mode gets turned on automatically on create. */ + nc4_info->flags |= NC_INDEF; + + /* Save the HDF5 superblock number and set the _NCProperties attribute. */ + if ((retval = NC4_new_provenance(nc4_info))) + BAIL(retval); + + return NC_NOERR; + +exit: /*failure exit*/ +#ifdef USE_PARALLEL4 + if (comm_duped) MPI_Comm_free(&nc4_info->comm); + if (info_duped) MPI_Info_free(&nc4_info->info); +#endif + if (fapl_id != H5P_DEFAULT) H5Pclose(fapl_id); + if(!nc4_info) return retval; + nc4_close_hdf5_file(nc4_info, 1, NULL); /* treat like abort */ + return retval; +} + +/** + * @internal Create a netCDF-4/HDF5 file. + * + * @param path The file name of the new file. + * @param cmode The creation mode flag. + * @param initialsz Ignored by this function. + * @param basepe Ignored by this function. + * @param chunksizehintp Ignored by this function. + * @param parameters pointer to struct holding extra data (e.g. for + * parallel I/O) layer. Ignored if NULL. + * @param dispatch Pointer to the dispatch table for this file. + * @param nc_file Pointer to an instance of NC. + * + * @return ::NC_NOERR No error. + * @return ::NC_EINVAL Invalid input (check cmode). + * @ingroup netcdf4 + * @author Ed Hartnett + */ +int +NC4_create(const char* path, int cmode, size_t initialsz, int basepe, + size_t *chunksizehintp, void *parameters, + NC_Dispatch *dispatch, NC *nc_file) +{ + int res; + + assert(nc_file && path); + + LOG((1, "%s: path %s cmode 0x%x parameters %p", + __func__, path, cmode, parameters)); + + /* If this is our first file, turn off HDF5 error messages. */ + if (!nc4_hdf5_initialized) + nc4_hdf5_initialize(); + +#ifdef LOGGING + /* If nc logging level has changed, see if we need to turn on + * HDF5's error messages. */ + hdf5_set_log_level(); +#endif /* LOGGING */ + + /* Check the cmode for validity. */ + if((cmode & ILLEGAL_CREATE_FLAGS) != 0) + {res = NC_EINVAL; goto done;} + + /* check parallel against NC_DISKLESS already done in NC_create() */ + + nc_file->int_ncid = nc_file->ext_ncid; + + res = nc4_create_file(path, cmode, initialsz, parameters, nc_file); + +done: + return res; +} diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5dim.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5dim.c new file mode 100644 index 0000000000000000000000000000000000000000..a184f99251aa8b9c8704f62a4ae59f22c238824b --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5dim.c @@ -0,0 +1,308 @@ +/* Copyright 2003-2019, 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 includes the HDF5 code to deal with dimensions. + * + * @author Ed Hartnett + */ + +#include "config.h" +#include "hdf5internal.h" + +/** + * @internal Dimensions are defined in attributes attached to the + * appropriate group in the data file. + * + * @param ncid File and group ID. + * @param name Name of the new dimension. + * @param len Length of the new dimension. + * @param idp Pointer that gets the ID of the new dimension. Ignored + * if NULL. + * + * @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. + * @return ::NC_EINVAL Invalid input. + * @return ::NC_EPERM Read-only file. + * @return ::NC_EUNLIMIT Only one unlimited dim for classic model. + * @return ::NC_ENOTINDEFINE Not in define mode. + * @return ::NC_EDIMSIZE Dim length too large. + * @return ::NC_ENAMEINUSE Name already in use in group. + * @return ::NC_ENOMEM Out of memory. + * @author Ed Hartnett + */ +int +NC4_def_dim(int ncid, const char *name, size_t len, int *idp) +{ + NC *nc; + NC_GRP_INFO_T *grp; + NC_FILE_INFO_T *h5; + NC_DIM_INFO_T *dim; + char norm_name[NC_MAX_NAME + 1]; + int retval = NC_NOERR; + int i; + + LOG((2, "%s: ncid 0x%x name %s len %d", __func__, ncid, name, + (int)len)); + + /* Find our global metadata structure. */ + if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) + return retval; + assert(h5 && nc && grp); + + /* If the file is read-only, return an error. */ + if (h5->no_write) + return NC_EPERM; + + /* Check some stuff if strict nc3 rules are in effect. */ + if (h5->cmode & NC_CLASSIC_MODEL) + { + /* Only one limited dimenson for strict nc3. */ + if (len == NC_UNLIMITED) { + for(i=0;i<ncindexsize(grp->dim);i++) { + dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i); + if(dim == NULL) continue; + if (dim->unlimited) + return NC_EUNLIMIT; + } + } + /* Must be in define mode for stict nc3. */ + if (!(h5->flags & NC_INDEF)) + return NC_ENOTINDEFINE; + } + + /* Make sure this is a valid netcdf name. */ + if ((retval = nc4_check_name(name, norm_name))) + return retval; + + /* For classic model: dim length has to fit in a 32-bit unsigned + * int, as permitted for 64-bit offset format. */ + if (h5->cmode & NC_CLASSIC_MODEL) + if(len > X_UINT_MAX) /* Backward compat */ + return NC_EDIMSIZE; + + /* Make sure the name is not already in use. */ + dim = (NC_DIM_INFO_T*)ncindexlookup(grp->dim,norm_name); + if(dim != NULL) + return NC_ENAMEINUSE; + + /* If it's not in define mode, enter define mode. Do this only + * after checking all input data, so we only enter define mode if + * input is good. */ + if (!(h5->flags & NC_INDEF)) + if ((retval = NC4_redef(ncid))) + return retval; + + /* Add a dimension to the list. The ID must come from the file + * information, since dimids are visible in more than one group. */ + if ((retval = nc4_dim_list_add(grp, norm_name, len, -1, &dim))) + return retval; + + /* Create struct for HDF5-specific dim info. */ + if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T)))) + return NC_ENOMEM; + + /* Pass back the dimid. */ + if (idp) + *idp = dim->hdr.id; + + return retval; +} + +/** + * @internal Find out name and len of a dim. For an unlimited + * dimension, the length is the largest length so far written. If the + * name of lenp pointers are NULL, they will be ignored. + * + * @param ncid File and group ID. + * @param dimid Dimension ID. + * @param name Pointer that gets name of the dimension. + * @param lenp Pointer that gets length of dimension. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_EDIMSIZE Dimension length too large. + * @return ::NC_EBADDIM Dimension not found. + * @author Ed Hartnett + */ +int +NC4_inq_dim(int ncid, int dimid, char *name, size_t *lenp) +{ + NC *nc; + NC_FILE_INFO_T *h5; + NC_GRP_INFO_T *grp, *dim_grp; + NC_DIM_INFO_T *dim; + int ret = NC_NOERR; + + LOG((2, "%s: ncid 0x%x dimid %d", __func__, ncid, dimid)); + + /* Find our global metadata structure. */ + if ((ret = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) + return ret; + assert(h5 && nc && grp); + + /* Find the dimension and its home group. */ + if ((ret = nc4_find_dim(grp, dimid, &dim, &dim_grp))) + return ret; + assert(dim); + + /* Return the dimension name, if the caller wants it. */ + if (name && dim->hdr.name) + strcpy(name, dim->hdr.name); + + /* Return the dimension length, if the caller wants it. */ + if (lenp) + { + if (dim->unlimited) + { + /* Since this is an unlimited dimension, go to the file + and see how many records there are. Take the max number + of records from all the vars that share this + dimension. */ + *lenp = 0; + if ((ret = nc4_find_dim_len(dim_grp, dimid, &lenp))) + return ret; + } + else + { + if (dim->too_long) + { + ret = NC_EDIMSIZE; + *lenp = NC_MAX_UINT; + } + else + *lenp = dim->len; + } + } + + return ret; +} + +/** + * @internal Rename a dimension, for those who like to prevaricate. + * + * @note If we're not in define mode, new name must be of equal or + * less size, if strict nc3 rules are in effect for this file. But we + * don't check this because reproducing the exact classic behavior + * would be too difficult. See github issue #1340. + * + * @param ncid File and group ID. + * @param dimid Dimension ID. + * @param name New dimension name. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_EHDFERR HDF5 returned error. + * @return ::NC_ENOMEM Out of memory. + * @return ::NC_EINVAL Name must be provided. + * @return ::NC_ENAMEINUSE Name is already in use in group. + * @return ::NC_EMAXNAME Name is too long. + * @return ::NC_EBADDIM Dimension not found. + * @return ::NC_EBADNAME Name breaks netCDF name rules. + * @return ::NC_EDIMMETA Unable to delete HDF5 dataset. + * @author Ed Hartnett + */ +int +NC4_rename_dim(int ncid, int dimid, const char *name) +{ + NC_GRP_INFO_T *grp; + NC_DIM_INFO_T *dim; + NC_HDF5_DIM_INFO_T *hdf5_dim; + NC_FILE_INFO_T *h5; + char norm_name[NC_MAX_NAME + 1]; + int retval; + + /* Note: name is new name */ + if (!name) + return NC_EINVAL; + + LOG((2, "%s: ncid 0x%x dimid %d name %s", __func__, ncid, + dimid, name)); + + /* 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); + + /* Trying to write to a read-only file? No way, Jose! */ + if (h5->no_write) + return NC_EPERM; + + /* Make sure this is a valid netcdf name. */ + if ((retval = nc4_check_name(name, norm_name))) + return retval; + + /* Get the original dim. */ + if ((retval = nc4_find_dim(grp, dimid, &dim, NULL))) + return retval; + assert(dim && dim->format_dim_info); + hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; + + /* Check if new name is in use. */ + if (ncindexlookup(grp->dim, norm_name)) + return NC_ENAMEINUSE; + + /* Check for renaming dimension w/o variable. */ + if (hdf5_dim->hdf_dimscaleid) + { + assert(!dim->coord_var); + LOG((3, "dim %s is a dim without variable", dim->hdr.name)); + + /* Delete the dimscale-only dataset. */ + if ((retval = delete_dimscale_dataset(grp, dimid, dim))) + return retval; + } + + /* Give the dimension its new name in metadata. UTF8 normalization + * has been done. */ + assert(dim->hdr.name); + free(dim->hdr.name); + if (!(dim->hdr.name = strdup(norm_name))) + return NC_ENOMEM; + LOG((3, "dim is now named %s", dim->hdr.name)); + + /* Fix hash key and rebuild index. */ + dim->hdr.hashkey = NC_hashmapkey(dim->hdr.name,strlen(dim->hdr.name)); + if (!ncindexrebuild(grp->dim)) + return NC_EINTERNAL; + + /* Check if dimension was a coordinate variable, but names are + * different now. */ + if (dim->coord_var && strcmp(dim->hdr.name, dim->coord_var->hdr.name)) + { + /* Break up the coordinate variable. */ + if ((retval = nc4_break_coord_var(grp, dim->coord_var, dim))) + return retval; + } + + /* Check if dimension should become a coordinate variable. */ + if (!dim->coord_var) + { + NC_VAR_INFO_T *var; + + /* Attempt to find a variable with the same name as the + * dimension in the current group. */ + if ((retval = nc4_find_var(grp, dim->hdr.name, &var))) + return retval; + + /* Check if we found a variable and the variable has the + * dimension in index 0. */ + if (var && var->dim[0] == dim) + { + /* Sanity check */ + assert(var->dimids[0] == dim->hdr.id); + + /* Reform the coordinate variable. */ + if ((retval = nc4_reform_coord_var(grp, var, dim))) + return retval; + } + } + + return NC_NOERR; +} diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5dispatch.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5dispatch.c new file mode 100644 index 0000000000000000000000000000000000000000..eec6fdb9e4b8d61ad2bf8be383df5f8ad4827433 --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5dispatch.c @@ -0,0 +1,144 @@ +/* Copyright 2005-2018 University Corporation for Atmospheric + Research/Unidata. */ +/** + * @file + * @internal This header file contains prototypes and initialization + * for the HDF5 dispatch layer. + * + * @author Ed Hartnett, Dennis Heimbigner + */ + +#include "config.h" +#include "hdf5internal.h" + +#ifdef ENABLE_BYTERANGE +#include "H5FDhttp.h" +#endif + +static NC_Dispatch NC4_dispatcher = { + + NC_FORMATX_NC4, + + NC4_create, + NC4_open, + + NC4_redef, + NC4__enddef, + NC4_sync, + NC4_abort, + NC4_close, + NC4_set_fill, + NC_NOTNC3_inq_base_pe, + NC_NOTNC3_set_base_pe, + NC4_inq_format, + NC4_inq_format_extended, + + NC4_inq, + NC4_inq_type, + + NC4_def_dim, + NC4_inq_dimid, + NC4_inq_dim, + NC4_inq_unlimdim, + NC4_rename_dim, + + NC4_HDF5_inq_att, + NC4_HDF5_inq_attid, + NC4_HDF5_inq_attname, + NC4_HDF5_rename_att, + NC4_HDF5_del_att, + NC4_HDF5_get_att, + NC4_HDF5_put_att, + + NC4_def_var, + NC4_inq_varid, + NC4_rename_var, + NC4_get_vara, + NC4_put_vara, + NC4_get_vars, + NC4_put_vars, + NCDEFAULT_get_varm, + NCDEFAULT_put_varm, + + NC4_HDF5_inq_var_all, + + NC4_var_par_access, + NC4_def_var_fill, + +#ifdef USE_NETCDF4 + NC4_show_metadata, + NC4_inq_unlimdims, + + NC4_inq_ncid, + NC4_inq_grps, + NC4_inq_grpname, + NC4_inq_grpname_full, + NC4_inq_grp_parent, + NC4_inq_grp_full_ncid, + NC4_inq_varids, + NC4_inq_dimids, + NC4_inq_typeids, + NC4_inq_type_equal, + NC4_def_grp, + NC4_rename_grp, + NC4_inq_user_type, + NC4_inq_typeid, + + NC4_def_compound, + NC4_insert_compound, + NC4_insert_array_compound, + NC4_inq_compound_field, + NC4_inq_compound_fieldindex, + NC4_def_vlen, + NC4_put_vlen_element, + NC4_get_vlen_element, + NC4_def_enum, + NC4_insert_enum, + NC4_inq_enum_member, + NC4_inq_enum_ident, + NC4_def_opaque, + NC4_def_var_deflate, + NC4_def_var_fletcher32, + NC4_def_var_chunking, + NC4_def_var_endian, + NC4_def_var_filter, + NC4_HDF5_set_var_chunk_cache, + NC4_get_var_chunk_cache, +#endif + +}; + +NC_Dispatch* HDF5_dispatch_table = NULL; /* moved here from ddispatch.c */ + +/** + * @internal Initialize the HDF5 dispatch layer. + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +int +NC_HDF5_initialize(void) +{ + HDF5_dispatch_table = &NC4_dispatcher; + + if (!nc4_hdf5_initialized) + nc4_hdf5_initialize(); + +#ifdef ENABLE_BYTERANGE + (void)H5FD_http_init(); +#endif + return NC4_provenance_init(); +} + +/** + * @internal Finalize the HDF5 dispatch layer. + * + * @return ::NC_NOERR No error. + * @author Dennis Heimbigner + */ +int +NC_HDF5_finalize(void) +{ + (void)nc4_hdf5_finalize(); + return NC_NOERR; +} diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5file.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5file.c new file mode 100644 index 0000000000000000000000000000000000000000..1b110a2812f3340c5af8e8c8beb9b6fb2d864bfc --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5file.c @@ -0,0 +1,783 @@ +/* Copyright 2003-2018, University Corporation for Atmospheric + * Research. See COPYRIGHT file for copying and redistribution + * conditions. */ +/** + * @file + * @internal The netCDF-4 file functions. + * + * 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. + * + * @author Ed Hartnett + */ + +#include "config.h" +#include "hdf5internal.h" +#include "ncrc.h" + +extern int NC4_extract_file_image(NC_FILE_INFO_T* h5); /* In nc4memcb.c */ + +static void dumpopenobjects(NC_FILE_INFO_T* h5); + +/** @internal When we have open objects at file close, should + we log them or print to stdout. Default is to log. */ +#define LOGOPEN 1 + +/** @internal Number of reserved attributes. These attributes are + * hidden from the netcdf user, but exist in the HDF5 file to help + * netcdf read the file. */ +#define NRESERVED 11 /*|NC_reservedatt|*/ + +/** @internal List of reserved attributes. This list must be in sorted + * order for binary search. */ +static const NC_reservedatt NC_reserved[NRESERVED] = { + {NC_ATT_CLASS, READONLYFLAG|DIMSCALEFLAG}, /*CLASS*/ + {NC_ATT_DIMENSION_LIST, READONLYFLAG|DIMSCALEFLAG}, /*DIMENSION_LIST*/ + {NC_ATT_NAME, READONLYFLAG|DIMSCALEFLAG}, /*NAME*/ + {NC_ATT_REFERENCE_LIST, READONLYFLAG|DIMSCALEFLAG}, /*REFERENCE_LIST*/ + {NC_ATT_FORMAT, READONLYFLAG}, /*_Format*/ + {ISNETCDF4ATT, READONLYFLAG|NAMEONLYFLAG}, /*_IsNetcdf4*/ + {NCPROPS, READONLYFLAG|NAMEONLYFLAG|MATERIALIZEDFLAG},/*_NCProperties*/ + {NC_ATT_COORDINATES, READONLYFLAG|DIMSCALEFLAG|MATERIALIZEDFLAG},/*_Netcdf4Coordinates*/ + {NC_DIMID_ATT_NAME, READONLYFLAG|DIMSCALEFLAG|MATERIALIZEDFLAG},/*_Netcdf4Dimid*/ + {SUPERBLOCKATT, READONLYFLAG|NAMEONLYFLAG},/*_SuperblockVersion*/ + {NC3_STRICT_ATT_NAME, READONLYFLAG|MATERIALIZEDFLAG}, /*_nc3_strict*/ +}; + +/* Forward */ +static int NC4_enddef(int ncid); +static void dumpopenobjects(NC_FILE_INFO_T* h5); + +/** + * @internal Define a binary searcher for reserved attributes + * @param name for which to search + * @return pointer to the matchig NC_reservedatt structure. + * @return NULL if not found. + * @author Dennis Heimbigner + */ +const NC_reservedatt* +NC_findreserved(const char* name) +{ + int n = NRESERVED; + int L = 0; + int R = (n - 1); + for(;;) { + if(L > R) break; + int m = (L + R) / 2; + const NC_reservedatt* p = &NC_reserved[m]; + int cmp = strcmp(p->name,name); + if(cmp == 0) return p; + if(cmp < 0) + L = (m + 1); + else /*cmp > 0*/ + R = (m - 1); + } + return NULL; +} + +/** + * @internal Recursively determine if there is a mismatch between + * order of coordinate creation and associated dimensions in this + * group or any subgroups, to find out if we have to handle that + * situation. Also check if there are any multidimensional coordinate + * variables defined, which require the same treatment to fix a + * potential bug when such variables occur in subgroups. + * + * @param grp Pointer to group info struct. + * @param bad_coord_orderp Pointer that gets 1 if there is a bad + * coordinate order. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @author Ed Hartnett + */ +static int +detect_preserve_dimids(NC_GRP_INFO_T *grp, nc_bool_t *bad_coord_orderp) +{ + NC_VAR_INFO_T *var; + NC_GRP_INFO_T *child_grp; + int last_dimid = -1; + int retval; + int i; + + /* Iterate over variables in this group */ + for (i=0; i < ncindexsize(grp->vars); i++) + { + var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i); + if (var == NULL) continue; + /* Only matters for dimension scale variables, with non-scalar dimensionality */ + if (var->dimscale && var->ndims) + { + /* If the user writes coord vars in a different order then he + * defined their dimensions, then, when the file is reopened, the + * order of the dimids will change to match the order of the coord + * vars. Detect if this is about to happen. */ + if (var->dimids[0] < last_dimid) + { + LOG((5, "%s: %s is out of order coord var", __func__, var->hdr.name)); + *bad_coord_orderp = NC_TRUE; + return NC_NOERR; + } + last_dimid = var->dimids[0]; + + /* If there are multidimensional coordinate variables defined, then + * it's also necessary to preserve dimension IDs when the file is + * reopened ... */ + if (var->ndims > 1) + { + LOG((5, "%s: %s is multidimensional coord var", __func__, var->hdr.name)); + *bad_coord_orderp = NC_TRUE; + return NC_NOERR; + } + + /* Did the user define a dimension, end define mode, reenter define + * mode, and then define a coordinate variable for that dimension? + * If so, dimensions will be out of order. */ + if (var->is_new_var || var->became_coord_var) + { + LOG((5, "%s: coord var defined after enddef/redef", __func__)); + *bad_coord_orderp = NC_TRUE; + return NC_NOERR; + } + } + } + + /* If there are any child groups, check them also for this condition. */ + for (i = 0; i < ncindexsize(grp->children); i++) + { + if (!(child_grp = (NC_GRP_INFO_T *)ncindexith(grp->children, i))) + continue; + if ((retval = detect_preserve_dimids(child_grp, bad_coord_orderp))) + return retval; + } + return NC_NOERR; +} + +/** + * @internal This function will write all changed metadata and flush + * HDF5 file to disk. + * + * @param h5 Pointer to HDF5 file info struct. + * + * @return ::NC_NOERR No error. + * @return ::NC_EINDEFINE Classic model file in define mode. + * @return ::NC_EHDFERR HDF5 error. + * @author Ed Hartnett + */ +static int +sync_netcdf4_file(NC_FILE_INFO_T *h5) +{ + NC_HDF5_FILE_INFO_T *hdf5_info; + int retval; + + assert(h5 && h5->format_file_info); + LOG((3, "%s", __func__)); + + /* If we're in define mode, that's an error, for strict nc3 rules, + * otherwise, end define mode. */ + if (h5->flags & NC_INDEF) + { + if (h5->cmode & NC_CLASSIC_MODEL) + return NC_EINDEFINE; + + /* Turn define mode off. */ + h5->flags ^= NC_INDEF; + + /* Redef mode needs to be tracked separately for nc_abort. */ + h5->redef = NC_FALSE; + } + +#ifdef LOGGING + /* This will print out the names, types, lens, etc of the vars and + atts in the file, if the logging level is 2 or greater. */ + log_metadata_nc(h5); +#endif + + /* Write any metadata that has changed. */ + if (!h5->no_write) + { + nc_bool_t bad_coord_order = NC_FALSE; + + /* Write any user-defined types. */ + if ((retval = nc4_rec_write_groups_types(h5->root_grp))) + return retval; + + /* Check to see if the coordinate order is messed up. If + * detected, propagate to all groups to consistently store + * dimids. */ + if ((retval = detect_preserve_dimids(h5->root_grp, &bad_coord_order))) + return retval; + + /* Write all the metadata. */ + if ((retval = nc4_rec_write_metadata(h5->root_grp, bad_coord_order))) + return retval; + + /* Write out provenance*/ + if((retval = NC4_write_provenance(h5))) + return retval; + } + + /* Tell HDF5 to flush all changes to the file. */ + hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info; + if (H5Fflush(hdf5_info->hdfid, H5F_SCOPE_GLOBAL) < 0) + return NC_EHDFERR; + + return NC_NOERR; +} + +/** + * @internal This function will free all allocated metadata memory, + * and close the HDF5 file. The group that is passed in must be the + * root group of the file. If inmemory is used, then save + * the final memory in mem.memio. + * + * @param h5 Pointer to HDF5 file info struct. + * @param abort True if this is an abort. + * @param memio the place to return a core image if not NULL + * + * @return ::NC_NOERR No error. + * @return ::NC_EHDFERR HDF5 could not close the file. + * @return ::NC_EINDEFINE Classic model file is in define mode. + * @author Ed Hartnett, Dennis Heimbigner + */ +int +nc4_close_netcdf4_file(NC_FILE_INFO_T *h5, int abort, NC_memio *memio) +{ + NC_HDF5_FILE_INFO_T *hdf5_info; + int retval; + + assert(h5 && h5->root_grp && h5->format_file_info); + LOG((3, "%s: h5->path %s abort %d", __func__, h5->controller->path, abort)); + + /* Get HDF5 specific info. */ + hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info; + + /* Delete all the list contents for vars, dims, and atts, in each + * group. */ + if ((retval = nc4_rec_grp_del(h5->root_grp))) + return retval; + + /* Free lists of dims, groups, and types in the root group. */ + nclistfree(h5->alldims); + nclistfree(h5->allgroups); + nclistfree(h5->alltypes); + +#ifdef USE_PARALLEL4 + /* Free the MPI Comm & Info objects, if we opened the file in + * parallel. */ + if (h5->parallel) + { + if (h5->comm != MPI_COMM_NULL) + MPI_Comm_free(&h5->comm); + if (h5->info != MPI_INFO_NULL) + MPI_Info_free(&h5->info); + } +#endif + + /* Free the fileinfo struct, which holds info from the fileinfo + * hidden attribute. */ + NC4_clear_provenance(&h5->provenance); + + /* Close hdf file. It may not be open, since this function is also + * called by NC_create() when a file opening is aborted. */ + if (hdf5_info->hdfid > 0 && H5Fclose(hdf5_info->hdfid) < 0) + { + dumpopenobjects(h5); + return NC_EHDFERR; + } + + /* If inmemory is used and user wants the final memory block, + then capture and return the final memory block else free it */ + if(h5->mem.inmemory) { + /* Pull out the final memory */ + (void)NC4_extract_file_image(h5); + if(!abort && memio != NULL) { + *memio = h5->mem.memio; /* capture it */ + h5->mem.memio.memory = NULL; /* avoid duplicate free */ + } + /* If needed, reclaim extraneous memory */ + if(h5->mem.memio.memory != NULL) { + /* If the original block of memory is not resizeable, then + it belongs to the caller and we should not free it. */ + if(!h5->mem.locked) + free(h5->mem.memio.memory); + } + h5->mem.memio.memory = NULL; + h5->mem.memio.size = 0; + NC4_image_finalize(h5->mem.udata); + } + + /* Free the HDF5-specific info. */ + if (h5->format_file_info) + free(h5->format_file_info); + + /* Free the nc4_info struct; above code should have reclaimed + everything else */ + free(h5); + + return NC_NOERR; +} + +/** + * @internal This function will recurse through an open HDF5 file and + * release resources. All open HDF5 objects in the file will be + * closed. + * + * @param h5 Pointer to HDF5 file info struct. + * @param abort True if this is an abort. + * @param memio the place to return a core image if not NULL + * + * @return ::NC_NOERR No error. + * @return ::NC_EHDFERR HDF5 could not close the file. + * @author Ed Hartnett + */ +int +nc4_close_hdf5_file(NC_FILE_INFO_T *h5, int abort, NC_memio *memio) +{ + int retval; + + assert(h5 && h5->root_grp && h5->format_file_info); + LOG((3, "%s: h5->path %s abort %d", __func__, h5->controller->path, abort)); + + /* According to the docs, always end define mode on close. */ + if (h5->flags & NC_INDEF) + h5->flags ^= NC_INDEF; + + /* Sync the file, unless we're aborting, or this is a read-only + * file. */ + if (!h5->no_write && !abort) + if ((retval = sync_netcdf4_file(h5))) + return retval; + + /* Close all open HDF5 objects within the file. */ + if ((retval = nc4_rec_grp_HDF5_del(h5->root_grp))) + return retval; + + /* Release all intarnal lists and metadata associated with this + * file. All HDF5 objects have already been released. */ + if ((retval = nc4_close_netcdf4_file(h5, abort, memio))) + return retval; + + return NC_NOERR; +} + +/** + * @internal Output a list of still-open objects in the HDF5 + * file. This is only called if the file fails to close cleanly. + * + * @param h5 Pointer to file info. + * + * @author Dennis Heimbigner + */ +static void +dumpopenobjects(NC_FILE_INFO_T* h5) +{ + NC_HDF5_FILE_INFO_T *hdf5_info; + int nobjs; + + assert(h5 && h5->format_file_info); + hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info; + + if(hdf5_info->hdfid <= 0) + return; /* File was never opened */ + + nobjs = H5Fget_obj_count(hdf5_info->hdfid, H5F_OBJ_ALL); + + /* Apparently we can get an error even when nobjs == 0 */ + if(nobjs < 0) { + return; + } else if(nobjs > 0) { + char msg[1024]; + int logit = 0; + /* If the close doesn't work, probably there are still some HDF5 + * objects open, which means there's a bug in the library. So + * print out some info on to help the poor programmer figure it + * out. */ + snprintf(msg,sizeof(msg),"There are %d HDF5 objects open!", nobjs); +#ifdef LOGGING +#ifdef LOGOPEN + LOG((0, msg)); + logit = 1; +#endif +#else + fprintf(stdout,"%s\n",msg); + logit = 0; +#endif + reportopenobjects(logit,hdf5_info->hdfid); + fflush(stderr); + } + + return; +} + +/** + * @internal Unfortunately HDF only allows specification of fill value + * only when a dataset is created. Whereas in netcdf, you first create + * the variable and then (optionally) specify the fill value. To + * accomplish this in HDF5 I have to delete the dataset, and recreate + * it, with the fill value specified. + * + * @param ncid File and group ID. + * @param fillmode File mode. + * @param old_modep Pointer that gets old mode. Ignored if NULL. + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +int +NC4_set_fill(int ncid, int fillmode, int *old_modep) +{ + NC_FILE_INFO_T *nc4_info; + int retval; + + LOG((2, "%s: ncid 0x%x fillmode %d", __func__, ncid, fillmode)); + + /* Get pointer to file info. */ + if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info))) + return retval; + assert(nc4_info); + + /* Trying to set fill on a read-only file? You sicken me! */ + if (nc4_info->no_write) + return NC_EPERM; + + /* Did you pass me some weird fillmode? */ + if (fillmode != NC_FILL && fillmode != NC_NOFILL) + return NC_EINVAL; + + /* If the user wants to know, tell him what the old mode was. */ + if (old_modep) + *old_modep = nc4_info->fill_mode; + + nc4_info->fill_mode = fillmode; + + return NC_NOERR; +} + +/** + * @internal Put the file back in redef mode. This is done + * automatically for netcdf-4 files, if the user forgets. + * + * @param ncid File and group ID. + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +int +NC4_redef(int ncid) +{ + NC_FILE_INFO_T *nc4_info; + int retval; + + LOG((1, "%s: ncid 0x%x", __func__, ncid)); + + /* Find this file's metadata. */ + if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info))) + return retval; + assert(nc4_info); + + /* If we're already in define mode, return an error. */ + if (nc4_info->flags & NC_INDEF) + return NC_EINDEFINE; + + /* If the file is read-only, return an error. */ + if (nc4_info->no_write) + return NC_EPERM; + + /* Set define mode. */ + nc4_info->flags |= NC_INDEF; + + /* For nc_abort, we need to remember if we're in define mode as a + redef. */ + nc4_info->redef = NC_TRUE; + + return NC_NOERR; +} + +/** + * @internal For netcdf-4 files, this just calls nc_enddef, ignoring + * the extra parameters. + * + * @param ncid File and group ID. + * @param h_minfree Ignored for netCDF-4 files. + * @param v_align Ignored for netCDF-4 files. + * @param v_minfree Ignored for netCDF-4 files. + * @param r_align Ignored for netCDF-4 files. + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +int +NC4__enddef(int ncid, size_t h_minfree, size_t v_align, + size_t v_minfree, size_t r_align) +{ + return NC4_enddef(ncid); +} + +/** + * @internal Take the file out of define mode. This is called + * automatically for netcdf-4 files, if the user forgets. + * + * @param ncid File and group ID. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_EBADGRPID Bad group ID. + * @author Ed Hartnett + */ +static int +NC4_enddef(int ncid) +{ + NC_FILE_INFO_T *nc4_info; + NC_GRP_INFO_T *grp; + NC_VAR_INFO_T *var; + int i; + int retval; + + LOG((1, "%s: ncid 0x%x", __func__, ncid)); + + /* Find pointer to group and nc4_info. */ + if ((retval = nc4_find_grp_h5(ncid, &grp, &nc4_info))) + return retval; + + /* When exiting define mode, mark all variable written. */ + for (i = 0; i < ncindexsize(grp->vars); i++) + { + var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i); + assert(var); + var->written_to = NC_TRUE; + } + + return nc4_enddef_netcdf4_file(nc4_info); +} + +/** + * @internal Flushes all buffers associated with the file, after + * writing all changed metadata. This may only be called in data mode. + * + * @param ncid File and group ID. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_EINDEFINE Classic model file is in define mode. + * @author Ed Hartnett + */ +int +NC4_sync(int ncid) +{ + NC_FILE_INFO_T *nc4_info; + int retval; + + LOG((2, "%s: ncid 0x%x", __func__, ncid)); + + if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info))) + return retval; + assert(nc4_info); + + /* If we're in define mode, we can't sync. */ + if (nc4_info->flags & NC_INDEF) + { + if (nc4_info->cmode & NC_CLASSIC_MODEL) + return NC_EINDEFINE; + if ((retval = NC4_enddef(ncid))) + return retval; + } + + return sync_netcdf4_file(nc4_info); +} + +/** + * @internal From the netcdf-3 docs: The function nc_abort just closes + * the netCDF dataset, if not in define mode. If the dataset is being + * created and is still in define mode, the dataset is deleted. If + * define mode was entered by a call to nc_redef, the netCDF dataset + * is restored to its state before definition mode was entered and the + * dataset is closed. + * + * @param ncid File and group ID. + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +int +NC4_abort(int ncid) +{ + NC *nc; + NC_FILE_INFO_T *nc4_info; + int delete_file = 0; + char path[NC_MAX_NAME + 1]; + int retval; + + LOG((2, "%s: ncid 0x%x", __func__, ncid)); + + /* Find metadata for this file. */ + if ((retval = nc4_find_nc_grp_h5(ncid, &nc, NULL, &nc4_info))) + return retval; + assert(nc4_info); + + /* If we're in define mode, but not redefing the file, delete it. */ + if (nc4_info->flags & NC_INDEF && !nc4_info->redef) + { + delete_file++; + strncpy(path, nc->path, NC_MAX_NAME); + } + + /* Free any resources the netcdf-4 library has for this file's + * metadata. */ + if ((retval = nc4_close_hdf5_file(nc4_info, 1, NULL))) + return retval; + + /* Delete the file, if we should. */ + if (delete_file) + if (remove(path) < 0) + return NC_ECANTREMOVE; + + return NC_NOERR; +} + +/** + * @internal Close the netcdf file, writing any changes first. + * + * @param ncid File and group ID. + * @param params any extra parameters in/out of close + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +int +NC4_close(int ncid, void* params) +{ + NC_GRP_INFO_T *grp; + NC_FILE_INFO_T *h5; + int retval; + int inmemory; + NC_memio* memio = NULL; + + LOG((1, "%s: ncid 0x%x", __func__, ncid)); + + /* Find our metadata for this file. */ + if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) + return retval; + + assert(h5 && grp); + + /* This must be the root group. */ + if (grp->parent) + return NC_EBADGRPID; + + inmemory = ((h5->cmode & NC_INMEMORY) == NC_INMEMORY); + + if(inmemory && params != NULL) { + memio = (NC_memio*)params; + } + + /* Call the nc4 close. */ + if ((retval = nc4_close_hdf5_file(grp->nc4_info, 0, memio))) + return retval; + + return NC_NOERR; +} + +/** + * @internal Learn number of dimensions, variables, global attributes, + * and the ID of the first unlimited dimension (if any). + * + * @note It's possible for any of these pointers to be NULL, in which + * case don't try to figure out that value. + * + * @param ncid File and group ID. + * @param ndimsp Pointer that gets number of dimensions. + * @param nvarsp Pointer that gets number of variables. + * @param nattsp Pointer that gets number of global attributes. + * @param unlimdimidp Pointer that gets first unlimited dimension ID, + * or -1 if there are no unlimied dimensions. + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +int +NC4_inq(int ncid, int *ndimsp, int *nvarsp, int *nattsp, int *unlimdimidp) +{ + NC *nc; + NC_FILE_INFO_T *h5; + NC_GRP_INFO_T *grp; + int retval; + int i; + + LOG((2, "%s: ncid 0x%x", __func__, ncid)); + + /* Find file metadata. */ + if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) + return retval; + + assert(h5 && grp && nc); + + /* Count the number of dims, vars, and global atts; need to iterate + * because of possible nulls. */ + if (ndimsp) + { + *ndimsp = ncindexcount(grp->dim); + } + if (nvarsp) + { + *nvarsp = ncindexcount(grp->vars); + } + if (nattsp) + { + /* Do we need to read the atts? */ + if (!grp->atts_read) + if ((retval = nc4_read_atts(grp, NULL))) + return retval; + + *nattsp = ncindexcount(grp->att); + } + + if (unlimdimidp) + { + /* Default, no unlimited dimension */ + *unlimdimidp = -1; + + /* If there's more than one unlimited dim, which was not possible + with netcdf-3, then only the last unlimited one will be reported + back in xtendimp. */ + /* Note that this code is inconsistent with nc_inq_unlimid() */ + for(i=0;i<ncindexsize(grp->dim);i++) { + NC_DIM_INFO_T* d = (NC_DIM_INFO_T*)ncindexith(grp->dim,i); + if(d == NULL) continue; + if(d->unlimited) { + *unlimdimidp = d->hdr.id; + break; + } + } + } + + return NC_NOERR; +} + +/** + * @internal This function will do the enddef stuff for a netcdf-4 file. + * + * @param h5 Pointer to HDF5 file info struct. + * + * @return ::NC_NOERR No error. + * @return ::NC_ENOTINDEFINE Not in define mode. + * @author Ed Hartnett + */ +int +nc4_enddef_netcdf4_file(NC_FILE_INFO_T *h5) +{ + assert(h5); + LOG((3, "%s", __func__)); + + /* If we're not in define mode, return an error. */ + if (!(h5->flags & NC_INDEF)) + return NC_ENOTINDEFINE; + + /* Turn define mode off. */ + h5->flags ^= NC_INDEF; + + /* Redef mode needs to be tracked separately for nc_abort. */ + h5->redef = NC_FALSE; + + return sync_netcdf4_file(h5); +} diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5grp.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5grp.c new file mode 100644 index 0000000000000000000000000000000000000000..880538f01cd98ab6bf34dfbf9b4c93325ece29cd --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5grp.c @@ -0,0 +1,175 @@ +/* Copyright 2005-2018, University Corporation for Atmospheric + * Research. See 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 HDF5 groups. + * + * @author Ed Hartnett + */ + +#include "config.h" +#include "hdf5internal.h" + +/** + * @internal Create a group. Its ncid is returned in the new_ncid + * pointer. + * + * @param parent_ncid Parent group. + * @param name Name of new group. + * @param new_ncid Pointer that gets ncid for new group. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_ESTRICTNC3 Classic model in use for this file. + * @return ::NC_ENOTNC4 Not a netCDF-4 file. + * @author Ed Hartnett + */ +int +NC4_def_grp(int parent_ncid, const char *name, int *new_ncid) +{ + NC_GRP_INFO_T *grp, *g; + NC_FILE_INFO_T *h5; + char norm_name[NC_MAX_NAME + 1]; + int retval; + + LOG((2, "%s: parent_ncid 0x%x name %s", __func__, parent_ncid, name)); + + /* Find info for this file and group, and set pointer to each. */ + if ((retval = nc4_find_grp_h5(parent_ncid, &grp, &h5))) + return retval; + assert(h5); + + /* Check and normalize the name. */ + if ((retval = nc4_check_name(name, norm_name))) + return retval; + + /* 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; + + /* No groups in netcdf-3! */ + if (h5->cmode & NC_CLASSIC_MODEL) + return NC_ESTRICTNC3; + + /* If it's not in define mode, switch to define mode. */ + if (!(h5->flags & NC_INDEF)) + if ((retval = NC4_redef(parent_ncid))) + return retval; + + /* Update internal lists to reflect new group. The actual HDF5 + * group creation will be done when metadata is written by a + * sync. */ + if ((retval = nc4_grp_list_add(h5, grp, norm_name, &g))) + return retval; + if (!(g->format_grp_info = calloc(1, sizeof(NC_HDF5_GRP_INFO_T)))) + return NC_ENOMEM; + + /* For new groups, there are no atts to read from file. */ + g->atts_read = 1; + + /* Return the ncid to the user. */ + if (new_ncid) + *new_ncid = grp->nc4_info->controller->ext_ncid | g->hdr.id; + + return NC_NOERR; +} + +/** + * @internal Rename a group. + * + * @param grpid Group ID. + * @param name New name for group. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_ENOTNC4 Not a netCDF-4 file. + * @return ::NC_EPERM File opened read-only. + * @return ::NC_EBADGRPID Renaming root forbidden. + * @return ::NC_EHDFERR HDF5 function returned error. + * @return ::NC_ENOMEM Out of memory. + * @author Ed Hartnett + */ +int +NC4_rename_grp(int grpid, const char *name) +{ + NC_GRP_INFO_T *grp; + NC_HDF5_GRP_INFO_T *hdf5_grp; + NC_FILE_INFO_T *h5; + char norm_name[NC_MAX_NAME + 1]; + int retval; + + LOG((2, "nc_rename_grp: grpid 0x%x name %s", grpid, name)); + + /* Find info for this file and group, and set pointer to each. */ + if ((retval = nc4_find_grp_h5(grpid, &grp, &h5))) + return retval; + assert(h5 && grp && grp->format_grp_info); + + /* Get HDF5-specific group info. */ + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + + if (h5->no_write) + return NC_EPERM; /* attempt to write to a read-only file */ + + /* Do not allow renaming the root group */ + if (grp->parent == NULL) + return NC_EBADGRPID; + + /* Check and normalize the name. */ + if ((retval = nc4_check_name(name, norm_name))) + return retval; + + /* Check that this name is not in use as a var, grp, or type in the + * parent group (i.e. the group that grp is in). */ + if ((retval = nc4_check_dup_name(grp->parent, norm_name))) + return retval; + + /* If it's not in define mode, switch to define mode. */ + if (!(h5->flags & NC_INDEF)) + if ((retval = NC4_redef(grpid))) + return retval; + + /* Rename the group, if it exists in the file */ + if (hdf5_grp->hdf_grpid) + { + NC_HDF5_GRP_INFO_T *parent_hdf5_grp; + parent_hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->parent->format_grp_info; + + /* Close the group */ + if (H5Gclose(hdf5_grp->hdf_grpid) < 0) + return NC_EHDFERR; + hdf5_grp->hdf_grpid = 0; + + /* Attempt to rename & re-open the group, if the parent group is open */ + if (parent_hdf5_grp->hdf_grpid) + { + /* Rename the group. */ + if (H5Lmove(parent_hdf5_grp->hdf_grpid, grp->hdr.name, + parent_hdf5_grp->hdf_grpid, name, H5P_DEFAULT, + H5P_DEFAULT) < 0) + return NC_EHDFERR; + + /* Reopen the group, with the new name. */ + if ((hdf5_grp->hdf_grpid = H5Gopen2(parent_hdf5_grp->hdf_grpid, name, + H5P_DEFAULT)) < 0) + return NC_EHDFERR; + } + } + + /* Give the group its new name in metadata. UTF8 normalization + * has been done. */ + free(grp->hdr.name); + if (!(grp->hdr.name = strdup(norm_name))) + return NC_ENOMEM; + + /* Update the hash and rebuild index. */ + grp->hdr.hashkey = NC_hashmapkey(grp->hdr.name,strlen(grp->hdr.name)); + if(!ncindexrebuild(grp->parent->children)) + return NC_EINTERNAL; + + return NC_NOERR; +} diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5internal.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5internal.c new file mode 100644 index 0000000000000000000000000000000000000000..ad843553d6659bb2e2d0e1907c5451adf15c2eba --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5internal.c @@ -0,0 +1,934 @@ +/* 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 "hdf5internal.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 Provide a function to do any necessary finalization of + * the HDF5 library. + */ +void +nc4_hdf5_finalize(void) +{ + /* Reclaim global resources */ + NC4_provenance_finalize(); + nc4_hdf5_initialized = 0; +} + +/** + * @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_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++) + { + NC_HDF5_TYPE_INFO_T *hdf5_type; + hid_t hdf_typeid; + + type = (NC_TYPE_INFO_T*)nclistget(h5->alltypes, i); + if(type == NULL) continue; + + /* Get HDF5-specific type info. */ + hdf5_type = (NC_HDF5_TYPE_INFO_T *)type->format_type_info; + + /* Select the HDF5 typeid to use. */ + hdf_typeid = hdf5_type->native_hdf_typeid ? + hdf5_type->native_hdf_typeid : hdf5_type->hdf_typeid; + + /* Is this the type we are searching for? */ + if ((equal = H5Tequal(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_VAR_INFO_T *var; + int retval; + int i; + + assert(grp && len); + LOG((3, "%s: grp->name %s dimid %d", __func__, grp->hdr.name, dimid)); + + /* If there are any groups, call this function recursively on + * them. */ + for (i = 0; i < ncindexsize(grp->children); i++) + if ((retval = nc4_find_dim_len((NC_GRP_INFO_T*)ncindexith(grp->children, i), + 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); + assert(var); + + /* 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; + + /* Sanity checks */ + assert(grp && coord_var && dim && dim->coord_var == coord_var && + coord_var->dim[0] == dim && coord_var->dimids[0] == dim->hdr.id && + !((NC_HDF5_DIM_INFO_T *)(dim->format_dim_info))->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, + ((NC_HDF5_VAR_INFO_T *)(coord_var->format_var_info))->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; + } + + /* 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_dimscale_dataset(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T *dim) +{ + NC_HDF5_DIM_INFO_T *hdf5_dim; + NC_HDF5_GRP_INFO_T *hdf5_grp; + int retval; + + assert(grp && grp->format_grp_info && dim && dim->format_dim_info); + LOG((2, "%s: deleting dimscale dataset %s dimid %d", __func__, dim->hdr.name, + dimid)); + + /* Get HDF5 specific grp and dim info. */ + hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + + /* Detach dimscale from any variables using it */ + if ((retval = rec_detach_scales(grp, dimid, hdf5_dim->hdf_dimscaleid)) < 0) + return retval; + + /* Close the HDF5 dataset */ + if (H5Dclose(hdf5_dim->hdf_dimscaleid) < 0) + return NC_EHDFERR; + hdf5_dim->hdf_dimscaleid = 0; + + /* Now delete the dataset. */ + if (H5Gunlink(hdf5_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, Ed Hartnett + */ +int +nc4_reform_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, NC_DIM_INFO_T *dim) +{ + NC_HDF5_DIM_INFO_T *hdf5_dim; + NC_HDF5_GRP_INFO_T *hdf5_grp; + NC_HDF5_VAR_INFO_T *hdf5_var; + int need_to_reattach_scales = 0; + int retval = NC_NOERR; + + assert(grp && grp->format_grp_info && var && var->format_var_info && + dim && dim->format_dim_info); + LOG((3, "%s: dim->hdr.name %s var->hdr.name %s", __func__, dim->hdr.name, + var->hdr.name)); + + /* Get HDF5-specific dim, group, and var info. */ + hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + /* 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; + NC_HDF5_DIM_INFO_T *hdf5_dim1; + + for (k = 0; k < ncindexsize(g->dim); k++) + { + dim1 = (NC_DIM_INFO_T *)ncindexith(g->dim, k); + assert(dim1 && dim1->format_dim_info); + hdf5_dim1 = (NC_HDF5_DIM_INFO_T *)dim1->format_dim_info; + + 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 = ((NC_HDF5_VAR_INFO_T *) + (dim1->coord_var->format_var_info))->hdf_datasetid; + else + dim_datasetid = hdf5_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(hdf5_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 (hdf5_dim->hdf_dimscaleid && grp != NULL) + { + LOG((3, "closing and unlinking dimscale dataset %s", dim->hdr.name)); + if (H5Dclose(hdf5_dim->hdf_dimscaleid) < 0) + BAIL(NC_EHDFERR); + hdf5_dim->hdf_dimscaleid = 0; + + /* Now delete the dimscale's dataset (it will be recreated + later, if necessary). */ + if (H5Gunlink(hdf5_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], hdf5_var->hdf_datasetid))) + return retval; + + /* Set state transition indicator (cancels earlier + * transition). */ + var->was_coord_var = NC_FALSE; + } + + /* Set state transition indicator */ + var->became_coord_var = NC_TRUE; + +exit: + return retval; +} + +/** + * @internal Close HDF5 resources for global atts in a group. + * + * @param grp Pointer to group info struct. + * + * @return ::NC_NOERR No error. + * @return ::NC_EHDFERR HDF5 error. + * @author Ed Hartnett + */ +static int +close_gatts(NC_GRP_INFO_T *grp) +{ + NC_ATT_INFO_T *att; + int a; + + for (a = 0; a < ncindexsize(grp->att); a++) + { + NC_HDF5_ATT_INFO_T *hdf5_att; + + att = (NC_ATT_INFO_T *)ncindexith(grp->att, a); + assert(att && att->format_att_info); + hdf5_att = (NC_HDF5_ATT_INFO_T *)att->format_att_info; + + /* Close the HDF5 typeid. */ + if (hdf5_att->native_hdf_typeid && + H5Tclose(hdf5_att->native_hdf_typeid) < 0) + return NC_EHDFERR; + } + return NC_NOERR; +} + +/** + * @internal Close HDF5 resources for vars in a group. + * + * @param grp Pointer to group info struct. + * + * @return ::NC_NOERR No error. + * @return ::NC_EHDFERR HDF5 error. + * @author Ed Hartnett + */ +static int +close_vars(NC_GRP_INFO_T *grp) +{ + NC_VAR_INFO_T *var; + NC_HDF5_VAR_INFO_T *hdf5_var; + NC_ATT_INFO_T *att; + int a, i; + + for (i = 0; i < ncindexsize(grp->vars); i++) + { + var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i); + assert(var && var->format_var_info); + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + /* Close the HDF5 dataset associated with this var. */ + if (hdf5_var->hdf_datasetid) + { + LOG((3, "closing HDF5 dataset %lld", hdf5_var->hdf_datasetid)); + if (H5Dclose(hdf5_var->hdf_datasetid) < 0) + return NC_EHDFERR; + + if (var->fill_value) + { + 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); + } + } + } + + /* Delete any HDF5 dimscale objid information. */ + if (hdf5_var->dimscale_hdf5_objids) + free(hdf5_var->dimscale_hdf5_objids); + + for (a = 0; a < ncindexsize(var->att); a++) + { + NC_HDF5_ATT_INFO_T *hdf5_att; + att = (NC_ATT_INFO_T *)ncindexith(var->att, a); + assert(att && att->format_att_info); + hdf5_att = (NC_HDF5_ATT_INFO_T *)att->format_att_info; + + /* Close the HDF5 typeid if one is open. */ + if (hdf5_att->native_hdf_typeid && + H5Tclose(hdf5_att->native_hdf_typeid) < 0) + return NC_EHDFERR; + } + } + + return NC_NOERR; +} + +/** + * @internal Close HDF5 resources for dims in a group. + * + * @param grp Pointer to group info struct. + * + * @return ::NC_NOERR No error. + * @return ::NC_EHDFERR HDF5 error. + * @author Ed Hartnett + */ +static int +close_dims(NC_GRP_INFO_T *grp) +{ + NC_DIM_INFO_T *dim; + int i; + + for (i = 0; i < ncindexsize(grp->dim); i++) + { + NC_HDF5_DIM_INFO_T *hdf5_dim; + + dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, i); + assert(dim && dim->format_dim_info); + hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; + + /* If this is a dim without a coordinate variable, then close + * the HDF5 DIM_WITHOUT_VARIABLE dataset associated with this + * dim. */ + if (hdf5_dim->hdf_dimscaleid && H5Dclose(hdf5_dim->hdf_dimscaleid) < 0) + return NC_EHDFERR; + } + + return NC_NOERR; +} + +/** + * @internal Close HDF5 resources for types in a group. Set values to + * 0 after closing types. Because of type reference counters, these + * closes can be called multiple times. + * + * @param grp Pointer to group info struct. + * + * @return ::NC_NOERR No error. + * @return ::NC_EHDFERR HDF5 error. + * @author Ed Hartnett, Dennis Heimbigner + */ +static int +close_types(NC_GRP_INFO_T *grp) +{ + int i; + + for (i = 0; i < ncindexsize(grp->type); i++) + { + NC_TYPE_INFO_T *type; + NC_HDF5_TYPE_INFO_T *hdf5_type; + + type = (NC_TYPE_INFO_T *)ncindexith(grp->type, i); + assert(type && type->format_type_info); + + /* Get HDF5-specific type info. */ + hdf5_type = (NC_HDF5_TYPE_INFO_T *)type->format_type_info; + + /* Close any open user-defined HDF5 typeids. */ + if (hdf5_type->hdf_typeid && H5Tclose(hdf5_type->hdf_typeid) < 0) + return NC_EHDFERR; + hdf5_type->hdf_typeid = 0; + if (hdf5_type->native_hdf_typeid && + H5Tclose(hdf5_type->native_hdf_typeid) < 0) + return NC_EHDFERR; + hdf5_type->native_hdf_typeid = 0; + } + + return NC_NOERR; +} + +/** + * @internal Recursively free HDF5 objects for a group (and everything + * it contains). + * + * @param grp Pointer to group info struct. + * + * @return ::NC_NOERR No error. + * @return ::NC_EHDFERR HDF5 error. + * @author Ed Hartnett + */ +int +nc4_rec_grp_HDF5_del(NC_GRP_INFO_T *grp) +{ + NC_HDF5_GRP_INFO_T *hdf5_grp; + int i; + int retval; + + assert(grp && grp->format_grp_info); + LOG((3, "%s: grp->name %s", __func__, grp->hdr.name)); + + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + + /* Recursively call this function for each child, if any, stopping + * if there is an error. */ + for (i = 0; i < ncindexsize(grp->children); i++) + if ((retval = nc4_rec_grp_HDF5_del((NC_GRP_INFO_T *)ncindexith(grp->children, + i)))) + return retval; + + /* Close HDF5 resources associated with global attributes. */ + if ((retval = close_gatts(grp))) + return retval; + + /* Close HDF5 resources associated with vars. */ + if ((retval = close_vars(grp))) + return retval; + + /* Close HDF5 resources associated with dims. */ + if ((retval = close_dims(grp))) + return retval; + + /* Close HDF5 resources associated with types. */ + if ((retval = close_types(grp))) + return retval; + + /* Close the HDF5 group. */ + LOG((4, "%s: closing group %s", __func__, grp->hdr.name)); + if (hdf5_grp->hdf_grpid && H5Gclose(hdf5_grp->hdf_grpid) < 0) + return NC_EHDFERR; + + return NC_NOERR; +} + +/** + * @internal Given an ncid and varid, get pointers to the group and var + * metadata. Lazy var metadata reads are done as needed. + * + * @param ncid File ID. + * @param varid Variable ID. + * @param h5 Pointer that gets pointer to the NC_FILE_INFO_T struct + * for this file. Ignored if NULL. + * @param grp Pointer that gets pointer to group info. Ignored if + * NULL. + * @param var Pointer that gets pointer to var info. Ignored if NULL. + * + * @return ::NC_NOERR No error. + * @return ::NC_ENOTVAR Variable not found. + * @author Ed Hartnett + */ +int +nc4_hdf5_find_grp_h5_var(int ncid, int varid, NC_FILE_INFO_T **h5, + NC_GRP_INFO_T **grp, NC_VAR_INFO_T **var) +{ + NC_FILE_INFO_T *my_h5; + NC_GRP_INFO_T *my_grp; + NC_VAR_INFO_T *my_var; + int retval; + + /* Look up file and group metadata. */ + if ((retval = nc4_find_grp_h5(ncid, &my_grp, &my_h5))) + return retval; + assert(my_grp && my_h5); + + /* Find the var. */ + if (!(my_var = (NC_VAR_INFO_T *)ncindexith(my_grp->vars, varid))) + return NC_ENOTVAR; + assert(my_var && my_var->hdr.id == varid); + + /* Do we need to read var metadata? */ + if (!my_var->meta_read && my_var->created) + if ((retval = nc4_get_var_meta(my_var))) + return retval; + + /* Return pointers that caller wants. */ + if (h5) + *h5 = my_h5; + if (grp) + *grp = my_grp; + if (var) + *var = my_var; + + return NC_NOERR; +} + +/** + * @internal Given an ncid, varid, and attribute name, return + * normalized name and pointers to the file, group, var, and att info + * structs. Lazy reads of attributes and variable metadata are done as + * needed. + * + * @param ncid File/group ID. + * @param varid Variable ID. + * @param name Name to of attribute. + * @param attnum Number of attribute. + * @param use_name If true, use the name to get the + * attribute. Otherwise use the attnum. + * @param norm_name Pointer to storage of size NC_MAX_NAME + 1, + * which will get the normalized name, if use_name is true. Ignored if + * NULL. + * @param h5 Pointer to pointer that gets file info struct. Ignored if + * NULL. + * @param grp Pointer to pointer that gets group info struct. Ignored + * if NULL. + * @param h5 Pointer to pointer that gets variable info + * struct. Ignored if NULL. + * @param att Pointer to pointer that gets attribute info + * struct. Ignored if NULL. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_ENOTVAR Variable not found. + * @return ::NC_ENOTATT Attribute not found. + * @author Ed Hartnett + */ +int +nc4_hdf5_find_grp_var_att(int ncid, int varid, const char *name, int attnum, + int use_name, char *norm_name, NC_FILE_INFO_T **h5, + NC_GRP_INFO_T **grp, NC_VAR_INFO_T **var, + NC_ATT_INFO_T **att) +{ + NC_FILE_INFO_T *my_h5; + NC_GRP_INFO_T *my_grp; + NC_VAR_INFO_T *my_var = NULL; + NC_ATT_INFO_T *my_att; + char my_norm_name[NC_MAX_NAME + 1] = ""; + NCindex *attlist = NULL; + int retval; + + LOG((4, "%s: ncid %d varid %d attnum %d use_name %d", __func__, ncid, varid, + attnum, use_name)); + + /* Don't need to provide name unless getting att pointer and using + * use_name. */ + assert(!att || ((use_name && name) || !use_name)); + + /* Find info for this file, group, and h5 info. */ + if ((retval = nc4_find_nc_grp_h5(ncid, NULL, &my_grp, &my_h5))) + return retval; + assert(my_grp && my_h5); + + /* Get either the global or a variable attribute list. */ + if (varid == NC_GLOBAL) + { + /* Do we need to read the atts? */ + if (!my_grp->atts_read) + if ((retval = nc4_read_atts(my_grp, NULL))) + return retval; + + attlist = my_grp->att; + } + else + { + if (!(my_var = (NC_VAR_INFO_T *)ncindexith(my_grp->vars, varid))) + return NC_ENOTVAR; + + /* Do we need to read the var attributes? */ + if (!my_var->atts_read) + if ((retval = nc4_read_atts(my_grp, my_var))) + return retval; + + /* Do we need to read var metadata? */ + if (!my_var->meta_read && my_var->created) + if ((retval = nc4_get_var_meta(my_var))) + return retval; + + attlist = my_var->att; + } + assert(attlist); + + /* Need a name if use_name is true. */ + if (use_name && !name) + return NC_EBADNAME; + + /* Normalize the name. */ + if (use_name) + if ((retval = nc4_normalize_name(name, my_norm_name))) + return retval; + + /* Now find the attribute by name or number. */ + if (att) + { + my_att = use_name ? (NC_ATT_INFO_T *)ncindexlookup(attlist, my_norm_name) : + (NC_ATT_INFO_T *)ncindexith(attlist, attnum); + if (!my_att) + return NC_ENOTATT; + } + + /* Give the people what they want. */ + if (norm_name) + strncpy(norm_name, my_norm_name, NC_MAX_NAME); + if (h5) + *h5 = my_h5; + if (grp) + *grp = my_grp; + if (var) + *var = my_var; + if (att) + *att = my_att; + + return NC_NOERR; +} + +#ifdef LOGGING +/* We will need to check against nc log level from nc4internal.c. */ +extern int nc_log_level; + +/** + * @internal This is like nc_set_log_level(), but will also turn on + * HDF5 internal logging, in addition to netCDF logging. This should + * never be called by the user. It is called in open/create when the + * nc logging level has changed. + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +int +hdf5_set_log_level() +{ + /* 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 (nc_log_level == NC_TURN_OFF_LOGGING) + { + set_auto(NULL, NULL); + LOG((1, "HDF5 error messages turned off!")); + } + else + { + if (set_auto((H5E_auto_t)&H5Eprint, stderr) < 0) + LOG((0, "H5Eset_auto failed!")); + LOG((1, "HDF5 error messages turned on.")); + } + + return NC_NOERR; +} +#endif /* LOGGING */ diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5open.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5open.c new file mode 100644 index 0000000000000000000000000000000000000000..42ef8cd2fa93ffc9b6fcf11a2d038b0c6d1742a7 --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5open.c @@ -0,0 +1,2617 @@ +/* Copyright 2003-2018, University Corporation for Atmospheric + * Research. See COPYRIGHT file for copying and redistribution + * conditions. */ +/** + * @file + * @internal This file contains functions that are used in file + * opens. + * + * @author Ed Hartnett + */ + +#include "config.h" +#include "hdf5internal.h" +#include "ncrc.h" +#include "ncmodel.h" + +#ifdef ENABLE_BYTERANGE +#include "H5FDhttp.h" +#endif + +#define NUM_TYPES 12 /**< Number of netCDF atomic types. */ +#define CD_NELEMS_ZLIB 1 /**< Number of parameters needed for ZLIB filter. */ + +/** @internal Native HDF5 constants for atomic types. For performance, + * fill this array only the first time, and keep it in global memory + * for each further use. */ +static hid_t h5_native_type_constant_g[NUM_TYPES]; + +/** @internal NetCDF atomic type names. */ +static const char nc_type_name_g[NUM_TYPES][NC_MAX_NAME + 1] = {"char", "byte", "short", + "int", "float", "double", "ubyte", + "ushort", "uint", "int64", + "uint64", "string"}; + +/** @internal NetCDF atomic types. */ +static const nc_type nc_type_constant_g[NUM_TYPES] = {NC_CHAR, NC_BYTE, NC_SHORT, + NC_INT, NC_FLOAT, NC_DOUBLE, NC_UBYTE, + NC_USHORT, NC_UINT, NC_INT64, + NC_UINT64, NC_STRING}; + +/** @internal NetCDF atomic type sizes. */ +static const int nc_type_size_g[NUM_TYPES] = {sizeof(char), sizeof(char), sizeof(short), + sizeof(int), sizeof(float), sizeof(double), sizeof(unsigned char), + sizeof(unsigned short), sizeof(unsigned int), sizeof(long long), + sizeof(unsigned long long), sizeof(char *)}; + +/** @internal These flags may not be set for open mode. */ +static const int ILLEGAL_OPEN_FLAGS = (NC_MMAP); + +/* From libsrc4, these are the netcdf-4 cache sizes. */ +extern size_t nc4_chunk_cache_size; +extern size_t nc4_chunk_cache_nelems; +extern float nc4_chunk_cache_preemption; + +/* From nc4mem.c */ +extern int NC4_open_image_file(NC_FILE_INFO_T* h5); + +/* Defined later in this file. */ +static int rec_read_metadata(NC_GRP_INFO_T *grp); + +/** + * @internal Struct to track HDF5 object info, for + * rec_read_metadata(). We get this info for every object in the + * HDF5 file when we H5Literate() over the file. */ +typedef struct hdf5_obj_info +{ + hid_t oid; /* HDF5 object ID */ + char oname[NC_MAX_NAME + 1]; /* Name of object */ + H5G_stat_t statbuf; /* Information about the object */ + struct hdf5_obj_info *next; /* Pointer to next node in list */ +} hdf5_obj_info_t; + +/** + * @internal User data struct for call to H5Literate() in + * rec_read_metadata(). When iterating through the objects in a + * group, if we find child groups, we save their hdf5_obj_info_t + * object in a list. Then we processes them after completely + * processing the parent group. */ +typedef struct user_data +{ + NClist *grps; /* NClist<hdf5_obj_info_t*> */ + NC_GRP_INFO_T *grp; /* Pointer to parent group */ +} user_data_t; + +/* Custom iteration callback data */ +typedef struct { + NC_GRP_INFO_T *grp; + NC_VAR_INFO_T *var; +} att_iter_info; + +/** + * @internal Given an HDF5 type, set a pointer to netcdf type_info + * struct, either an existing one (for user-defined types) or a newly + * created one. + * + * @param h5 Pointer to HDF5 file info struct. + * @param datasetid HDF5 dataset ID. + * @param type_info Pointer to pointer that gets type info struct. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_EHDFERR HDF5 returned error. + * @return ::NC_EBADTYPID Type not found. + * @author Ed Hartnett + */ +static int +get_type_info2(NC_FILE_INFO_T *h5, hid_t datasetid, NC_TYPE_INFO_T **type_info) +{ + NC_HDF5_TYPE_INFO_T *hdf5_type; + htri_t is_str, equal = 0; + H5T_class_t class; + hid_t native_typeid, hdf_typeid; + H5T_order_t order; + int t; + + assert(h5 && type_info); + + /* Because these N5T_NATIVE_* constants are actually function calls + * (!) in H5Tpublic.h, I can't initialize this array in the usual + * way, because at least some C compilers (like Irix) complain + * about calling functions when defining constants. So I have to do + * it like this. Note that there's no native types for char or + * string. Those are handled later. */ + if (!h5_native_type_constant_g[1]) + { + h5_native_type_constant_g[1] = H5T_NATIVE_SCHAR; + h5_native_type_constant_g[2] = H5T_NATIVE_SHORT; + h5_native_type_constant_g[3] = H5T_NATIVE_INT; + h5_native_type_constant_g[4] = H5T_NATIVE_FLOAT; + h5_native_type_constant_g[5] = H5T_NATIVE_DOUBLE; + h5_native_type_constant_g[6] = H5T_NATIVE_UCHAR; + h5_native_type_constant_g[7] = H5T_NATIVE_USHORT; + h5_native_type_constant_g[8] = H5T_NATIVE_UINT; + h5_native_type_constant_g[9] = H5T_NATIVE_LLONG; + h5_native_type_constant_g[10] = H5T_NATIVE_ULLONG; + } + + /* Get the HDF5 typeid - we'll need it later. */ + if ((hdf_typeid = H5Dget_type(datasetid)) < 0) + return NC_EHDFERR; + + /* Get the native typeid. Will be equivalent to hdf_typeid when + * creating but not necessarily when reading, a variable. */ + if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0) + return NC_EHDFERR; + + /* Is this type an integer, string, compound, or what? */ + if ((class = H5Tget_class(native_typeid)) < 0) + return NC_EHDFERR; + + /* Is this an atomic type? */ + if (class == H5T_STRING || class == H5T_INTEGER || class == H5T_FLOAT) + { + /* Allocate a phony NC_TYPE_INFO_T struct to hold type info. */ + if (!(*type_info = calloc(1, sizeof(NC_TYPE_INFO_T)))) + return NC_ENOMEM; + + /* Allocate storage for HDF5-specific type info. */ + if (!(hdf5_type = calloc(1, sizeof(NC_HDF5_TYPE_INFO_T)))) + return NC_ENOMEM; + (*type_info)->format_type_info = hdf5_type; + + /* H5Tequal doesn't work with H5T_C_S1 for some reason. But + * H5Tget_class will return H5T_STRING if this is a string. */ + if (class == H5T_STRING) + { + if ((is_str = H5Tis_variable_str(native_typeid)) < 0) + return NC_EHDFERR; + /* Make sure fixed-len strings will work like variable-len + * strings */ + if (is_str || H5Tget_size(hdf_typeid) > 1) + { + /* Set a class for the type */ + t = NUM_TYPES - 1; + (*type_info)->nc_type_class = NC_STRING; + } + else + { + /* Set a class for the type */ + t = 0; + (*type_info)->nc_type_class = NC_CHAR; + } + } + else + { + for (t = 1; t < NUM_TYPES - 1; t++) + { + if ((equal = H5Tequal(native_typeid, + h5_native_type_constant_g[t])) < 0) + return NC_EHDFERR; + if (equal) + break; + } + + /* Find out about endianness. As of HDF 1.8.6, this works + * with all data types Not just H5T_INTEGER. See + * https://www.hdfgroup.org/HDF5/doc/RM/RM_H5T.html#Datatype-GetOrder */ + if ((order = H5Tget_order(hdf_typeid)) < 0) + return NC_EHDFERR; + + if (order == H5T_ORDER_LE) + (*type_info)->endianness = NC_ENDIAN_LITTLE; + else if (order == H5T_ORDER_BE) + (*type_info)->endianness = NC_ENDIAN_BIG; + else + return NC_EBADTYPE; + + if (class == H5T_INTEGER) + (*type_info)->nc_type_class = NC_INT; + else + (*type_info)->nc_type_class = NC_FLOAT; + } + (*type_info)->hdr.id = nc_type_constant_g[t]; + (*type_info)->size = nc_type_size_g[t]; + if (!((*type_info)->hdr.name = strdup(nc_type_name_g[t]))) + return NC_ENOMEM; + hdf5_type->hdf_typeid = hdf_typeid; + hdf5_type->native_hdf_typeid = native_typeid; + return NC_NOERR; + } + else + { + NC_TYPE_INFO_T *type; + + /* This is a user-defined type. */ + if((type = nc4_rec_find_hdf_type(h5, native_typeid))) + *type_info = type; + + /* The type entry in the array of user-defined types already has + * an open data typeid (and native typeid), so close the ones we + * opened above. */ + if (H5Tclose(native_typeid) < 0) + return NC_EHDFERR; + if (H5Tclose(hdf_typeid) < 0) + return NC_EHDFERR; + + if (type) + return NC_NOERR; + } + + return NC_EBADTYPID; +} + +/** + * @internal This function reads the coordinates attribute used for + * multi-dimensional coordinates. It then sets var->dimids[], and + * attempts to find a pointer to the dims and sets var->dim[] as well. + * + * @param grp Group info pointer. + * @param var Var info pointer. + * + * @return ::NC_NOERR No error. + * @return ::NC_ENOTATT Attribute does not exist. + * @return ::NC_EATTMETA Attribute metadata error. + * @return ::NC_EHDFERR HDF5 error. + * @author Ed Hartnett + */ +static int +read_coord_dimids(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var) +{ + NC_HDF5_VAR_INFO_T *hdf5_var; + hid_t coord_att_typeid = -1, coord_attid = -1, spaceid = -1; + hssize_t npoints; + htri_t attr_exists; + int d; + int retval = NC_NOERR; + + assert(grp && var && var->format_var_info); + LOG((3, "%s: var->hdr.name %s", __func__, var->hdr.name)); + + /* Have we already read the coordinates hidden att for this var? */ + if (var->coords_read) + return NC_NOERR; + + /* Get HDF5-sepecific var info. */ + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + /* Does the COORDINATES att exist? */ + if ((attr_exists = H5Aexists(hdf5_var->hdf_datasetid, COORDINATES)) < 0) + return NC_EHDFERR; + if (!attr_exists) + return NC_ENOTATT; + + /* There is a hidden attribute telling us the ids of the + * dimensions that apply to this multi-dimensional coordinate + * variable. Read it. */ + if ((coord_attid = H5Aopen_name(hdf5_var->hdf_datasetid, COORDINATES)) < 0) + BAIL(NC_EATTMETA); + + if ((coord_att_typeid = H5Aget_type(coord_attid)) < 0) + BAIL(NC_EATTMETA); + + /* How many dimensions are there? */ + if ((spaceid = H5Aget_space(coord_attid)) < 0) + BAIL(NC_EATTMETA); + if ((npoints = H5Sget_simple_extent_npoints(spaceid)) < 0) + BAIL(NC_EATTMETA); + + /* Check that the number of points is the same as the number of + * dimensions for the variable. */ + if (npoints != var->ndims) + BAIL(NC_EATTMETA); + + /* Read the dimids for this var. */ + if (H5Aread(coord_attid, coord_att_typeid, var->dimids) < 0) + BAIL(NC_EATTMETA); + LOG((4, "read dimids for this var")); + + /* Update var->dim field based on the var->dimids. Ok if does not + * find a dim at this time, but if found set it. */ + for (d = 0; d < var->ndims; d++) + nc4_find_dim(grp, var->dimids[d], &var->dim[d], NULL); + + /* Remember that we have read the coordinates hidden attribute. */ + var->coords_read = NC_TRUE; + +exit: + if (spaceid >= 0 && H5Sclose(spaceid) < 0) + BAIL2(NC_EHDFERR); + if (coord_att_typeid >= 0 && H5Tclose(coord_att_typeid) < 0) + BAIL2(NC_EHDFERR); + if (coord_attid >= 0 && H5Aclose(coord_attid) < 0) + BAIL2(NC_EHDFERR); + return retval; +} + +/** + * @internal This function is called when reading a file's metadata + * for each dimension scale attached to a variable. + * + * @param did HDF5 ID for dimscale. + * @param dim + * @param dsid + * @param dimscale_hdf5_objids + * + * @return 0 for success, -1 for error. + * @author Ed Hartnett + */ +static herr_t +dimscale_visitor(hid_t did, unsigned dim, hid_t dsid, + void *dimscale_hdf5_objids) +{ + H5G_stat_t statbuf; + + LOG((4, "%s", __func__)); + + /* Get more info on the dimscale object.*/ + if (H5Gget_objinfo(dsid, ".", 1, &statbuf) < 0) + return -1; + + /* Pass this information back to caller. */ + (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[0] = statbuf.fileno[0]; + (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[1] = statbuf.fileno[1]; + (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[0] = statbuf.objno[0]; + (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[1] = statbuf.objno[1]; + return 0; +} + +/** + * @internal For files without any netCDF-4 dimensions defined, create phony + * dimension to match the available datasets. + * + * @param grp Pointer to the group info. + * @param hdf_datasetid HDF5 datsetid for the var's dataset. + * @param var Pointer to the var info. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @returns NC_ENOMEM Out of memory. + * @author Ed Hartnett + */ +static int +create_phony_dims(NC_GRP_INFO_T *grp, hid_t hdf_datasetid, NC_VAR_INFO_T *var) +{ + NC_DIM_INFO_T *dim; + hid_t spaceid = 0; + hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL; + int dataset_ndims; + int d; + int retval = NC_NOERR; + + /* Find the space information for this dimension. */ + if ((spaceid = H5Dget_space(hdf_datasetid)) < 0) + BAIL(NC_EHDFERR); + + /* Get the len of each dim in the space. */ + if (var->ndims) + { + /* Allocate storage for dim lens and max lens for this var. */ + if (!(h5dimlen = malloc(var->ndims * sizeof(hsize_t)))) + return NC_ENOMEM; + if (!(h5dimlenmax = malloc(var->ndims * sizeof(hsize_t)))) + BAIL(NC_ENOMEM); + + /* Get ndims, also len and mac len of all dims. */ + if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, h5dimlen, + h5dimlenmax)) < 0) + BAIL(NC_EHDFERR); + assert(dataset_ndims == var->ndims); + } + else + { + /* Make sure it's scalar. */ + assert(H5Sget_simple_extent_type(spaceid) == H5S_SCALAR); + } + + /* Create a phony dimension for each dimension in the dataset, + * unless there already is one the correct size. */ + for (d = 0; d < var->ndims; d++) + { + int k; + int match; + + /* Is there already a phony dimension of the correct size? */ + for (match=-1, k = 0; k < ncindexsize(grp->dim); k++) + { + dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, k); + assert(dim); + if ((dim->len == h5dimlen[d]) && + ((h5dimlenmax[d] == H5S_UNLIMITED && dim->unlimited) || + (h5dimlenmax[d] != H5S_UNLIMITED && !dim->unlimited))) + {match = k; break;} + } + + /* Didn't find a phony dim? Then create one. */ + if (match < 0) + { + char phony_dim_name[NC_MAX_NAME + 1]; + sprintf(phony_dim_name, "phony_dim_%d", grp->nc4_info->next_dimid); + LOG((3, "%s: creating phony dim for var %s", __func__, var->hdr.name)); + + /* Add phony dim to metadata list. */ + if ((retval = nc4_dim_list_add(grp, phony_dim_name, h5dimlen[d], -1, &dim))) + BAIL(retval); + + /* Create struct for HDF5-specific dim info. */ + if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T)))) + BAIL(NC_ENOMEM); + if (h5dimlenmax[d] == H5S_UNLIMITED) + dim->unlimited = NC_TRUE; + } + + /* The variable must remember the dimid. */ + var->dimids[d] = dim->hdr.id; + var->dim[d] = dim; + } /* next dim */ + +exit: + /* Free resources. */ + if (spaceid > 0 && H5Sclose(spaceid) < 0) + BAIL2(NC_EHDFERR); + if (h5dimlenmax) + free(h5dimlenmax); + if (h5dimlen) + free(h5dimlen); + + return retval; +} + +/** + * @internal Iterate through the vars in this file and make sure we've + * got a dimid and a pointer to a dim for each dimension. This may + * already have been done using the COORDINATES hidden attribute, in + * which case this function will not have to do anything. This is + * desirable because recurdively matching the dimscales (when + * necessary) is very much the slowest part of opening a file. + * + * @param grp Pointer to group info struct. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @returns NC_ENOMEM Out of memory. + * @author Ed Hartnett + */ +static int +rec_match_dimscales(NC_GRP_INFO_T *grp) +{ + NC_VAR_INFO_T *var; + NC_DIM_INFO_T *dim; + int retval = NC_NOERR; + int i; + + assert(grp && grp->hdr.name); + LOG((4, "%s: grp->hdr.name %s", __func__, grp->hdr.name)); + + /* Perform var dimscale match for child groups. */ + for (i = 0; i < ncindexsize(grp->children); i++) + if ((retval = rec_match_dimscales((NC_GRP_INFO_T *)ncindexith(grp->children, i)))) + return retval; + + /* Check all the vars in this group. If they have dimscale info, + * try and find a dimension for them. */ + for (i = 0; i < ncindexsize(grp->vars); i++) + { + NC_HDF5_VAR_INFO_T *hdf5_var; + int d; + + /* Get pointer to var and to the HDF5-specific var info. */ + var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i); + assert(var && var->format_var_info); + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + /* Check all vars and see if dim[i] != NULL if dimids[i] + * valid. Recall that dimids were initialized to -1. */ + for (d = 0; d < var->ndims; d++) + { + if (!var->dim[d]) + nc4_find_dim(grp, var->dimids[d], &var->dim[d], NULL); + } + + /* Skip dimension scale variables */ + if (var->dimscale) + continue; + + /* If we have already read hidden coordinates att, then we don't + * have to match dimscales for this var. */ + if (var->coords_read) + continue; + + /* Skip dimension scale variables */ + if (!var->dimscale) + { + int d; + int j; + + /* Are there dimscales for this variable? */ + if (hdf5_var->dimscale_hdf5_objids) + { + for (d = 0; d < var->ndims; d++) + { + NC_GRP_INFO_T *g; + nc_bool_t finished = NC_FALSE; + LOG((5, "%s: var %s has dimscale info...", __func__, var->hdr.name)); + + /* If we already have the dimension, we don't need to + * match the dimscales. This is better because matching + * the dimscales is slow. */ + if (var->dim[d]) + continue; + + /* Now we have to try to match dimscales. Check this + * and parent groups. */ + for (g = grp; g && !finished; g = g->parent) + { + /* Check all dims in this group. */ + for (j = 0; j < ncindexsize(g->dim); j++) + { + /* Get the HDF5 specific dim info. */ + NC_HDF5_DIM_INFO_T *hdf5_dim; + dim = (NC_DIM_INFO_T *)ncindexith(g->dim, j); + assert(dim && dim->format_dim_info); + hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; + + /* Check for exact match of fileno/objid arrays + * to find identical objects in HDF5 file. */ + if (hdf5_var->dimscale_hdf5_objids[d].fileno[0] == hdf5_dim->hdf5_objid.fileno[0] && + hdf5_var->dimscale_hdf5_objids[d].objno[0] == hdf5_dim->hdf5_objid.objno[0] && + hdf5_var->dimscale_hdf5_objids[d].fileno[1] == hdf5_dim->hdf5_objid.fileno[1] && + hdf5_var->dimscale_hdf5_objids[d].objno[1] == hdf5_dim->hdf5_objid.objno[1]) + { + LOG((4, "%s: for dimension %d, found dim %s", __func__, + d, dim->hdr.name)); + var->dimids[d] = dim->hdr.id; + var->dim[d] = dim; + finished = NC_TRUE; + break; + } + } /* next dim */ + } /* next grp */ + } /* next var->dim */ + } + else + { + /* No dimscales for this var! Invent phony dimensions. */ + if ((retval = create_phony_dims(grp, hdf5_var->hdf_datasetid, var))) + return retval; + } + } + } + + return retval; +} + +/** + * @internal Check for the attribute that indicates that netcdf + * classic model is in use. + * + * @param root_grp pointer to the group info for the root group of the + * @param is_classic store 1 if this is a classic file. + * file. + * + * @return NC_NOERR No error. + * @author Ed Hartnett + */ +static int +check_for_classic_model(NC_GRP_INFO_T *root_grp, int *is_classic) +{ + htri_t attr_exists; + hid_t grpid; + + /* Check inputs. */ + assert(root_grp && root_grp->format_grp_info && !root_grp->parent + && is_classic); + + /* Get the HDF5 group id. */ + grpid = ((NC_HDF5_GRP_INFO_T *)(root_grp->format_grp_info))->hdf_grpid; + + /* If this attribute exists in the root group, then classic model + * is in effect. */ + if ((attr_exists = H5Aexists(grpid, NC3_STRICT_ATT_NAME)) < 0) + return NC_EHDFERR; + *is_classic = attr_exists ? 1 : 0; + + return NC_NOERR; +} + +/** + * @internal Open a netcdf-4 file. Things have already been kicked off + * in ncfunc.c in nc_open, but here the netCDF-4 part of opening a + * file is handled. + * + * @param path The file name of the new file. + * @param mode The open mode flag. + * @param parameters File parameters. + * @param nc Pointer to NC file info. + * + * @return ::NC_NOERR No error. + * @return ::NC_ENOMEM Out of memory. + * @return ::NC_EINTERNAL Internal list error. + * @return ::NC_EHDFERR HDF error. + * @return ::NC_EMPI MPI error for parallel. + * @return ::NC_EPARINIT Parallel I/O initialization error. + * @return ::NC_EINMEMMORY Memory file error. + * @author Ed Hartnett, Dennis Heimbigner + */ +static int +nc4_open_file(const char *path, int mode, void* parameters, NC *nc) +{ + hid_t fapl_id = H5P_DEFAULT; + int retval; + unsigned flags; + NC_FILE_INFO_T *nc4_info = NULL; + int is_classic; + NC_HDF5_FILE_INFO_T *h5 = NULL; + +#ifdef USE_PARALLEL4 + NC_MPI_INFO* mpiinfo = NULL; + int comm_duped = 0; /* Whether the MPI Communicator was duplicated */ + int info_duped = 0; /* Whether the MPI Info object was duplicated */ +#endif + + LOG((3, "%s: path %s mode %d", __func__, path, mode)); + assert(path && nc); + + flags = (mode & NC_WRITE) ? H5F_ACC_RDWR : H5F_ACC_RDONLY; + + /* Add necessary structs to hold netcdf-4 file data. */ + if ((retval = nc4_nc4f_list_add(nc, path, mode))) + BAIL(retval); + nc4_info = NC4_DATA(nc); + assert(nc4_info && nc4_info->root_grp); + + /* Add struct to hold HDF5-specific file metadata. */ + if (!(nc4_info->format_file_info = calloc(1, sizeof(NC_HDF5_FILE_INFO_T)))) + BAIL(NC_ENOMEM); + + /* Add struct to hold HDF5-specific group info. */ + if (!(nc4_info->root_grp->format_grp_info = calloc(1, sizeof(NC_HDF5_GRP_INFO_T)))) + BAIL(NC_ENOMEM); + + h5 = (NC_HDF5_FILE_INFO_T*)nc4_info->format_file_info; + +#ifdef ENABLE_BYTERANGE + /* See if we want the byte range protocol */ + if(nc->model->iosp == NC_IOSP_HTTP) { + h5->http.iosp = 1; + /* Kill off any conflicting modes flags */ + mode &= ~(NC_WRITE|NC_DISKLESS|NC_PERSIST|NC_INMEMORY); + parameters = NULL; /* kill off parallel */ + } else + h5->http.iosp = 0; +#endif /*ENABLE_BYTERANGE*/ + + nc4_info->mem.inmemory = ((mode & NC_INMEMORY) == NC_INMEMORY); + nc4_info->mem.diskless = ((mode & NC_DISKLESS) == NC_DISKLESS); + nc4_info->mem.persist = ((mode & NC_PERSIST) == NC_PERSIST); + + /* Does the mode specify that this file is read-only? */ + if ((mode & NC_WRITE) == 0) + nc4_info->no_write = NC_TRUE; + + if(nc4_info->mem.inmemory && nc4_info->mem.diskless) + BAIL(NC_EINTERNAL); + +#ifdef USE_PARALLEL4 + mpiinfo = (NC_MPI_INFO*)parameters; /* assume, may be changed if inmemory is true */ +#endif /* !USE_PARALLEL4 */ + + /* Need this access plist to control how HDF5 handles open objects + * on file close. (Setting H5F_CLOSE_SEMI will cause H5Fclose to + * fail if there are any open objects in the file). */ + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + BAIL(NC_EHDFERR); + + if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_SEMI) < 0) + BAIL(NC_EHDFERR); + +#ifdef USE_PARALLEL4 + if (!(mode & (NC_INMEMORY | NC_DISKLESS)) && mpiinfo != NULL) { + /* If this is a parallel file create, set up the file creation + * property list. + */ + nc4_info->parallel = NC_TRUE; + LOG((4, "opening parallel file with MPI/IO")); + if (H5Pset_fapl_mpio(fapl_id, mpiinfo->comm, mpiinfo->info) < 0) + BAIL(NC_EPARINIT); + + /* Keep copies of the MPI Comm & Info objects */ + if (MPI_SUCCESS != MPI_Comm_dup(mpiinfo->comm, &nc4_info->comm)) + BAIL(NC_EMPI); + comm_duped++; + if (MPI_INFO_NULL != mpiinfo->info) + { + if (MPI_SUCCESS != MPI_Info_dup(mpiinfo->info, &nc4_info->info)) + BAIL(NC_EMPI); + info_duped++; + } + else + { + /* No dup, just copy it. */ + nc4_info->info = mpiinfo->info; + } + } + +#ifdef HDF5_HAS_COLL_METADATA_OPS + if (H5Pset_all_coll_metadata_ops(fapl_id, 1) < 0) + BAIL(NC_EPARINIT); +#endif + +#else /* only set cache for non-parallel. */ + if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size, + nc4_chunk_cache_preemption) < 0) + BAIL(NC_EHDFERR); + LOG((4, "%s: set HDF raw chunk cache to size %d nelems %d preemption %f", + __func__, nc4_chunk_cache_size, nc4_chunk_cache_nelems, + nc4_chunk_cache_preemption)); +#endif /* USE_PARALLEL4 */ + + /* Process NC_INMEMORY */ + if(nc4_info->mem.inmemory) { + NC_memio* memio; + /* validate */ + if(parameters == NULL) + BAIL(NC_EINMEMORY); + memio = (NC_memio*)parameters; + if(memio->memory == NULL || memio->size == 0) + BAIL(NC_EINMEMORY); + /* initialize h5->mem */ + nc4_info->mem.memio = *memio; + /* Is the incoming memory locked? */ + nc4_info->mem.locked = (nc4_info->mem.memio.flags & NC_MEMIO_LOCKED) == NC_MEMIO_LOCKED; + /* As a safeguard, if not locked and not read-only, + then we must take control of the incoming memory */ + if(!nc4_info->mem.locked && !nc4_info->no_write) { + memio->memory = NULL; /* take control */ + memio->size = 0; + } + retval = NC4_open_image_file(nc4_info); + if(retval) + BAIL(NC_EHDFERR); + } + else + if(nc4_info->mem.diskless) { /* Process NC_DISKLESS */ + size_t min_incr = 65536; /* Minimum buffer increment */ + /* Configure FAPL to use the core file driver */ + if (H5Pset_fapl_core(fapl_id, min_incr, (nc4_info->mem.persist?1:0)) < 0) + BAIL(NC_EHDFERR); + /* Open the HDF5 file. */ + if ((h5->hdfid = H5Fopen(path, flags, fapl_id)) < 0) + BAIL(NC_EHDFERR); + } +#ifdef ENABLE_BYTERANGE + else + if(h5->http.iosp) { /* Arrange to use the byte-range driver */ + /* Configure FAPL to use the byte-range file driver */ + if (H5Pset_fapl_http(fapl_id) < 0) + BAIL(NC_EHDFERR); + /* Open the HDF5 file. */ + if ((h5->hdfid = H5Fopen(path, flags, fapl_id)) < 0) + BAIL(NC_EHDFERR); + } +#endif + else + { + /* Open the HDF5 file. */ + if ((h5->hdfid = H5Fopen(path, flags, fapl_id)) < 0) + BAIL(NC_EHDFERR); + } + + /* Now read in all the metadata. Some types and dimscale + * information may be difficult to resolve here, if, for example, a + * dataset of user-defined type is encountered before the + * definition of that type. */ + if ((retval = rec_read_metadata(nc4_info->root_grp))) + BAIL(retval); + + /* Check for classic model attribute. */ + if ((retval = check_for_classic_model(nc4_info->root_grp, &is_classic))) + BAIL(retval); + if (is_classic) + nc4_info->cmode |= NC_CLASSIC_MODEL; + + /* Set the provenance info for this file */ + if ((retval = NC4_read_provenance(nc4_info))) + BAIL(retval); + + /* Now figure out which netCDF dims are indicated by the dimscale + * information. */ + if ((retval = rec_match_dimscales(nc4_info->root_grp))) + BAIL(retval); + +#ifdef LOGGING + /* This will print out the names, types, lens, etc of the vars and + atts in the file, if the logging level is 2 or greater. */ + log_metadata_nc(nc4_info); +#endif + + /* Close the property list. */ + if (H5Pclose(fapl_id) < 0) + BAIL(NC_EHDFERR); + + return NC_NOERR; + +exit: +#ifdef USE_PARALLEL4 + if (comm_duped) MPI_Comm_free(&nc4_info->comm); + if (info_duped) MPI_Info_free(&nc4_info->info); +#endif + + if (fapl_id > 0 && fapl_id != H5P_DEFAULT) + H5Pclose(fapl_id); + if (nc4_info) + nc4_close_hdf5_file(nc4_info, 1, 0); /* treat like abort*/ + return retval; +} + +/** + * @internal Open a netCDF-4 file. + * + * @param path The file name of the new file. + * @param mode The open mode flag. + * @param basepe Ignored by this function. + * @param chunksizehintp Ignored by this function. + * @param parameters pointer to struct holding extra data (e.g. for parallel I/O) + * layer. Ignored if NULL. + * @param dispatch Pointer to the dispatch table for this file. + * @param nc_file Pointer to an instance of NC. + * + * @return ::NC_NOERR No error. + * @return ::NC_EINVAL Invalid inputs. + * @author Ed Hartnett + */ +int +NC4_open(const char *path, int mode, int basepe, size_t *chunksizehintp, + void *parameters, NC_Dispatch *dispatch, NC *nc_file) +{ + assert(nc_file && path && dispatch && nc_file && + nc_file->model->impl == NC_FORMATX_NC4); + + LOG((1, "%s: path %s mode %d params %x", + __func__, path, mode, parameters)); + + /* Check the mode for validity */ + if (mode & ILLEGAL_OPEN_FLAGS) + return NC_EINVAL; + + if((mode & NC_DISKLESS) && (mode & NC_INMEMORY)) + return NC_EINVAL; + + /* If this is our first file, initialize HDF5. */ + if (!nc4_hdf5_initialized) + nc4_hdf5_initialize(); + +#ifdef LOGGING + /* If nc logging level has changed, see if we need to turn on + * HDF5's error messages. */ + hdf5_set_log_level(); +#endif /* LOGGING */ + + nc_file->int_ncid = nc_file->ext_ncid; + + /* Open the file. */ + return nc4_open_file(path, mode, parameters, nc_file); +} + +/** + * @internal Find out what filters are applied to this HDF5 dataset, + * fletcher32, deflate, and/or shuffle. All other filters are just + * dumped The possible values of + * + * @param propid ID of HDF5 var creation properties list. + * @param var Pointer to NC_VAR_INFO_T for this variable. + * + * @return ::NC_NOERR No error. + * @return ::NC_ENOMEM Out of memory. + * @return ::NC_EHDFERR HDF5 returned error. + * @author Dennis Heimbigner, Ed Hartnett + */ +static int get_filter_info(hid_t propid, NC_VAR_INFO_T *var) +{ + H5Z_filter_t filter; + int num_filters; + unsigned int cd_values_zip[CD_NELEMS_ZLIB]; + size_t cd_nelems = CD_NELEMS_ZLIB; + int f; + + assert(var); + + if ((num_filters = H5Pget_nfilters(propid)) < 0) + return NC_EHDFERR; + + for (f = 0; f < num_filters; f++) + { + if ((filter = H5Pget_filter2(propid, f, NULL, &cd_nelems, cd_values_zip, + 0, NULL, NULL)) < 0) + return NC_EHDFERR; + switch (filter) + { + case H5Z_FILTER_SHUFFLE: + var->shuffle = NC_TRUE; + break; + + case H5Z_FILTER_FLETCHER32: + var->fletcher32 = NC_TRUE; + break; + + case H5Z_FILTER_DEFLATE: + var->deflate = NC_TRUE; + if (cd_nelems != CD_NELEMS_ZLIB || + cd_values_zip[0] > NC_MAX_DEFLATE_LEVEL) + return NC_EHDFERR; + var->deflate_level = cd_values_zip[0]; + break; + + case H5Z_FILTER_SZIP: + /* Szip is tricky because the filter code expands the set of parameters from 2 to 4 + and changes some of the parameter values */ + var->filterid = filter; + if(cd_nelems == 0) + var->params = NULL; + else { + /* We have to re-read the parameters based on actual nparams, + which in the case of szip, differs from users original nparams */ + var->params = (unsigned int*)calloc(1,sizeof(unsigned int)*cd_nelems); + if(var->params == NULL) + return NC_ENOMEM; + if((filter = H5Pget_filter2(propid, f, NULL, &cd_nelems, + var->params, 0, NULL, NULL)) < 0) + return NC_EHDFERR; + /* fix up the parameters and the #params */ + var->nparams = cd_nelems; + } + break; + + default: + var->filterid = filter; + var->nparams = cd_nelems; + if(cd_nelems == 0) + var->params = NULL; + else { + /* We have to re-read the parameters based on actual nparams */ + var->params = (unsigned int*)calloc(1,sizeof(unsigned int)*var->nparams); + if(var->params == NULL) + return NC_ENOMEM; + if((filter = H5Pget_filter2(propid, f, NULL, &cd_nelems, + var->params, 0, NULL, NULL)) < 0) + return NC_EHDFERR; + } + break; + } + } + return NC_NOERR; +} + +/** + * @internal Learn if there is a fill value defined for a variable, + * and, if so, its value. + * + * @param propid ID of HDF5 var creation properties list. + * @param var Pointer to NC_VAR_INFO_T for this variable. + * + * @return ::NC_NOERR No error. + * @return ::NC_ENOMEM Out of memory. + * @return ::NC_EHDFERR HDF5 returned error. + * @author Dennis Heimbigner, Ed Hartnett + */ +static int get_fill_info(hid_t propid, NC_VAR_INFO_T *var) +{ + H5D_fill_value_t fill_status; + + /* Is there a fill value associated with this dataset? */ + if (H5Pfill_value_defined(propid, &fill_status) < 0) + return NC_EHDFERR; + + /* Get the fill value, if there is one defined. */ + if (fill_status == H5D_FILL_VALUE_USER_DEFINED) + { + /* Allocate space to hold the fill value. */ + if (!var->fill_value) + { + if (var->type_info->nc_type_class == NC_VLEN) + { + if (!(var->fill_value = malloc(sizeof(nc_vlen_t)))) + return NC_ENOMEM; + } + else if (var->type_info->nc_type_class == NC_STRING) + { + if (!(var->fill_value = malloc(sizeof(char *)))) + return NC_ENOMEM; + } + else + { + assert(var->type_info->size); + if (!(var->fill_value = malloc(var->type_info->size))) + return NC_ENOMEM; + } + } + + /* Get the fill value from the HDF5 property lust. */ + if (H5Pget_fill_value(propid, ((NC_HDF5_TYPE_INFO_T *)var->type_info->format_type_info)->native_hdf_typeid, + var->fill_value) < 0) + return NC_EHDFERR; + } + else + var->no_fill = NC_TRUE; + + return NC_NOERR; +} + +/** + * @internal Learn the chunking settings of a var. + * + * @param propid ID of HDF5 var creation properties list. + * @param var Pointer to NC_VAR_INFO_T for this variable. + * + * @return ::NC_NOERR No error. + * @return ::NC_ENOMEM Out of memory. + * @return ::NC_EHDFERR HDF5 returned error. + * @author Dennis Heimbigner, Ed Hartnett + */ +static int get_chunking_info(hid_t propid, NC_VAR_INFO_T *var) +{ + H5D_layout_t layout; + hsize_t chunksize[H5S_MAX_RANK] = {0}; + int d; + + /* Get the chunking info the var. */ + if ((layout = H5Pget_layout(propid)) < -1) + return NC_EHDFERR; + + /* Remember the layout and, if chunked, the chunksizes. */ + if (layout == H5D_CHUNKED) + { + if (H5Pget_chunk(propid, H5S_MAX_RANK, chunksize) < 0) + return NC_EHDFERR; + if (!(var->chunksizes = malloc(var->ndims * sizeof(size_t)))) + return NC_ENOMEM; + for (d = 0; d < var->ndims; d++) + var->chunksizes[d] = chunksize[d]; + } + else if (layout == H5D_CONTIGUOUS || layout == H5D_COMPACT) + var->contiguous = NC_TRUE; + + return NC_NOERR; +} + +/** + * @internal This function gets info about the dimscales attached to a + * dataset. The info is used later for dimscale matching. + * + * @param var Pointer to var info struct. + * @param hdf5_var Pointer to HDF5 var info struct. + * @param ndims Number of dims for this var. + * @param datasetid HDF5 datasetid. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_ENOMEM Out of memory. + * @return ::NC_EHDFERR HDF5 returned error. + * @return ::NC_EVARMETA Error with var metadata. + * @author Ed Hartnett, Dennis Heimbigner + */ +static int +get_attached_info(NC_VAR_INFO_T *var, NC_HDF5_VAR_INFO_T *hdf5_var, int ndims, + hid_t datasetid) +{ + int d; + int num_scales = 0; + + LOG((4, "%s ndims %d datasetid %ld", __func__, ndims, datasetid)); + + /* Find out how many scales are attached to this + * dataset. H5DSget_num_scales returns an error if there are no + * scales, so convert a negative return value to zero. */ + num_scales = H5DSget_num_scales(datasetid, 0); + if (num_scales < 0) + num_scales = 0; + LOG((4, "num_scales %d", num_scales)); + + /* If an enddef has already been called, the dimscales will already + * be taken care of. */ + if (num_scales && ndims && !var->dimscale_attached) + { + /* Allocate space to remember whether the dimscale has been + * attached for each dimension, and the HDF5 object IDs of the + * scale(s). */ + assert(!hdf5_var->dimscale_hdf5_objids); + if (!(var->dimscale_attached = calloc(ndims, sizeof(nc_bool_t)))) + return NC_ENOMEM; + if (!(hdf5_var->dimscale_hdf5_objids = malloc(ndims * + sizeof(struct hdf5_objid)))) + return NC_ENOMEM; + + /* Store id information allowing us to match hdf5 dimscales to + * netcdf dimensions. */ + for (d = 0; d < var->ndims; d++) + { + LOG((4, "about to iterate scales for dim %d", d)); + if (H5DSiterate_scales(hdf5_var->hdf_datasetid, d, NULL, dimscale_visitor, + &(hdf5_var->dimscale_hdf5_objids[d])) < 0) + return NC_EHDFERR; + var->dimscale_attached[d] = NC_TRUE; + LOG((4, "dimscale attached")); + } + } + + return NC_NOERR; +} + +/** + * @internal This function reads scale info for vars, whether they + * are scales or not. + * + * @param grp Pointer to group info struct. + * @param dim Pointer to dim info struct if this is a scale, NULL + * otherwise. + * @param var Pointer to var info struct. + * @param hdf5_var Pointer to HDF5 var info struct. + * @param ndims Number of dims for this var. + * @param datasetid HDF5 datasetid. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_ENOMEM Out of memory. + * @return ::NC_EHDFERR HDF5 returned error. + * @return ::NC_EVARMETA Error with var metadata. + * @author Ed Hartnett, Dennis Heimbigner + */ +static int +get_scale_info(NC_GRP_INFO_T *grp, NC_DIM_INFO_T *dim, NC_VAR_INFO_T *var, + NC_HDF5_VAR_INFO_T *hdf5_var, int ndims, hid_t datasetid) +{ + int retval; + + /* If it's a scale, mark it as such. */ + if (dim) + { + assert(ndims); + var->dimscale = NC_TRUE; + + /* If this is a multi-dimensional coordinate var, then the + * dimids must be stored in the hidden coordinates attribute. */ + if (var->ndims > 1) + { + if ((retval = read_coord_dimids(grp, var))) + return retval; + } + else + { + /* This is a 1-dimensional coordinate var. */ + assert(!strcmp(var->hdr.name, dim->hdr.name)); + var->dimids[0] = dim->hdr.id; + var->dim[0] = dim; + } + dim->coord_var = var; + } + else /* Not a scale. */ + { + if (!var->coords_read) + if ((retval = get_attached_info(var, hdf5_var, ndims, datasetid))) + return retval; + } + + return NC_NOERR; +} + +/** + * @internal Get the metadata for a variable. + * + * @param var Pointer to var info struct. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_ENOMEM Out of memory. + * @return ::NC_EHDFERR HDF5 returned error. + * @return ::NC_EVARMETA Error with var metadata. + * @author Ed Hartnett + */ +int +nc4_get_var_meta(NC_VAR_INFO_T *var) +{ + NC_HDF5_VAR_INFO_T *hdf5_var; + hid_t access_pid = 0; + hid_t propid = 0; + double rdcc_w0; + int retval = NC_NOERR; + + assert(var && var->format_var_info); + LOG((3, "%s: var %s", __func__, var->hdr.name)); + + /* Have we already read the var metadata? */ + if (var->meta_read) + return NC_NOERR; + + /* Get pointer to the HDF5-specific var info struct. */ + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + /* Get the current chunk cache settings. */ + if ((access_pid = H5Dget_access_plist(hdf5_var->hdf_datasetid)) < 0) + BAIL(NC_EVARMETA); + + /* Learn about current chunk cache settings. */ + if ((H5Pget_chunk_cache(access_pid, &(var->chunk_cache_nelems), + &(var->chunk_cache_size), &rdcc_w0)) < 0) + BAIL(NC_EHDFERR); + var->chunk_cache_preemption = rdcc_w0; + + /* Get the dataset creation properties. */ + if ((propid = H5Dget_create_plist(hdf5_var->hdf_datasetid)) < 0) + BAIL(NC_EHDFERR); + + /* Get var chunking info. */ + if ((retval = get_chunking_info(propid, var))) + BAIL(retval); + + /* Get filter info for a var. */ + if ((retval = get_filter_info(propid, var))) + BAIL(retval); + + /* Get fill value, if defined. */ + if ((retval = get_fill_info(propid, var))) + BAIL(retval); + + /* Is this a deflated variable with a chunksize greater than the + * current cache size? */ + if ((retval = nc4_adjust_var_cache(var->container, var))) + BAIL(retval); + + if (var->coords_read && !var->dimscale) + if ((retval = get_attached_info(var, hdf5_var, var->ndims, hdf5_var->hdf_datasetid))) + return retval; + + /* Remember that we have read the metadata for this var. */ + var->meta_read = NC_TRUE; + +exit: + if (access_pid && H5Pclose(access_pid) < 0) + BAIL2(NC_EHDFERR); + if (propid > 0 && H5Pclose(propid) < 0) + BAIL2(NC_EHDFERR); + return retval; +} + +/** + * @internal This function is called by read_dataset(), (which is + * called by rec_read_metadata()) when a netCDF variable is found in + * the file. This function reads in all the metadata about the + * var. Attributes are not read until the user asks for information + * about one of them. + * + * @param grp Pointer to group info struct. + * @param datasetid HDF5 dataset ID. + * @param obj_name Name of the HDF5 object to read. + * @param ndims Number of dimensions. + * @param dim If non-NULL, then this var is a coordinate var for a + * dimension, and this points to the info for that dimension. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_ENOMEM Out of memory. + * @return ::NC_EHDFERR HDF5 returned error. + * @return ::NC_EVARMETA Error with var metadata. + * @author Ed Hartnett, Dennis Heimbigner + */ +static int +read_var(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name, + size_t ndims, NC_DIM_INFO_T *dim) +{ + NC_VAR_INFO_T *var = NULL; + NC_HDF5_VAR_INFO_T *hdf5_var; + int incr_id_rc = 0; /* Whether dataset ID's ref count has been incremented */ + char *finalname = NULL; + int retval = NC_NOERR; + + assert(obj_name && grp); + LOG((4, "%s: obj_name %s", __func__, obj_name)); + + /* Check for a weird case: a non-coordinate variable that has the + * same name as a dimension. It's legal in netcdf, and requires + * that the HDF5 dataset name be changed. */ + if (strlen(obj_name) > strlen(NON_COORD_PREPEND) && + !strncmp(obj_name, NON_COORD_PREPEND, strlen(NON_COORD_PREPEND))) + { + /* Allocate space for the name. */ + if (!(finalname = malloc(((strlen(obj_name) - + strlen(NON_COORD_PREPEND))+ 1) * sizeof(char)))) + BAIL(NC_ENOMEM); + strcpy(finalname, &obj_name[strlen(NON_COORD_PREPEND)]); + } else + finalname = strdup(obj_name); + + /* Add a variable to the end of the group's var list. */ + if ((retval = nc4_var_list_add(grp, finalname, ndims, &var))) + BAIL(retval); + + /* Add storage for HDF5-specific var info. */ + if (!(var->format_var_info = calloc(1, sizeof(NC_HDF5_VAR_INFO_T)))) + BAIL(NC_ENOMEM); + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + /* Fill in what we already know. */ + hdf5_var->hdf_datasetid = datasetid; + H5Iinc_ref(hdf5_var->hdf_datasetid); /* Increment number of objects using ID */ + incr_id_rc++; /* Indicate that we've incremented the ref. count (for errors) */ + var->created = NC_TRUE; + var->atts_read = 0; + + /* Try and read the dimids from the COORDINATES attribute. If it's + * not present, we will have to do dimsscale matching to locate the + * dims for this var. */ + retval = read_coord_dimids(grp, var); + if (retval && retval != NC_ENOTATT) + BAIL(retval); + retval = NC_NOERR; + + /* Handle scale info. */ + if ((retval = get_scale_info(grp, dim, var, hdf5_var, ndims, datasetid))) + BAIL(retval); + + /* Learn all about the type of this variable. This will fail for + * HDF5 reference types, and then the var we just created will be + * deleted, thus ignoring HDF5 reference type objects. */ + if ((retval = get_type_info2(var->container->nc4_info, hdf5_var->hdf_datasetid, + &var->type_info))) + BAIL(retval); + + /* Indicate that the variable has a pointer to the type */ + var->type_info->rc++; + +exit: + if (finalname) + free(finalname); + if (retval) + { + /* If there was an error, decrement the dataset ref counter, and + * delete the var info struct we just created. */ + if (incr_id_rc && H5Idec_ref(datasetid) < 0) + BAIL2(NC_EHDFERR); + if (var) + nc4_var_list_del(grp, var); + } + + return retval; +} + +/** + * @internal Given an HDF5 type, set a pointer to netcdf type. + * + * @param h5 Pointer to HDF5 file info struct. + * @param native_typeid HDF5 type ID. + * @param xtype Pointer that gets netCDF type. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_EHDFERR HDF5 returned error. + * @return ::NC_EBADTYPID Type not found. + * @author Ed Hartnett + */ +static int +get_netcdf_type(NC_FILE_INFO_T *h5, hid_t native_typeid, + nc_type *xtype) +{ + NC_TYPE_INFO_T *type; + H5T_class_t class; + htri_t is_str, equal = 0; + + assert(h5 && xtype); + + if ((class = H5Tget_class(native_typeid)) < 0) + return NC_EHDFERR; + + /* H5Tequal doesn't work with H5T_C_S1 for some reason. But + * H5Tget_class will return H5T_STRING if this is a string. */ + if (class == H5T_STRING) + { + if ((is_str = H5Tis_variable_str(native_typeid)) < 0) + return NC_EHDFERR; + if (is_str) + *xtype = NC_STRING; + else + *xtype = NC_CHAR; + return NC_NOERR; + } + else if (class == H5T_INTEGER || class == H5T_FLOAT) + { + /* For integers and floats, we don't have to worry about + * endianness if we compare native types. */ + if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SCHAR)) < 0) + return NC_EHDFERR; + if (equal) + { + *xtype = NC_BYTE; + return NC_NOERR; + } + if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SHORT)) < 0) + return NC_EHDFERR; + if (equal) + { + *xtype = NC_SHORT; + return NC_NOERR; + } + if ((equal = H5Tequal(native_typeid, H5T_NATIVE_INT)) < 0) + return NC_EHDFERR; + if (equal) + { + *xtype = NC_INT; + return NC_NOERR; + } + if ((equal = H5Tequal(native_typeid, H5T_NATIVE_FLOAT)) < 0) + return NC_EHDFERR; + if (equal) + { + *xtype = NC_FLOAT; + return NC_NOERR; + } + if ((equal = H5Tequal(native_typeid, H5T_NATIVE_DOUBLE)) < 0) + return NC_EHDFERR; + if (equal) + { + *xtype = NC_DOUBLE; + return NC_NOERR; + } + if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UCHAR)) < 0) + return NC_EHDFERR; + if (equal) + { + *xtype = NC_UBYTE; + return NC_NOERR; + } + if ((equal = H5Tequal(native_typeid, H5T_NATIVE_USHORT)) < 0) + return NC_EHDFERR; + if (equal) + { + *xtype = NC_USHORT; + return NC_NOERR; + } + if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UINT)) < 0) + return NC_EHDFERR; + if (equal) + { + *xtype = NC_UINT; + return NC_NOERR; + } + if ((equal = H5Tequal(native_typeid, H5T_NATIVE_LLONG)) < 0) + return NC_EHDFERR; + if (equal) + { + *xtype = NC_INT64; + return NC_NOERR; + } + if ((equal = H5Tequal(native_typeid, H5T_NATIVE_ULLONG)) < 0) + return NC_EHDFERR; + if (equal) + { + *xtype = NC_UINT64; + return NC_NOERR; + } + } + + /* Maybe we already know about this type. */ + if (!equal) + if((type = nc4_rec_find_hdf_type(h5, native_typeid))) + { + *xtype = type->hdr.id; + return NC_NOERR; + } + + *xtype = NC_NAT; + return NC_EBADTYPID; +} + +/** + * @internal Read an attribute. This is called by + * att_read_callbk(). + * + * @param grp Pointer to group info struct. + * @param attid Attribute ID. + * @param att Pointer that gets att info struct. + * + * @return ::NC_NOERR No error. + * @return ::NC_EHDFERR HDF5 returned error. + * @return ::NC_EATTMETA Att metadata error. + * @return ::NC_ENOMEM Out of memory. + * @author Ed Hartnett + */ +static int +read_hdf5_att(NC_GRP_INFO_T *grp, hid_t attid, NC_ATT_INFO_T *att) +{ + NC_HDF5_ATT_INFO_T *hdf5_att; + hid_t spaceid = 0, file_typeid = 0; + hsize_t dims[1] = {0}; /* netcdf attributes always 1-D. */ + size_t type_size; + int att_ndims; + hssize_t att_npoints; + H5T_class_t att_class; + int fixed_len_string = 0; + size_t fixed_size = 0; + int retval = NC_NOERR; + + assert(att && att->hdr.name && att->format_att_info); + LOG((5, "%s: att->hdr.id %d att->hdr.name %s att->nc_typeid %d att->len %d", + __func__, att->hdr.id, att->hdr.name, (int)att->nc_typeid, att->len)); + + /* Get HDF5-sepecific info stuct for this attribute. */ + hdf5_att = (NC_HDF5_ATT_INFO_T *)att->format_att_info; + + /* Get type of attribute in file. */ + if ((file_typeid = H5Aget_type(attid)) < 0) + return NC_EATTMETA; + if ((hdf5_att->native_hdf_typeid = H5Tget_native_type(file_typeid, + H5T_DIR_DEFAULT)) < 0) + BAIL(NC_EHDFERR); + if ((att_class = H5Tget_class(hdf5_att->native_hdf_typeid)) < 0) + BAIL(NC_EATTMETA); + if (att_class == H5T_STRING && + !H5Tis_variable_str(hdf5_att->native_hdf_typeid)) + { + fixed_len_string++; + if (!(fixed_size = H5Tget_size(hdf5_att->native_hdf_typeid))) + BAIL(NC_EATTMETA); + } + if ((retval = get_netcdf_type(grp->nc4_info, hdf5_att->native_hdf_typeid, + &(att->nc_typeid)))) + BAIL(retval); + + + /* Get len. */ + if ((spaceid = H5Aget_space(attid)) < 0) + BAIL(NC_EATTMETA); + if ((att_ndims = H5Sget_simple_extent_ndims(spaceid)) < 0) + BAIL(NC_EATTMETA); + if ((att_npoints = H5Sget_simple_extent_npoints(spaceid)) < 0) + BAIL(NC_EATTMETA); + + /* If both att_ndims and att_npoints are zero, then this is a + * zero length att. */ + if (att_ndims == 0 && att_npoints == 0) + dims[0] = 0; + else if (att->nc_typeid == NC_STRING) + dims[0] = att_npoints; + else if (att->nc_typeid == NC_CHAR) + { + /* NC_CHAR attributes are written as a scalar in HDF5, of type + * H5T_C_S1, of variable length. */ + if (att_ndims == 0) + { + if (!(dims[0] = H5Tget_size(file_typeid))) + BAIL(NC_EATTMETA); + } + else + { + /* This is really a string type! */ + att->nc_typeid = NC_STRING; + dims[0] = att_npoints; + } + } + else + { + H5S_class_t space_class; + + /* All netcdf attributes are scalar or 1-D only. */ + if (att_ndims > 1) + BAIL(NC_EATTMETA); + + /* Check class of HDF5 dataspace */ + if ((space_class = H5Sget_simple_extent_type(spaceid)) < 0) + BAIL(NC_EATTMETA); + + /* Check for NULL HDF5 dataspace class (should be weeded out + * earlier) */ + if (H5S_NULL == space_class) + BAIL(NC_EATTMETA); + + /* check for SCALAR HDF5 dataspace class */ + if (H5S_SCALAR == space_class) + dims[0] = 1; + else /* Must be "simple" dataspace */ + { + /* Read the size of this attribute. */ + if (H5Sget_simple_extent_dims(spaceid, dims, NULL) < 0) + BAIL(NC_EATTMETA); + } + } + + /* Tell the user what the length if this attribute is. */ + att->len = dims[0]; + + /* Allocate some memory if the len is not zero, and read the + attribute. */ + if (dims[0]) + { + if ((retval = nc4_get_typelen_mem(grp->nc4_info, att->nc_typeid, + &type_size))) + return retval; + if (att_class == H5T_VLEN) + { + if (!(att->vldata = malloc((unsigned int)(att->len * sizeof(hvl_t))))) + BAIL(NC_ENOMEM); + if (H5Aread(attid, hdf5_att->native_hdf_typeid, att->vldata) < 0) + BAIL(NC_EATTMETA); + } + else if (att->nc_typeid == NC_STRING) + { + if (!(att->stdata = calloc(att->len, sizeof(char *)))) + BAIL(NC_ENOMEM); + /* For a fixed length HDF5 string, the read requires + * contiguous memory. Meanwhile, the netCDF API requires that + * nc_free_string be called on string arrays, which would not + * work if one contiguous memory block were used. So here I + * convert the contiguous block of strings into an array of + * malloced strings (each string with its own malloc). Then I + * copy the data and free the contiguous memory. This + * involves copying the data, which is bad, but this only + * occurs for fixed length string attributes, and presumably + * these are small. (And netCDF-4 does not create them - it + * always uses variable length strings. */ + if (fixed_len_string) + { + int i; + char *contig_buf, *cur; + + /* Alloc space for the contiguous memory read. */ + if (!(contig_buf = malloc(att->len * fixed_size * sizeof(char)))) + BAIL(NC_ENOMEM); + + /* Read the fixed-len strings as one big block. */ + if (H5Aread(attid, hdf5_att->native_hdf_typeid, contig_buf) < 0) { + free(contig_buf); + BAIL(NC_EATTMETA); + } + + /* Copy strings, one at a time, into their new home. Alloc + space for each string. The user will later free this + space with nc_free_string. */ + cur = contig_buf; + for (i = 0; i < att->len; i++) + { + if (!(att->stdata[i] = malloc(fixed_size))) { + free(contig_buf); + BAIL(NC_ENOMEM); + } + strncpy(att->stdata[i], cur, fixed_size); + cur += fixed_size; + } + + /* Free contiguous memory buffer. */ + free(contig_buf); + } + else + { + /* Read variable-length string atts. */ + if (H5Aread(attid, hdf5_att->native_hdf_typeid, att->stdata) < 0) + BAIL(NC_EATTMETA); + } + } + else + { + if (!(att->data = malloc((unsigned int)(att->len * type_size)))) + BAIL(NC_ENOMEM); + if (H5Aread(attid, hdf5_att->native_hdf_typeid, att->data) < 0) + BAIL(NC_EATTMETA); + } + } + + if (H5Tclose(file_typeid) < 0) + BAIL(NC_EHDFERR); + if (H5Sclose(spaceid) < 0) + return NC_EHDFERR; + + return NC_NOERR; + +exit: + if (H5Tclose(file_typeid) < 0) + BAIL2(NC_EHDFERR); + if (spaceid > 0 && H5Sclose(spaceid) < 0) + BAIL2(NC_EHDFERR); + return retval; +} + +/** + * @internal Wrap HDF5 allocated memory free operations + * + * @param memory Pointer to memory to be freed. + * + * @return ::NC_NOERR No error. + * @author Dennis Heimbigner + */ +static void +hdf5free(void* memory) +{ +#ifndef JNA + /* On Windows using the microsoft runtime, it is an error + for one library to free memory allocated by a different library.*/ +#ifdef HDF5_HAS_H5FREE + if(memory != NULL) H5free_memory(memory); +#else +#ifndef _MSC_VER + if(memory != NULL) free(memory); +#endif +#endif +#endif +} + +/** + * @internal Read information about a user defined type from the HDF5 + * file, and stash it in the group's list of types. + * + * @param grp Pointer to group info struct. + * @param hdf_typeid HDF5 type ID. + * @param type_name Pointer that gets the type name. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_EHDFERR HDF5 returned error. + * @return ::NC_EBADTYPID Type not found. + * @return ::NC_ENOMEM Out of memory. + * @author Ed Hartnett + */ +static int +read_type(NC_GRP_INFO_T *grp, hid_t hdf_typeid, char *type_name) +{ + NC_TYPE_INFO_T *type; + NC_HDF5_TYPE_INFO_T *hdf5_type; + H5T_class_t class; + hid_t native_typeid; + size_t type_size; + int nmembers; + int retval; + + assert(grp && type_name); + + LOG((4, "%s: type_name %s grp->hdr.name %s", __func__, type_name, + grp->hdr.name)); + + /* What is the native type for this platform? */ + if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0) + return NC_EHDFERR; + + /* What is the size of this type on this platform. */ + if (!(type_size = H5Tget_size(native_typeid))) + return NC_EHDFERR; + LOG((5, "type_size %d", type_size)); + + /* Add to the list for this new type, and get a local pointer to it. */ + if ((retval = nc4_type_list_add(grp, type_size, type_name, &type))) + return retval; + + /* Allocate storage for HDF5-specific type info. */ + if (!(hdf5_type = calloc(1, sizeof(NC_HDF5_TYPE_INFO_T)))) + return NC_ENOMEM; + type->format_type_info = hdf5_type; + + /* Remember HDF5-specific type info. */ + hdf5_type->hdf_typeid = hdf_typeid; + hdf5_type->native_hdf_typeid = native_typeid; + + /* Remember we have committed this type. */ + type->committed = NC_TRUE; + + /* Increment number of objects using ID. */ + if (H5Iinc_ref(hdf5_type->hdf_typeid) < 0) + return NC_EHDFERR; + + /* What is the class of this type, compound, vlen, etc. */ + if ((class = H5Tget_class(hdf_typeid)) < 0) + return NC_EHDFERR; + switch (class) + { + case H5T_STRING: + type->nc_type_class = NC_STRING; + break; + + case H5T_COMPOUND: + { + int nmembers; + unsigned int m; + char* member_name = NULL; +#ifdef JNA + char jna[1001]; +#endif + + type->nc_type_class = NC_COMPOUND; + + if ((nmembers = H5Tget_nmembers(hdf_typeid)) < 0) + return NC_EHDFERR; + LOG((5, "compound type has %d members", nmembers)); + type->u.c.field = nclistnew(); + nclistsetalloc(type->u.c.field,nmembers); + + for (m = 0; m < nmembers; m++) + { + hid_t member_hdf_typeid; + hid_t member_native_typeid; + size_t member_offset; + H5T_class_t mem_class; + nc_type member_xtype; + + /* Get the typeid and native typeid of this member of the + * compound type. */ + if ((member_hdf_typeid = H5Tget_member_type(native_typeid, m)) < 0) + return NC_EHDFERR; + + if ((member_native_typeid = H5Tget_native_type(member_hdf_typeid, + H5T_DIR_DEFAULT)) < 0) + return NC_EHDFERR; + + /* Get the name of the member.*/ + member_name = H5Tget_member_name(native_typeid, m); + if (!member_name || strlen(member_name) > NC_MAX_NAME) { + retval = NC_EBADNAME; + break; + } +#ifdef JNA + else { + strncpy(jna,member_name,1000); + member_name = jna; + } +#endif + + /* Offset in bytes on *this* platform. */ + member_offset = H5Tget_member_offset(native_typeid, m); + + /* Get dimensional data if this member is an array of something. */ + if ((mem_class = H5Tget_class(member_hdf_typeid)) < 0) + return NC_EHDFERR; + if (mem_class == H5T_ARRAY) + { + int ndims, dim_size[NC_MAX_VAR_DIMS]; + hsize_t dims[NC_MAX_VAR_DIMS]; + int d; + + if ((ndims = H5Tget_array_ndims(member_hdf_typeid)) < 0) + return NC_EHDFERR; + + if (H5Tget_array_dims(member_hdf_typeid, dims, NULL) != ndims) + return NC_EHDFERR; + + for (d = 0; d < ndims; d++) + dim_size[d] = dims[d]; + + /* What is the netCDF typeid of this member? */ + if ((retval = get_netcdf_type(grp->nc4_info, H5Tget_super(member_hdf_typeid), + &member_xtype))) + return retval; + + /* Add this member to our list of fields in this compound type. */ + if ((retval = nc4_field_list_add(type, member_name, member_offset, + member_xtype, ndims, dim_size))) + return retval; + } + else + { + /* What is the netCDF typeid of this member? */ + if ((retval = get_netcdf_type(grp->nc4_info, member_native_typeid, + &member_xtype))) + return retval; + + /* Add this member to our list of fields in this compound type. */ + if ((retval = nc4_field_list_add(type, member_name, member_offset, + member_xtype, 0, NULL))) + return retval; + } + + hdf5free(member_name); + } + } + break; + + case H5T_VLEN: + { + htri_t ret; + + /* For conveninence we allow user to pass vlens of strings + * with null terminated strings. This means strings are + * treated slightly differently by the API, although they are + * really just VLENs of characters. */ + if ((ret = H5Tis_variable_str(hdf_typeid)) < 0) + return NC_EHDFERR; + if (ret) + type->nc_type_class = NC_STRING; + else + { + hid_t base_hdf_typeid; + nc_type base_nc_type = NC_NAT; + + type->nc_type_class = NC_VLEN; + + /* Find the base type of this vlen (i.e. what is this a + * vlen of?) */ + if (!(base_hdf_typeid = H5Tget_super(native_typeid))) + return NC_EHDFERR; + + /* What size is this type? */ + if (!(type_size = H5Tget_size(base_hdf_typeid))) + return NC_EHDFERR; + + /* What is the netcdf corresponding type. */ + if ((retval = get_netcdf_type(grp->nc4_info, base_hdf_typeid, + &base_nc_type))) + return retval; + LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d", + base_hdf_typeid, type_size, base_nc_type)); + + /* Remember the base type for this vlen. */ + type->u.v.base_nc_typeid = base_nc_type; + } + } + break; + + case H5T_OPAQUE: + type->nc_type_class = NC_OPAQUE; + break; + + case H5T_ENUM: + { + hid_t base_hdf_typeid; + nc_type base_nc_type = NC_NAT; + void *value; + int i; + char *member_name = NULL; +#ifdef JNA + char jna[1001]; +#endif + + type->nc_type_class = NC_ENUM; + + /* Find the base type of this enum (i.e. what is this a + * enum of?) */ + if (!(base_hdf_typeid = H5Tget_super(hdf_typeid))) + return NC_EHDFERR; + /* What size is this type? */ + if (!(type_size = H5Tget_size(base_hdf_typeid))) + return NC_EHDFERR; + /* What is the netcdf corresponding type. */ + if ((retval = get_netcdf_type(grp->nc4_info, base_hdf_typeid, + &base_nc_type))) + return retval; + LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d", + base_hdf_typeid, type_size, base_nc_type)); + + /* Remember the base type for this enum. */ + type->u.e.base_nc_typeid = base_nc_type; + + /* Find out how many member are in the enum. */ + if ((nmembers = H5Tget_nmembers(hdf_typeid)) < 0) + return NC_EHDFERR; + type->u.e.enum_member = nclistnew(); + nclistsetalloc(type->u.e.enum_member,nmembers); + + /* Allocate space for one value. */ + if (!(value = calloc(1, type_size))) + return NC_ENOMEM; + + /* Read each name and value defined in the enum. */ + for (i = 0; i < nmembers; i++) + { + /* Get the name and value from HDF5. */ + if (!(member_name = H5Tget_member_name(hdf_typeid, i))) + return NC_EHDFERR; + +#ifdef JNA + strncpy(jna,member_name,1000); + member_name = jna; +#endif + + if (strlen(member_name) > NC_MAX_NAME) + return NC_EBADNAME; + + if (H5Tget_member_value(hdf_typeid, i, value) < 0) + return NC_EHDFERR; + + /* Insert new field into this type's list of fields. */ + if ((retval = nc4_enum_member_add(type, type->size, + member_name, value))) + return retval; + + hdf5free(member_name); + } + free(value); + } + break; + + default: + LOG((0, "unknown class")); + return NC_EBADCLASS; + } + return retval; +} + +/** + * @internal Callback function for reading attributes. This is used + * for both global and variable attributes. + * + * @param loc_id HDF5 attribute ID. + * @param att_name Name of the attrigute. + * @param ainfo HDF5 info struct for attribute. + * @param att_data Pointer to an att_iter_info struct, which contains + * pointers to the NC_GRP_INFO_T and (for variable attributes) the + * NC_VAR_INFO_T. For global atts the var pointer is NULL. + * + * @return ::NC_NOERR No error. Iteration continues. + * @return ::-1 Error. Stop iteration. + * @author Ed Hartnett + */ +static herr_t +att_read_callbk(hid_t loc_id, const char *att_name, const H5A_info_t *ainfo, + void *att_data) +{ + + hid_t attid = 0; + NC_ATT_INFO_T *att; + NCindex *list; + att_iter_info *att_info = (att_iter_info *)att_data; + int retval = NC_NOERR; + + /* Determin what list is being added to. */ + list = att_info->var ? att_info->var->att : att_info->grp->att; + + /* This may be an attribute telling us that strict netcdf-3 rules + * are in effect. If so, we will make note of the fact, but not add + * this attribute to the metadata. It's not a user attribute, but + * an internal netcdf-4 one. */ + if (!strcmp(att_name, NC3_STRICT_ATT_NAME)) + { + /* Only relevant for groups, not vars. */ + if (!att_info->var) + att_info->grp->nc4_info->cmode |= NC_CLASSIC_MODEL; + return NC_NOERR; + } + + /* Should we ignore this attribute? */ + if (NC_findreserved(att_name)) + return NC_NOERR; + + /* Add to the end of the list of atts for this var. */ + if ((retval = nc4_att_list_add(list, att_name, &att))) + BAIL(-1); + + /* Allocate storage for the HDF5 specific att info. */ + if (!(att->format_att_info = calloc(1, sizeof(NC_HDF5_ATT_INFO_T)))) + BAIL(-1); + + /* Open the att by name. */ + if ((attid = H5Aopen(loc_id, att_name, H5P_DEFAULT)) < 0) + BAIL(-1); + LOG((4, "%s:: att_name %s", __func__, att_name)); + + /* Read the rest of the info about the att, + * including its values. */ + if ((retval = read_hdf5_att(att_info->grp, attid, att))) + BAIL(retval); + + if (att) + att->created = NC_TRUE; + +exit: + if (retval == NC_EBADTYPID) + { + /* NC_EBADTYPID will be normally converted to NC_NOERR so that + the parent iterator does not fail. */ + retval = nc4_att_list_del(list, att); + att = NULL; + } + if (attid > 0 && H5Aclose(attid) < 0) + retval = -1; + + /* Since this is a HDF5 iterator callback, return -1 for any error + * to stop iteration. */ + if (retval) + retval = -1; + return retval; +} + +/** + * @internal This function reads all the attributes of a variable or + * the global attributes of a group. + * + * @param grp Pointer to the group info. + * @param var Pointer to the var info. NULL for global att reads. + * + * @return ::NC_NOERR No error. + * @return ::NC_EATTMETA Some error occured reading attributes. + * @author Ed Hartnett + */ +int +nc4_read_atts(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var) +{ + att_iter_info att_info; /* Custom iteration information */ + hid_t locid; /* HDF5 location to read atts from. */ + + /* Check inputs. */ + assert(grp); + + /* Assign var and grp in struct. (var may be NULL). */ + att_info.var = var; + att_info.grp = grp; + + /* Determine where to read from in the HDF5 file. */ + locid = var ? ((NC_HDF5_VAR_INFO_T *)(var->format_var_info))->hdf_datasetid : + ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid; + + /* Now read all the attributes at this location, ignoring special + * netCDF hidden attributes. */ + if ((H5Aiterate2(locid, H5_INDEX_CRT_ORDER, H5_ITER_INC, NULL, + att_read_callbk, &att_info)) < 0) + return NC_EATTMETA; + + /* Remember that we have read the atts for this var or group. */ + if (var) + var->atts_read = 1; + else + grp->atts_read = 1; + + return NC_NOERR; +} + +/** + * @internal This function is called by read_dataset() when a + * dimension scale dataset is encountered. It reads in the dimension + * data (creating a new NC_DIM_INFO_T object), and also checks to see + * if this is a dimension without a variable - that is, a coordinate + * dimension which does not have any coordinate data. + * + * @param grp Pointer to group info struct. + * @param datasetid The HDF5 dataset ID. + * @param obj_name The HDF5 object name. + * @param statbuf HDF5 status buffer. + * @param scale_size Size of dimension scale. + * @param max_scale_size Maximum size of dim scale. + * @param dim Pointer to pointer that gets new dim info struct. + * + * @returns ::NC_NOERR No error. + * @return ::NC_EHDFERR HDF5 returned error. + * @author Ed Hartnett + */ +static int +read_scale(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name, + const H5G_stat_t *statbuf, hsize_t scale_size, + hsize_t max_scale_size, NC_DIM_INFO_T **dim) +{ + NC_DIM_INFO_T *new_dim; /* Dimension added to group */ + NC_HDF5_DIM_INFO_T *new_hdf5_dim; /* HDF5-specific dim info. */ + char dimscale_name_att[NC_MAX_NAME + 1] = ""; /* Dimscale name, for checking if dim without var */ + htri_t attr_exists = -1; /* Flag indicating hidden attribute exists */ + hid_t attid = -1; /* ID of hidden attribute (to store dim ID) */ + int dimscale_created = 0; /* Remember if a dimension was created (for error recovery) */ + short initial_next_dimid = grp->nc4_info->next_dimid;/* Retain for error recovery */ + size_t len = 0; + int too_long = NC_FALSE; + int assigned_id = -1; + int retval = NC_NOERR; + + assert(grp && dim); + + /* Does this dataset have a hidden attribute that tells us its + * dimid? If so, read it. */ + if ((attr_exists = H5Aexists(datasetid, NC_DIMID_ATT_NAME)) < 0) + BAIL(NC_EHDFERR); + if (attr_exists) + { + if ((attid = H5Aopen_name(datasetid, NC_DIMID_ATT_NAME)) < 0) + BAIL(NC_EHDFERR); + + if (H5Aread(attid, H5T_NATIVE_INT, &assigned_id) < 0) + BAIL(NC_EHDFERR); + + /* Check if scale's dimid should impact the group's next dimid */ + if (assigned_id >= grp->nc4_info->next_dimid) + grp->nc4_info->next_dimid = assigned_id + 1; + } + + /* Get dim size. On machines with a size_t of less than 8 bytes, it + * is possible for a dimension to be too long. */ + if (SIZEOF_SIZE_T < 8 && scale_size > NC_MAX_UINT) + { + len = NC_MAX_UINT; + too_long = NC_TRUE; + } + else + len = scale_size; + + /* Create the dimension for this scale. */ + if ((retval = nc4_dim_list_add(grp, obj_name, len, assigned_id, &new_dim))) + BAIL(retval); + new_dim->too_long = too_long; + + /* Create struct for HDF5-specific dim info. */ + if (!(new_dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T)))) + BAIL(NC_ENOMEM); + new_hdf5_dim = (NC_HDF5_DIM_INFO_T *)new_dim->format_dim_info; + + dimscale_created++; + + /* Remember these 4 values to uniquely identify this dataset in the + * HDF5 file. */ + new_hdf5_dim->hdf5_objid.fileno[0] = statbuf->fileno[0]; + new_hdf5_dim->hdf5_objid.fileno[1] = statbuf->fileno[1]; + new_hdf5_dim->hdf5_objid.objno[0] = statbuf->objno[0]; + new_hdf5_dim->hdf5_objid.objno[1] = statbuf->objno[1]; + + /* If the dimscale has an unlimited dimension, then this dimension + * is unlimited. */ + if (max_scale_size == H5S_UNLIMITED) + new_dim->unlimited = NC_TRUE; + + /* If the scale name is set to DIM_WITHOUT_VARIABLE, then this is a + * dimension, but not a variable. (If get_scale_name returns an + * error, just move on, there's no NAME.) */ + if (H5DSget_scale_name(datasetid, dimscale_name_att, NC_MAX_NAME) >= 0) + { + if (!strncmp(dimscale_name_att, DIM_WITHOUT_VARIABLE, + strlen(DIM_WITHOUT_VARIABLE))) + { + if (new_dim->unlimited) + { + size_t len = 0, *lenp = &len; + + /* Find actual length by checking all datasets that use + * this dim. */ + if ((retval = nc4_find_dim_len(grp, new_dim->hdr.id, &lenp))) + BAIL(retval); + new_dim->len = *lenp; + } + + /* Hold open the dataset, since the dimension doesn't have a + * coordinate variable */ + new_hdf5_dim->hdf_dimscaleid = datasetid; + H5Iinc_ref(new_hdf5_dim->hdf_dimscaleid); /* Increment number of objects using ID */ + } + } + + /* Set the dimension created. */ + *dim = new_dim; + +exit: + /* Close the hidden attribute, if it was opened. */ + if (attid > 0 && H5Aclose(attid) < 0) + BAIL2(NC_EHDFERR); + + /* On error, undo any dimscale creation */ + if (retval && dimscale_created) + { + /* free the dimension */ + if ((retval = nc4_dim_list_del(grp, new_dim))) + BAIL2(retval); + + /* Reset the group's information */ + grp->nc4_info->next_dimid = initial_next_dimid; + } + + return retval; +} + +/** + * @internal Read a HDF5 dataset. This function is called when + * read_hdf5_obj() encounters an HDF5 dataset when opening a file. + * + * @param grp Pointer to group info struct. + * @param datasetid HDF5 dataset ID. + * @param obj_name Object name. + * @param statbuf HDF5 status buffer. + * + * @return ::NC_NOERR No error. + * @return ::NC_EBADID Bad ncid. + * @return ::NC_EHDFERR HDF5 returned error. + * @author Ed Hartnett + */ +static int +read_dataset(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name, + const H5G_stat_t *statbuf) +{ + NC_DIM_INFO_T *dim = NULL; /* Dimension created for scales */ + NC_HDF5_DIM_INFO_T *hdf5_dim; + hid_t spaceid = 0; + int ndims; + htri_t is_scale; + int retval = NC_NOERR; + + /* Get the dimension information for this dataset. */ + if ((spaceid = H5Dget_space(datasetid)) < 0) + BAIL(NC_EHDFERR); + if ((ndims = H5Sget_simple_extent_ndims(spaceid)) < 0) + BAIL(NC_EHDFERR); + + /* Is this a dimscale? */ + if ((is_scale = H5DSis_scale(datasetid)) < 0) + BAIL(NC_EHDFERR); + if (is_scale) + { + hsize_t dims[H5S_MAX_RANK]; + hsize_t max_dims[H5S_MAX_RANK]; + + /* Query the scale's size & max. size */ + if (H5Sget_simple_extent_dims(spaceid, dims, max_dims) < 0) + BAIL(NC_EHDFERR); + + /* Read the scale information. */ + if ((retval = read_scale(grp, datasetid, obj_name, statbuf, dims[0], + max_dims[0], &dim))) + BAIL(retval); + hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; + } + + /* Add a var to the linked list, and get its metadata, + * unless this is one of those funny dimscales that are a + * dimension in netCDF but not a variable. (Spooky!) */ + if (!dim || (dim && !hdf5_dim->hdf_dimscaleid)) + if ((retval = read_var(grp, datasetid, obj_name, ndims, dim))) + BAIL(retval); + +exit: + if (spaceid && H5Sclose(spaceid) <0) + BAIL2(retval); + + return retval; +} + +/** + * @internal Add HDF5 object info for a group to a list for later + * processing. We do this when we encounter groups, so that the parent + * group can be fully processed before the child groups. + * + * @param udata Pointer to the user data, in this case a + * user_data_t. + * @param oinfo The HDF5 object info. + * + * @return ::NC_NOERR No error. + * @return ::NC_ENOMEM Out of memory. + * @author Ed Hartnett, Dennis Heimbigner + */ +static int +oinfo_list_add(user_data_t *udata, const hdf5_obj_info_t *oinfo) +{ + hdf5_obj_info_t *new_oinfo; /* Pointer to info for object */ + + /* Allocate memory for the object's info. */ + if (!(new_oinfo = calloc(1, sizeof(hdf5_obj_info_t)))) + return NC_ENOMEM; + + /* Make a copy of the object's info. */ + memcpy(new_oinfo, oinfo, sizeof(hdf5_obj_info_t)); + + /* Add it to the list for future processing. */ + nclistpush(udata->grps, new_oinfo); + + return NC_NOERR; +} + +/** + * @internal Callback function called by H5Literate() for every HDF5 + * object in the file. + * + * @note This function is called by HDF5 so does not return a netCDF + * error code. + * + * @param grpid HDF5 group ID. + * @param name Name of object. + * @param info Info struct for object. + * @param _op_data Pointer to user data, a user_data_t. It will + * contain a pointer to the current group and a list of + * hdf5_obj_info_t. Any child groups will get their hdf5_obj_info + * added to this list. + * + * @return H5_ITER_CONT No error, continue iteration. + * @return H5_ITER_ERROR HDF5 error, stop iteration. + * @author Ed Hartnett + */ +static int +read_hdf5_obj(hid_t grpid, const char *name, const H5L_info_t *info, + void *_op_data) +{ + /* Pointer to user data for callback */ + user_data_t *udata = (user_data_t *)_op_data; + hdf5_obj_info_t oinfo; /* Pointer to info for object */ + int retval = H5_ITER_CONT; + + /* Open this critter. */ + if ((oinfo.oid = H5Oopen(grpid, name, H5P_DEFAULT)) < 0) + BAIL(H5_ITER_ERROR); + + /* Get info about the object.*/ + if (H5Gget_objinfo(oinfo.oid, ".", 1, &oinfo.statbuf) < 0) + BAIL(H5_ITER_ERROR); + + strncpy(oinfo.oname, name, NC_MAX_NAME); + + /* Add object to list, for later */ + switch(oinfo.statbuf.type) + { + case H5G_GROUP: + LOG((3, "found group %s", oinfo.oname)); + + /* Defer descending into child group immediately, so that the + * types in the current group can be processed and be ready for + * use by vars in the child group(s). */ + if (oinfo_list_add(udata, &oinfo)) + BAIL(H5_ITER_ERROR); + break; + + case H5G_DATASET: + LOG((3, "found dataset %s", oinfo.oname)); + + /* Learn all about this dataset, which may be a dimscale + * (i.e. dimension metadata), or real data. */ + if ((retval = read_dataset(udata->grp, oinfo.oid, oinfo.oname, + &oinfo.statbuf))) + { + /* Allow NC_EBADTYPID to transparently skip over datasets + * which have a datatype that netCDF-4 doesn't undertand + * (currently), but break out of iteration for other + * errors. */ + if (retval != NC_EBADTYPID) + BAIL(H5_ITER_ERROR); + else + retval = H5_ITER_CONT; + } + + /* Close the object */ + if (H5Oclose(oinfo.oid) < 0) + BAIL(H5_ITER_ERROR); + break; + + case H5G_TYPE: + LOG((3, "found datatype %s", oinfo.oname)); + + /* Process the named datatype */ + if (read_type(udata->grp, oinfo.oid, oinfo.oname)) + BAIL(H5_ITER_ERROR); + + /* Close the object */ + if (H5Oclose(oinfo.oid) < 0) + BAIL(H5_ITER_ERROR); + break; + + default: + LOG((0, "Unknown object class %d in %s!", oinfo.statbuf.type, __func__)); + BAIL(H5_ITER_ERROR); + } + +exit: + if (retval) + { + if (oinfo.oid > 0 && H5Oclose(oinfo.oid) < 0) + BAIL2(H5_ITER_ERROR); + } + + return (retval); +} + +/** + * @internal This is the main function to recursively read all the + * metadata for the file. The links in the 'grp' are iterated over and + * added to the file's metadata information. Note that child groups + * are not immediately processed, but are deferred until all the other + * links in the group are handled (so that vars in the child groups + * are guaranteed to have types that they use in a parent group in + * place). + * + * @param grp Pointer to a group. + * + * @return ::NC_NOERR No error. + * @return ::NC_EHDFERR HDF5 error. + * @return ::NC_ENOMEM Out of memory. + * @return ::NC_ECANTWRITE File must be opened read-only. + * @author Ed Hartnett, Dennis Heimbigner + */ +static int +rec_read_metadata(NC_GRP_INFO_T *grp) +{ + NC_HDF5_GRP_INFO_T *hdf5_grp; + user_data_t udata; /* User data for iteration */ + hdf5_obj_info_t *oinfo; /* Pointer to info for object */ + hsize_t idx = 0; + hid_t pid = -1; + unsigned crt_order_flags = 0; + H5_index_t iter_index; + int i, retval = NC_NOERR; + + assert(grp && grp->hdr.name && grp->format_grp_info); + LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name)); + + /* Get HDF5-specific group info. */ + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + + /* Open this HDF5 group and retain its grpid. It will remain open + * with HDF5 until this file is nc_closed. */ + if (!hdf5_grp->hdf_grpid) + { + if (grp->parent) + { + /* This is a child group. */ + NC_HDF5_GRP_INFO_T *parent_hdf5_grp; + parent_hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->parent->format_grp_info; + + if ((hdf5_grp->hdf_grpid = H5Gopen2(parent_hdf5_grp->hdf_grpid, + grp->hdr.name, H5P_DEFAULT)) < 0) + BAIL(NC_EHDFERR); + } + else + { + /* This is the root group. */ + NC_HDF5_FILE_INFO_T *hdf5_info; + hdf5_info = (NC_HDF5_FILE_INFO_T *)grp->nc4_info->format_file_info; + + if ((hdf5_grp->hdf_grpid = H5Gopen2(hdf5_info->hdfid, "/", + H5P_DEFAULT)) < 0) + BAIL(NC_EHDFERR); + } + } + assert(hdf5_grp->hdf_grpid > 0); + + /* Get the group creation flags, to check for creation ordering. */ + if ((pid = H5Gget_create_plist(hdf5_grp->hdf_grpid)) < 0) + BAIL(NC_EHDFERR); + if (H5Pget_link_creation_order(pid, &crt_order_flags) < 0) + BAIL(NC_EHDFERR); + + /* Set the iteration index to use. */ + if (crt_order_flags & H5P_CRT_ORDER_TRACKED) + iter_index = H5_INDEX_CRT_ORDER; + else + { + NC_FILE_INFO_T *h5 = grp->nc4_info; + + /* Without creation ordering, file must be read-only. */ + if (!h5->no_write) + BAIL(NC_ECANTWRITE); + + iter_index = H5_INDEX_NAME; + } + + /* Set user data for iteration over any child groups. */ + udata.grp = grp; + udata.grps = nclistnew(); + + /* Iterate over links in this group, building lists for the types, + * datasets and groups encountered. A pointer to udata will be + * passed as a parameter to the callback function + * read_hdf5_obj(). (I have also tried H5Oiterate(), but it is much + * slower iterating over the same file - Ed.) */ + if (H5Literate(hdf5_grp->hdf_grpid, iter_index, H5_ITER_INC, &idx, + read_hdf5_obj, (void *)&udata) < 0) + BAIL(NC_EHDFERR); + + /* Process the child groups found. (Deferred until now, so that the + * types in the current group get processed and are available for + * vars in the child group(s).) */ + for (i = 0; i < nclistlength(udata.grps); i++) + { + NC_GRP_INFO_T *child_grp; + oinfo = (hdf5_obj_info_t*)nclistget(udata.grps, i); + + /* Add group to file's hierarchy. */ + if ((retval = nc4_grp_list_add(grp->nc4_info, grp, oinfo->oname, + &child_grp))) + BAIL(retval); + + /* Allocate storage for HDF5-specific group info. */ + if (!(child_grp->format_grp_info = calloc(1, sizeof(NC_HDF5_GRP_INFO_T)))) + return NC_ENOMEM; + + /* Recursively read the child group's metadata. */ + if ((retval = rec_read_metadata(child_grp))) + BAIL(retval); + } + + /* When reading existing file, mark all variables as written. */ + for (i = 0; i < ncindexsize(grp->vars); i++) + ((NC_VAR_INFO_T *)ncindexith(grp->vars, i))->written_to = NC_TRUE; + +exit: + if (pid > 0 && H5Pclose(pid) < 0) + BAIL2(NC_EHDFERR); + + /* Clean up list of child groups. */ + for (i = 0; i < nclistlength(udata.grps); i++) + { + oinfo = (hdf5_obj_info_t *)nclistget(udata.grps, i); + /* Close the open HDF5 object. */ + if (H5Oclose(oinfo->oid) < 0) + BAIL2(NC_EHDFERR); + free(oinfo); + } + nclistfree(udata.grps); + + return retval; +} diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5type.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5type.c new file mode 100644 index 0000000000000000000000000000000000000000..37a3dfd7a077471bfb9e50b38a84f427c6b94781 --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5type.c @@ -0,0 +1,549 @@ +/* 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 "config.h" +#include "hdf5internal.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)) + { + *equalp = 0; + return NC_NOERR; + } + + /* If both are atomic types, the answer is easy. */ + if (typeid1 <= NUM_ATOMIC_TYPES) + { + 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 = nclistget(grpone->nc4_info->alltypes, typeid1))) + return NC_EBADTYPE; + if ((retval = nc4_find_nc4_grp(ncid2, &grptwo))) + return retval; + if (!(type2 = nclistget(grptwo->nc4_info->alltypes, typeid2))) + return NC_EBADTYPE; + + /* Are the two types equal? */ + { + hid_t hid1, hid2; + + /* Get the HDF5 types from the HDF5-specific type info. */ + assert(type1->format_type_info && type2->format_type_info); + hid1 = ((NC_HDF5_TYPE_INFO_T *)type1->format_type_info)->native_hdf_typeid; + hid2 = ((NC_HDF5_TYPE_INFO_T *)type2->format_type_info)->native_hdf_typeid; + + /* Ask HDF5 if the types are equal. */ + if ((retval = H5Tequal(hid1, hid2)) < 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_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. + * @return ::NC_ESTRICTNC3 Cannot define user types in classic model. + * @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_FILE_INFO_T *h5; + NC_GRP_INFO_T *grp; + NC_TYPE_INFO_T *type; + NC_HDF5_TYPE_INFO_T *hdf5_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); + + /* User types cannot be defined with classic model flag. */ + if (h5->cmode & NC_CLASSIC_MODEL) + return NC_ESTRICTNC3; + + /* 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; + + /* Allocate storage for HDF5-specific type info. */ + if (!(hdf5_type = calloc(1, sizeof(NC_HDF5_TYPE_INFO_T)))) + return NC_ENOMEM; + type->format_type_info = hdf5_type; + + /* 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_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. + * @return ::NC_ESTRICTNC3 Cannot define user types in classic model. + * @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, 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_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. + * @return ::NC_ESTRICTNC3 Cannot define user types in classic model. + * @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_ENOTNC4 User types in netCDF-4 files only. + * @return ::NC_EMAXNAME Name is too long. + * @return ::NC_EBADNAME Name breaks netCDF name rules. + * @return ::NC_ESTRICTNC3 Cannot define user types in classic model. + * @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_EBADID Bad ncid. + * @return ::NC_ENOTNC4 User types in netCDF-4 files only. + * @return ::NC_EMAXNAME Name is too long. + * @return ::NC_EBADNAME Name breaks netCDF name rules. + * @return ::NC_ESTRICTNC3 Cannot define user types in classic model. + * @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; +} diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5var.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5var.c new file mode 100644 index 0000000000000000000000000000000000000000..3888526422c23bd928861b6aa86357ea135f8929 --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5var.c @@ -0,0 +1,2265 @@ +/* Copyright 2003-2019, University Corporation for Atmospheric + * Research. See COPYRIGHT file for copying and redistribution + * conditions.*/ +/** + * @file + * @internal This file handles the HDF5 variable functions. + * + * @author Ed Hartnett + */ + +#include "config.h" +#include <hdf5internal.h> +#include <math.h> /* For pow() used below. */ + +/** @internal Default size for unlimited dim chunksize. */ +#define DEFAULT_1D_UNLIM_SIZE (4096) + +/** @internal Temp name used when renaming vars to preserve varid + * order. */ +#define NC_TEMP_NAME "_netcdf4_temporary_variable_name_for_rename" + +/** + * @internal If the HDF5 dataset for this variable is open, then close + * it and reopen it, with the perhaps new settings for chunk caching. + * + * @param grp Pointer to the group info. + * @param var Pointer to the var info. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EHDFERR HDF5 error. + * @author Ed Hartnett + */ +int +nc4_reopen_dataset(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var) +{ + NC_HDF5_VAR_INFO_T *hdf5_var; + hid_t access_pid; + hid_t grpid; + + assert(var && var->format_var_info && grp && grp->format_grp_info); + + /* Get the HDF5-specific var info. */ + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + if (hdf5_var->hdf_datasetid) + { + /* Get the HDF5 group id. */ + grpid = ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid; + + + if ((access_pid = H5Pcreate(H5P_DATASET_ACCESS)) < 0) + return NC_EHDFERR; + if (H5Pset_chunk_cache(access_pid, var->chunk_cache_nelems, + var->chunk_cache_size, + var->chunk_cache_preemption) < 0) + return NC_EHDFERR; + if (H5Dclose(hdf5_var->hdf_datasetid) < 0) + return NC_EHDFERR; + if ((hdf5_var->hdf_datasetid = H5Dopen2(grpid, var->hdr.name, access_pid)) < 0) + return NC_EHDFERR; + if (H5Pclose(access_pid) < 0) + return NC_EHDFERR; + } + + return NC_NOERR; +} + +/** + * @internal Check a set of chunksizes to see if they specify a chunk + * that is too big. + * + * @param grp Pointer to the group info. + * @param var Pointer to the var info. + * @param chunksizes Array of chunksizes to check. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @returns ::NC_EBADCHUNK Bad chunksize. + */ +static int +check_chunksizes(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, const size_t *chunksizes) +{ + double dprod; + size_t type_len; + int d; + int retval; + + if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->hdr.id, &type_len))) + return retval; + if (var->type_info->nc_type_class == NC_VLEN) + dprod = (double)sizeof(hvl_t); + else + dprod = (double)type_len; + for (d = 0; d < var->ndims; d++) + dprod *= (double)chunksizes[d]; + + if (dprod > (double) NC_MAX_UINT) + return NC_EBADCHUNK; + + return NC_NOERR; +} + +/** + * @internal Determine some default chunksizes for a variable. + * + * @param grp Pointer to the group info. + * @param var Pointer to the var info. + * + * @returns ::NC_NOERR for success + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @author Ed Hartnett, Dennis Heimbigner + */ +static int +nc4_find_default_chunksizes2(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var) +{ + int d; + size_t type_size; + float num_values = 1, num_unlim = 0; + int retval; + size_t suggested_size; +#ifdef LOGGING + double total_chunk_size; +#endif + + if (var->type_info->nc_type_class == NC_STRING) + type_size = sizeof(char *); + else + type_size = var->type_info->size; + +#ifdef LOGGING + /* Later this will become the total number of bytes in the default + * chunk. */ + total_chunk_size = (double) type_size; +#endif + + /* How many values in the variable (or one record, if there are + * unlimited dimensions). */ + for (d = 0; d < var->ndims; d++) + { + assert(var->dim[d]); + if (! var->dim[d]->unlimited) + num_values *= (float)var->dim[d]->len; + else { + num_unlim++; + var->chunksizes[d] = 1; /* overwritten below, if all dims are unlimited */ + } + } + /* Special case to avoid 1D vars with unlim dim taking huge amount + of space (DEFAULT_CHUNK_SIZE bytes). Instead we limit to about + 4KB */ + if (var->ndims == 1 && num_unlim == 1) { + if (DEFAULT_CHUNK_SIZE / type_size <= 0) + suggested_size = 1; + else if (DEFAULT_CHUNK_SIZE / type_size > DEFAULT_1D_UNLIM_SIZE) + suggested_size = DEFAULT_1D_UNLIM_SIZE; + else + suggested_size = DEFAULT_CHUNK_SIZE / type_size; + var->chunksizes[0] = suggested_size / type_size; + LOG((4, "%s: name %s dim %d DEFAULT_CHUNK_SIZE %d num_values %f type_size %d " + "chunksize %ld", __func__, var->hdr.name, d, DEFAULT_CHUNK_SIZE, num_values, type_size, var->chunksizes[0])); + } + if (var->ndims > 1 && var->ndims == num_unlim) { /* all dims unlimited */ + suggested_size = pow((double)DEFAULT_CHUNK_SIZE/type_size, 1.0/(double)(var->ndims)); + for (d = 0; d < var->ndims; d++) + { + var->chunksizes[d] = suggested_size ? suggested_size : 1; + LOG((4, "%s: name %s dim %d DEFAULT_CHUNK_SIZE %d num_values %f type_size %d " + "chunksize %ld", __func__, var->hdr.name, d, DEFAULT_CHUNK_SIZE, num_values, type_size, var->chunksizes[d])); + } + } + + /* Pick a chunk length for each dimension, if one has not already + * been picked above. */ + for (d = 0; d < var->ndims; d++) + if (!var->chunksizes[d]) + { + suggested_size = (pow((double)DEFAULT_CHUNK_SIZE/(num_values * type_size), + 1.0/(double)(var->ndims - num_unlim)) * var->dim[d]->len - .5); + if (suggested_size > var->dim[d]->len) + suggested_size = var->dim[d]->len; + var->chunksizes[d] = suggested_size ? suggested_size : 1; + LOG((4, "%s: name %s dim %d DEFAULT_CHUNK_SIZE %d num_values %f type_size %d " + "chunksize %ld", __func__, var->hdr.name, d, DEFAULT_CHUNK_SIZE, num_values, type_size, var->chunksizes[d])); + } + +#ifdef LOGGING + /* Find total chunk size. */ + for (d = 0; d < var->ndims; d++) + total_chunk_size *= (double) var->chunksizes[d]; + LOG((4, "total_chunk_size %f", total_chunk_size)); +#endif + + /* But did this result in a chunk that is too big? */ + retval = check_chunksizes(grp, var, var->chunksizes); + if (retval) + { + /* Other error? */ + if (retval != NC_EBADCHUNK) + return retval; + + /* Chunk is too big! Reduce each dimension by half and try again. */ + for ( ; retval == NC_EBADCHUNK; retval = check_chunksizes(grp, var, var->chunksizes)) + for (d = 0; d < var->ndims; d++) + var->chunksizes[d] = var->chunksizes[d]/2 ? var->chunksizes[d]/2 : 1; + } + + /* Do we have any big data overhangs? They can be dangerous to + * babies, the elderly, or confused campers who have had too much + * beer. */ + for (d = 0; d < var->ndims; d++) + { + size_t num_chunks; + size_t overhang; + assert(var->chunksizes[d] > 0); + num_chunks = (var->dim[d]->len + var->chunksizes[d] - 1) / var->chunksizes[d]; + if(num_chunks > 0) { + overhang = (num_chunks * var->chunksizes[d]) - var->dim[d]->len; + var->chunksizes[d] -= overhang / num_chunks; + } + } + + return NC_NOERR; +} + +/** + * @internal Give a var a secret HDF5 name. This is needed when a var + * is defined with the same name as a dim, but it is not a coord var + * of that dim. In that case, the var uses a secret name inside the + * HDF5 file. + * + * @param var Pointer to var info. + * @param name Name to use for base of secret name. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EMAXNAME Name too long to fit secret prefix. + * @returns ::NC_ENOMEM Out of memory. + * @author Ed Hartnett + */ +static int +give_var_secret_name(NC_VAR_INFO_T *var, const char *name) +{ + /* Set a different hdf5 name for this variable to avoid name + * clash. */ + if (strlen(name) + strlen(NON_COORD_PREPEND) > NC_MAX_NAME) + return NC_EMAXNAME; + if (!(var->hdf5_name = malloc((strlen(NON_COORD_PREPEND) + + strlen(name) + 1) * sizeof(char)))) + return NC_ENOMEM; + + sprintf(var->hdf5_name, "%s%s", NON_COORD_PREPEND, name); + + return NC_NOERR; +} + +/** + * @internal This is called when a new netCDF-4 variable is defined + * with nc_def_var(). + * + * @param ncid File ID. + * @param name Name. + * @param xtype Type. + * @param ndims Number of dims. HDF5 has maximim of 32. + * @param dimidsp Array of dim IDs. + * @param varidp Gets the var ID. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is + * not netCDF-4/HDF5. + * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3 + * netcdf-4 file. + * @returns ::NC_ELATEDEF Too late to change settings for this variable. + * @returns ::NC_ENOTINDEFINE Not in define mode. + * @returns ::NC_EPERM File is read only. + * @returns ::NC_EMAXDIMS Classic model file exceeds ::NC_MAX_VAR_DIMS. + * @returns ::NC_ESTRICTNC3 Attempting to create netCDF-4 type var in + * classic model file + * @returns ::NC_EBADNAME Bad name. + * @returns ::NC_EBADTYPE Bad type. + * @returns ::NC_ENOMEM Out of memory. + * @returns ::NC_EHDFERR Error returned by HDF5 layer. + * @returns ::NC_EINVAL Invalid input + * @author Ed Hartnett, Dennis Heimbigner + */ +int +NC4_def_var(int ncid, const char *name, nc_type xtype, int ndims, + const int *dimidsp, int *varidp) +{ + NC_GRP_INFO_T *grp; + NC_VAR_INFO_T *var; + NC_DIM_INFO_T *dim; + NC_FILE_INFO_T *h5; + NC_TYPE_INFO_T *type = NULL; + NC_HDF5_TYPE_INFO_T *hdf5_type; + NC_HDF5_GRP_INFO_T *hdf5_grp; + char norm_name[NC_MAX_NAME + 1]; + int d; + int retval; + + /* Find info for this file and group, and set pointer to each. */ + if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) + BAIL(retval); + assert(grp && grp->format_grp_info && h5); + + /* Get HDF5-specific group info. */ + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + + /* HDF5 allows maximum of 32 dimensions. */ + if (ndims > H5S_MAX_RANK) + BAIL(NC_EMAXDIMS); + + /* If it's not in define mode, strict nc3 files error out, + * otherwise switch to define mode. This will also check that the + * file is writable. */ + if (!(h5->flags & NC_INDEF)) + { + if (h5->cmode & NC_CLASSIC_MODEL) + BAIL(NC_ENOTINDEFINE); + if ((retval = NC4_redef(ncid))) + BAIL(retval); + } + assert(!h5->no_write); + + /* Check and normalize the name. */ + if ((retval = nc4_check_name(name, norm_name))) + BAIL(retval); + + /* Not a Type is, well, not a type.*/ + if (xtype == NC_NAT) + BAIL(NC_EBADTYPE); + + /* For classic files, only classic types are allowed. */ + if (h5->cmode & NC_CLASSIC_MODEL && xtype > NC_DOUBLE) + BAIL(NC_ESTRICTNC3); + + /* For classic files limit number of dims. */ + if (h5->cmode & NC_CLASSIC_MODEL && ndims > NC_MAX_VAR_DIMS) + BAIL(NC_EMAXDIMS); + + /* cast needed for braindead systems with signed size_t */ + if ((unsigned long) ndims > X_INT_MAX) /* Backward compat */ + BAIL(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))) + BAIL(retval); + + /* For non-scalar vars, dim IDs must be provided. */ + if (ndims && !dimidsp) + BAIL(NC_EINVAL); + + /* Check all the dimids to make sure they exist. */ + for (d = 0; d < ndims; d++) + if ((retval = nc4_find_dim(grp, dimidsp[d], &dim, NULL))) + BAIL(retval); + + /* These degrubbing messages sure are handy! */ + LOG((2, "%s: name %s type %d ndims %d", __func__, norm_name, xtype, ndims)); +#ifdef LOGGING + { + int dd; + for (dd = 0; dd < ndims; dd++) + LOG((4, "dimid[%d] %d", dd, dimidsp[dd])); + } +#endif + + /* If this is a user-defined type, there is a type struct with + * all the type information. For atomic types, fake up a type + * struct. */ + if (xtype <= NC_STRING) + { + size_t len; + + /* Get type length. */ + if ((retval = nc4_get_typelen_mem(h5, xtype, &len))) + BAIL(retval); + + /* Create new NC_TYPE_INFO_T struct for this atomic type. */ + if ((retval = nc4_type_new(len, nc4_atomic_name[xtype], xtype, &type))) + BAIL(retval); + type->endianness = NC_ENDIAN_NATIVE; + type->size = len; + + /* Allocate storage for HDF5-specific type info. */ + if (!(hdf5_type = calloc(1, sizeof(NC_HDF5_TYPE_INFO_T)))) + BAIL(NC_ENOMEM); + type->format_type_info = hdf5_type; + + /* Get HDF5 typeids. */ + if ((retval = nc4_get_hdf_typeid(h5, xtype, &hdf5_type->hdf_typeid, + type->endianness))) + BAIL(retval); + + /* Get the native HDF5 typeid. */ + if ((hdf5_type->native_hdf_typeid = H5Tget_native_type(hdf5_type->hdf_typeid, + H5T_DIR_DEFAULT)) < 0) + BAIL(NC_EHDFERR); + + /* Set the "class" of the type */ + if (xtype == NC_CHAR) + type->nc_type_class = NC_CHAR; + else + { + H5T_class_t class; + + if ((class = H5Tget_class(hdf5_type->hdf_typeid)) < 0) + BAIL(NC_EHDFERR); + switch(class) + { + case H5T_STRING: + type->nc_type_class = NC_STRING; + break; + + case H5T_INTEGER: + type->nc_type_class = NC_INT; + break; + + case H5T_FLOAT: + type->nc_type_class = NC_FLOAT; + break; + + default: + BAIL(NC_EBADTYPID); + } + } + } + else + { + /* If this is a user defined type, find it. */ + if (nc4_find_type(grp->nc4_info, xtype, &type)) + BAIL(NC_EBADTYPE); + } + + /* Create a new var and fill in some HDF5 cache setting values. */ + if ((retval = nc4_var_list_add(grp, norm_name, ndims, &var))) + BAIL(retval); + + /* Add storage for HDF5-specific var info. */ + if (!(var->format_var_info = calloc(1, sizeof(NC_HDF5_VAR_INFO_T)))) + BAIL(NC_ENOMEM); + + /* Set these state flags for the var. */ + var->is_new_var = NC_TRUE; + var->meta_read = NC_TRUE; + var->atts_read = NC_TRUE; + + /* Point to the type, and increment its ref. count */ + var->type_info = type; + var->type_info->rc++; + type = NULL; + + /* Set variables no_fill to match the database default unless the + * variable type is variable length (NC_STRING or NC_VLEN) or is + * user-defined type. */ + if (var->type_info->nc_type_class < NC_STRING) + var->no_fill = h5->fill_mode; + + /* Assign dimensions to the variable. At the same time, check to + * see if this is a coordinate variable. If so, it will have the + * same name as one of its dimensions. If it is a coordinate var, + * is it a coordinate var in the same group as the dim? Also, check + * whether we should use contiguous or chunked storage. */ + var->contiguous = NC_TRUE; + for (d = 0; d < ndims; d++) + { + NC_GRP_INFO_T *dim_grp; + NC_HDF5_DIM_INFO_T *hdf5_dim; + + /* Look up each dimension */ + if ((retval = nc4_find_dim(grp, dimidsp[d], &dim, &dim_grp))) + BAIL(retval); + assert(dim && dim->format_dim_info); + hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; + + /* Check for dim index 0 having the same name, in the same group */ + if (d == 0 && dim_grp == grp && strcmp(dim->hdr.name, norm_name) == 0) + { + var->dimscale = NC_TRUE; + dim->coord_var = var; + + /* Use variable's dataset ID for the dimscale ID. So delete + * the HDF5 DIM_WITHOUT_VARIABLE dataset that was created for + * this dim. */ + if (hdf5_dim->hdf_dimscaleid) + { + /* Detach dimscale from any variables using it */ + if ((retval = rec_detach_scales(grp, dimidsp[d], + hdf5_dim->hdf_dimscaleid)) < 0) + BAIL(retval); + + /* Close the HDF5 DIM_WITHOUT_VARIABLE dataset. */ + if (H5Dclose(hdf5_dim->hdf_dimscaleid) < 0) + BAIL(NC_EHDFERR); + hdf5_dim->hdf_dimscaleid = 0; + + /* Now delete the DIM_WITHOUT_VARIABLE dataset (it will be + * recreated later, if necessary). */ + if (H5Gunlink(hdf5_grp->hdf_grpid, dim->hdr.name) < 0) + BAIL(NC_EDIMMETA); + } + } + + /* Check for unlimited dimension and turn off contiguous storage. */ + if (dim->unlimited) + var->contiguous = NC_FALSE; + + /* Track dimensions for variable */ + var->dimids[d] = dimidsp[d]; + var->dim[d] = dim; + } + + /* Determine default chunksizes for this variable. (Even for + * variables which may be contiguous.) */ + LOG((4, "allocating array of %d size_t to hold chunksizes for var %s", + var->ndims, var->hdr.name)); + if (var->ndims) + if (!(var->chunksizes = calloc(var->ndims, sizeof(size_t)))) + BAIL(NC_ENOMEM); + + if ((retval = nc4_find_default_chunksizes2(grp, var))) + BAIL(retval); + + /* Is this a variable with a chunksize greater than the current + * cache size? */ + if ((retval = nc4_adjust_var_cache(grp, var))) + BAIL(retval); + + /* If the user names this variable the same as a dimension, but + * doesn't use that dimension first in its list of dimension ids, + * is not a coordinate variable. I need to change its HDF5 name, + * because the dimension will cause a HDF5 dataset to be created, + * and this var has the same name. */ + dim = (NC_DIM_INFO_T*)ncindexlookup(grp->dim,norm_name); + if (dim && (!var->ndims || dimidsp[0] != dim->hdr.id)) + if ((retval = give_var_secret_name(var, var->hdr.name))) + BAIL(retval); + + /* If this is a coordinate var, it is marked as a HDF5 dimension + * scale. (We found dim above.) Otherwise, allocate space to + * remember whether dimension scales have been attached to each + * dimension. */ + if (!var->dimscale && ndims) + if (!(var->dimscale_attached = calloc(ndims, sizeof(nc_bool_t)))) + BAIL(NC_ENOMEM); + + /* Return the varid. */ + if (varidp) + *varidp = var->hdr.id; + LOG((4, "new varid %d", var->hdr.id)); + +exit: + if (type) + if ((retval = nc4_type_free(type))) + BAIL2(retval); + + return retval; +} + +/** + * @internal This functions sets extra stuff about a netCDF-4 variable which + * must be set before the enddef but after the def_var. + * + * @note All pointer parameters may be NULL, in which case they are ignored. + * @param ncid File ID. + * @param varid Variable ID. + * @param shuffle Pointer to shuffle setting. + * @param deflate Pointer to deflate setting. + * @param deflate_level Pointer to deflate level. + * @param fletcher32 Pointer to fletcher32 setting. + * @param contiguous Pointer to contiguous setting. + * @param chunksizes Array of chunksizes. + * @param no_fill Pointer to no_fill setting. + * @param fill_value Pointer to fill value. + * @param endianness Pointer to endianness setting. + * + * @returns ::NC_NOERR for success + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is + * not netCDF-4/HDF5. + * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3 + * netcdf-4 file. + * @returns ::NC_ELATEDEF Too late to change settings for this variable. + * @returns ::NC_ENOTINDEFINE Not in define mode. + * @returns ::NC_EPERM File is read only. + * @returns ::NC_EINVAL Invalid input + * @returns ::NC_EBADCHUNK Bad chunksize. + * @author Ed Hartnett + */ +static int +nc_def_var_extra(int ncid, int varid, int *shuffle, int *deflate, + int *deflate_level, int *fletcher32, int *contiguous, + const size_t *chunksizes, int *no_fill, + const void *fill_value, int *endianness) +{ + NC_GRP_INFO_T *grp; + NC_FILE_INFO_T *h5; + NC_VAR_INFO_T *var; + int d; + int retval; + + /* All or none of these will be provided. */ + assert((deflate && deflate_level && shuffle) || + (!deflate && !deflate_level && !shuffle)); + + LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); + + /* Find info for this file and group, and set pointer to each. */ + if ((retval = nc4_find_nc_grp_h5(ncid, NULL, &grp, &h5))) + return retval; + assert(grp && h5); + + /* Trying to write to a read-only file? No way, Jose! */ + if (h5->no_write) + return NC_EPERM; + + /* Find the var. */ + if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid))) + return NC_ENOTVAR; + assert(var && var->hdr.id == varid); + + /* Can't turn on parallel and deflate/fletcher32/szip/shuffle. */ + if (h5->parallel == NC_TRUE) + if (deflate || fletcher32 || shuffle) + return NC_EINVAL; + + /* If the HDF5 dataset has already been created, then it is too + * late to set all the extra stuff. */ + if (var->created) + return NC_ELATEDEF; + + /* Check compression options. */ + if (deflate && !deflate_level) + return NC_EINVAL; + + /* Valid deflate level? */ + if (deflate) + { + if (*deflate) + if (*deflate_level < NC_MIN_DEFLATE_LEVEL || + *deflate_level > NC_MAX_DEFLATE_LEVEL) + return NC_EINVAL; + + /* For scalars, just ignore attempt to deflate. */ + if (!var->ndims) + return NC_NOERR; + + /* Well, if we couldn't find any errors, I guess we have to take + * the users settings. Darn! */ + var->contiguous = NC_FALSE; + var->deflate = *deflate; + if (*deflate) + var->deflate_level = *deflate_level; + LOG((3, "%s: *deflate_level %d", __func__, *deflate_level)); + } + + /* Shuffle filter? */ + if (shuffle) + { + var->shuffle = *shuffle; + var->contiguous = NC_FALSE; + } + + /* Fletcher32 checksum error protection? */ + if (fletcher32) + { + var->fletcher32 = *fletcher32; + var->contiguous = NC_FALSE; + } + + /* Does the user want a contiguous dataset? Not so fast! Make sure + * that there are no unlimited dimensions, and no filters in use + * for this data. */ + if (contiguous && *contiguous) + { + if (var->deflate || var->fletcher32 || var->shuffle) + return NC_EINVAL; + + for (d = 0; d < var->ndims; d++) + if (var->dim[d]->unlimited) + return NC_EINVAL; + var->contiguous = NC_TRUE; + } + + /* Chunksizes anyone? */ + if (contiguous && *contiguous == NC_CHUNKED) + { + var->contiguous = NC_FALSE; + + /* If the user provided chunksizes, check that they are not too + * big, and that their total size of chunk is less than 4 GB. */ + if (chunksizes) + { + + if ((retval = check_chunksizes(grp, var, chunksizes))) + return retval; + + /* Ensure chunksize is smaller than dimension size */ + for (d = 0; d < var->ndims; d++) + if(!var->dim[d]->unlimited && var->dim[d]->len > 0 && chunksizes[d] > var->dim[d]->len) + return NC_EBADCHUNK; + + /* Set the chunksizes for this variable. */ + for (d = 0; d < var->ndims; d++) + var->chunksizes[d] = chunksizes[d]; + } + } + + /* Is this a variable with a chunksize greater than the current + * cache size? */ + if (!var->contiguous && (deflate || contiguous)) + { + /* Determine default chunksizes for this variable (do nothing + * for scalar vars). */ + if (var->chunksizes && !var->chunksizes[0]) + if ((retval = nc4_find_default_chunksizes2(grp, var))) + return retval; + + /* Adjust the cache. */ + if ((retval = nc4_adjust_var_cache(grp, var))) + return retval; + } + + /* Are we setting a fill modes? */ + if (no_fill) + { + if (*no_fill) + { + /* NC_STRING types may not turn off fill mode. It's disallowed + * by HDF5 and will cause a HDF5 error later. */ + if (*no_fill) + if (var->type_info->hdr.id == NC_STRING) + return NC_EINVAL; + + /* Set the no-fill mode. */ + var->no_fill = NC_TRUE; + } + else + var->no_fill = NC_FALSE; + } + + /* Are we setting a fill value? */ + if (fill_value && !var->no_fill) + { + /* Copy the fill_value. */ + LOG((4, "Copying fill value into metadata for variable %s", + var->hdr.name)); + + /* If there's a _FillValue attribute, delete it. */ + retval = NC4_HDF5_del_att(ncid, varid, _FillValue); + if (retval && retval != NC_ENOTATT) + return retval; + + /* Create a _FillValue attribute. */ + if ((retval = nc_put_att(ncid, varid, _FillValue, var->type_info->hdr.id, + 1, fill_value))) + return retval; + } + + /* Is the user setting the endianness? */ + if (endianness) + var->type_info->endianness = *endianness; + + return NC_NOERR; +} + +/** + * @internal Set compression settings on a variable. This is called by + * nc_def_var_deflate(). + * + * @param ncid File ID. + * @param varid Variable ID. + * @param shuffle True to turn on the shuffle filter. + * @param deflate True to turn on deflation. + * @param deflate_level A number between 0 (no compression) and 9 + * (maximum compression). + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is + * not netCDF-4/HDF5. + * @returns ::NC_ELATEDEF Too late to change settings for this variable. + * @returns ::NC_ENOTINDEFINE Not in define mode. + * @returns ::NC_EINVAL Invalid input + * @author Ed Hartnett, Dennis Heimbigner + */ +int +NC4_def_var_deflate(int ncid, int varid, int shuffle, int deflate, + int deflate_level) +{ + return nc_def_var_extra(ncid, varid, &shuffle, &deflate, + &deflate_level, NULL, NULL, NULL, NULL, NULL, NULL); +} + +/** + * @internal Set checksum on a variable. This is called by + * nc_def_var_fletcher32(). + * + * @param ncid File ID. + * @param varid Variable ID. + * @param fletcher32 Pointer to fletcher32 setting. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is + * not netCDF-4/HDF5. + * @returns ::NC_ELATEDEF Too late to change settings for this variable. + * @returns ::NC_ENOTINDEFINE Not in define mode. + * @returns ::NC_EINVAL Invalid input + * @author Ed Hartnett, Dennis Heimbigner + */ +int +NC4_def_var_fletcher32(int ncid, int varid, int fletcher32) +{ + return nc_def_var_extra(ncid, varid, NULL, NULL, NULL, &fletcher32, + NULL, NULL, NULL, NULL, NULL); +} + +/** + * @internal Define chunking stuff for a var. This is called by + * nc_def_var_chunking(). Chunking is required in any dataset with one + * or more unlimited dimensions in HDF5, or any dataset using a + * filter. + * + * @param ncid File ID. + * @param varid Variable ID. + * @param contiguous Pointer to contiguous setting. + * @param chunksizesp Array of chunksizes. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is + * not netCDF-4/HDF5. + * @returns ::NC_ELATEDEF Too late to change settings for this variable. + * @returns ::NC_ENOTINDEFINE Not in define mode. + * @returns ::NC_EINVAL Invalid input + * @returns ::NC_EBADCHUNK Bad chunksize. + * @author Ed Hartnett, Dennis Heimbigner + */ +int +NC4_def_var_chunking(int ncid, int varid, int contiguous, const size_t *chunksizesp) +{ + return nc_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, + &contiguous, chunksizesp, NULL, NULL, NULL); +} + +/** + * @internal Define chunking stuff for a var. This is called by + * the fortran API. + * + * @param ncid File ID. + * @param varid Variable ID. + * @param contiguous Pointer to contiguous setting. + * @param chunksizesp Array of chunksizes. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is + * not netCDF-4/HDF5. + * @returns ::NC_ELATEDEF Too late to change settings for this variable. + * @returns ::NC_ENOTINDEFINE Not in define mode. + * @returns ::NC_EINVAL Invalid input + * @returns ::NC_EBADCHUNK Bad chunksize. + * @author Ed Hartnett + */ +int +nc_def_var_chunking_ints(int ncid, int varid, int contiguous, int *chunksizesp) +{ + NC_VAR_INFO_T *var; + size_t *cs; + int i, retval; + + /* Get pointer to the var. */ + if ((retval = nc4_hdf5_find_grp_h5_var(ncid, varid, NULL, NULL, &var))) + return retval; + assert(var); + + /* Allocate space for the size_t copy of the chunksizes array. */ + if (var->ndims) + if (!(cs = malloc(var->ndims * sizeof(size_t)))) + return NC_ENOMEM; + + /* Copy to size_t array. */ + for (i = 0; i < var->ndims; i++) + cs[i] = chunksizesp[i]; + + retval = nc_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, + &contiguous, cs, NULL, NULL, NULL); + + if (var->ndims) + free(cs); + return retval; +} + +/** + * @internal This functions sets fill value and no_fill mode for a + * netCDF-4 variable. It is called by nc_def_var_fill(). + * + * @note All pointer parameters may be NULL, in which case they are ignored. + * @param ncid File ID. + * @param varid Variable ID. + * @param no_fill No_fill setting. + * @param fill_value Pointer to fill value. + * + * @returns ::NC_NOERR for success + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is + * not netCDF-4/HDF5. + * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3 + * netcdf-4 file. + * @returns ::NC_ELATEDEF Too late to change settings for this variable. + * @returns ::NC_ENOTINDEFINE Not in define mode. + * @returns ::NC_EPERM File is read only. + * @returns ::NC_EINVAL Invalid input + * @author Ed Hartnett + */ +int +NC4_def_var_fill(int ncid, int varid, int no_fill, const void *fill_value) +{ + return nc_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, NULL, + NULL, &no_fill, fill_value, NULL); +} + +/** + * @internal This functions sets endianness for a netCDF-4 + * variable. Called by nc_def_var_endian(). + * + * @note All pointer parameters may be NULL, in which case they are ignored. + * @param ncid File ID. + * @param varid Variable ID. + * @param endianness Endianness setting. + * + * @returns ::NC_NOERR for success + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is + * not netCDF-4/HDF5. + * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3 + * netcdf-4 file. + * @returns ::NC_ELATEDEF Too late to change settings for this variable. + * @returns ::NC_ENOTINDEFINE Not in define mode. + * @returns ::NC_EPERM File is read only. + * @returns ::NC_EINVAL Invalid input + * @author Ed Hartnett + */ +int +NC4_def_var_endian(int ncid, int varid, int endianness) +{ + return nc_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, &endianness); +} + +/** + * @internal Define filter settings. Called by nc_def_var_filter(). + * + * @param ncid File ID. + * @param varid Variable ID. + * @param id Filter ID + * @param nparams Number of parameters for filter. + * @param parms Filter parameters. + * + * @returns ::NC_NOERR for success + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is + * not netCDF-4/HDF5. + * @returns ::NC_ELATEDEF Too late to change settings for this variable. + * @returns ::NC_EFILTER Filter error. + * @returns ::NC_EINVAL Invalid input + * @author Dennis Heimbigner + */ +int +NC4_def_var_filter(int ncid, int varid, unsigned int id, size_t nparams, + const unsigned int* parms) +{ + int retval = NC_NOERR; + NC *nc; + NC_GRP_INFO_T *grp; + NC_FILE_INFO_T *h5; + NC_VAR_INFO_T *var; + + LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); + + /* Find info for this file and group, and set pointer to each. */ + if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) + return retval; + + assert(nc && grp && h5); + + /* Find the var. */ + var = (NC_VAR_INFO_T*)ncindexith(grp->vars,varid); + if(!var) + return NC_ENOTVAR; + assert(var->hdr.id == varid); + + /* If the HDF5 dataset has already been created, then it is too + * late to set all the extra stuff. */ + if (var->created) + return NC_ELATEDEF; + +#ifdef HAVE_H5Z_SZIP + if(id == H5Z_FILTER_SZIP) { + if(nparams != 2) + return NC_EFILTER; /* incorrect no. of parameters */ + } +#else /*!HAVE_H5Z_SZIP*/ + if(id == H5Z_FILTER_SZIP) + return NC_EFILTER; /* Not allowed */ +#endif + +#if 0 + { + unsigned int fcfg = 0; + herr_t herr = H5Zget_filter_info(id,&fcfg); + if(herr < 0) + return NC_EFILTER; + if((H5Z_FILTER_CONFIG_ENCODE_ENABLED & fcfg) == 0 + || (H5Z_FILTER_CONFIG_DECODE_ENABLED & fcfg) == 0) + return NC_EFILTER; + } +#endif /*0*/ + + var->filterid = id; + var->nparams = nparams; + var->params = NULL; + if(parms != NULL) { + var->params = (unsigned int*)calloc(nparams,sizeof(unsigned int)); + if(var->params == NULL) return NC_ENOMEM; + memcpy(var->params,parms,sizeof(unsigned int)*var->nparams); + } + return NC_NOERR; +} + +/** + * @internal Rename a var to "bubba," for example. This is called by + * nc_rename_var() for netCDF-4 files. This results in complexities + * when coordinate variables are involved. + + * Whenever a var has the same name as a dim, and also uses that dim + * as its first dimension, then that var is aid to be a coordinate + * variable for that dimensions. Coordinate variables are represented + * in the HDF5 by making them dimscales. Dimensions without coordinate + * vars are represented by datasets which are dimscales, but have a + * special attribute marking them as dimscales without associated + * coordinate variables. + * + * When a var is renamed, we must detect whether it has become a + * coordinate var (by being renamed to the same name as a dim that is + * also its first dimension), or whether it is no longer a coordinate + * var. These cause flags to be set in NC_VAR_INFO_T which are used at + * enddef time to make changes in the HDF5 file. + * + * @param ncid File ID. + * @param varid Variable ID + * @param name New name of the variable. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @returns ::NC_EBADNAME Bad name. + * @returns ::NC_EMAXNAME Name is too long. + * @returns ::NC_ENAMEINUSE Name in use. + * @returns ::NC_ENOMEM Out of memory. + * @author Ed Hartnett + */ +int +NC4_rename_var(int ncid, int varid, const char *name) +{ + NC_GRP_INFO_T *grp; + NC_HDF5_GRP_INFO_T *hdf5_grp; + NC_FILE_INFO_T *h5; + NC_VAR_INFO_T *var; + NC_DIM_INFO_T *other_dim; + int use_secret_name = 0; + int retval = NC_NOERR; + + if (!name) + return NC_EINVAL; + + LOG((2, "%s: ncid 0x%x varid %d name %s", __func__, ncid, varid, name)); + + /* 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 && grp->format_grp_info); + + /* Get HDF5-specific group info. */ + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + + /* Is the new name too long? */ + if (strlen(name) > NC_MAX_NAME) + return NC_EMAXNAME; + + /* Trying to write to a read-only file? No way, Jose! */ + if (h5->no_write) + return NC_EPERM; + + /* Check name validity, if strict nc3 rules are in effect for this + * file. */ + if ((retval = NC_check_name(name))) + return retval; + + /* Get the variable wrt varid */ + if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid))) + return NC_ENOTVAR; + + /* Check if new name is in use; note that renaming to same name is + still an error according to the nc_test/test_write.c + code. Why?*/ + if (ncindexlookup(grp->vars, name)) + return NC_ENAMEINUSE; + + /* If we're not in define mode, new name must be of equal or + less size, if strict nc3 rules are in effect for this . */ + if (!(h5->flags & NC_INDEF) && strlen(name) > strlen(var->hdr.name) && + (h5->cmode & NC_CLASSIC_MODEL)) + return NC_ENOTINDEFINE; + + /* Is there another dim with this name, for which this var will not + * be a coord var? If so, we have to create a dim without a + * variable for the old name. */ + if ((other_dim = (NC_DIM_INFO_T *)ncindexlookup(grp->dim, name)) && + strcmp(name, var->dim[0]->hdr.name)) + { + /* Create a dim without var dataset for old dim. */ + if ((retval = nc4_create_dim_wo_var(other_dim))) + return retval; + + /* Give this var a secret HDF5 name so it can co-exist in file + * with dim wp var dataset. Base the secret name on the new var + * name. */ + if ((retval = give_var_secret_name(var, name))) + return retval; + use_secret_name++; + } + + /* Change the HDF5 file, if this var has already been created + there. */ + if (var->created) + { + int v; + char *hdf5_name; /* Dataset will be renamed to this. */ + hdf5_name = use_secret_name ? var->hdf5_name: (char *)name; + + /* Do we need to read var metadata? */ + if (!var->meta_read) + if ((retval = nc4_get_var_meta(var))) + return retval; + + if (var->ndims) + { + NC_HDF5_DIM_INFO_T *hdf5_d0; + hdf5_d0 = (NC_HDF5_DIM_INFO_T *)var->dim[0]->format_dim_info; + + /* Is there an existing dimscale-only dataset of this name? If + * so, it must be deleted. */ + if (hdf5_d0->hdf_dimscaleid) + { + if ((retval = delete_dimscale_dataset(grp, var->dim[0]->hdr.id, + var->dim[0]))) + return retval; + } + } + + LOG((3, "Moving dataset %s to %s", var->hdr.name, name)); + if (H5Lmove(hdf5_grp->hdf_grpid, var->hdr.name, hdf5_grp->hdf_grpid, + hdf5_name, H5P_DEFAULT, H5P_DEFAULT) < 0) + return NC_EHDFERR; + + /* Rename all the vars in this file with a varid greater than + * this var. Varids are assigned based on dataset creation time, + * and we have just changed that for this var. We must do the + * same for all vars with a > varid, so that the creation order + * will continue to be correct. */ + for (v = var->hdr.id + 1; v < ncindexsize(grp->vars); v++) + { + NC_VAR_INFO_T *my_var; + my_var = (NC_VAR_INFO_T *)ncindexith(grp->vars, v); + assert(my_var); + + LOG((3, "mandatory rename of %s to same name", my_var->hdr.name)); + + /* Rename to temp name. */ + if (H5Lmove(hdf5_grp->hdf_grpid, my_var->hdr.name, hdf5_grp->hdf_grpid, + NC_TEMP_NAME, H5P_DEFAULT, H5P_DEFAULT) < 0) + return NC_EHDFERR; + + /* Rename to real name. */ + if (H5Lmove(hdf5_grp->hdf_grpid, NC_TEMP_NAME, hdf5_grp->hdf_grpid, + my_var->hdr.name, H5P_DEFAULT, H5P_DEFAULT) < 0) + return NC_EHDFERR; + } + } + + /* Now change the name in our metadata. */ + free(var->hdr.name); + if (!(var->hdr.name = strdup(name))) + return NC_ENOMEM; + LOG((3, "var is now %s", var->hdr.name)); + + /* Fix hash key and rebuild index. */ + var->hdr.hashkey = NC_hashmapkey(var->hdr.name, strlen(var->hdr.name)); + if (!ncindexrebuild(grp->vars)) + return NC_EINTERNAL; + + /* Check if this was a coordinate variable previously, but names + * are different now */ + if (var->dimscale && strcmp(var->hdr.name, var->dim[0]->hdr.name)) + { + /* Break up the coordinate variable */ + if ((retval = nc4_break_coord_var(grp, var, var->dim[0]))) + return retval; + } + + /* Check if this should become a coordinate variable. */ + if (!var->dimscale) + { + /* Only variables with >0 dimensions can become coordinate + * variables. */ + if (var->ndims) + { + NC_GRP_INFO_T *dim_grp; + NC_DIM_INFO_T *dim; + + /* Check to see if this is became a coordinate variable. If + * so, it will have the same name as dimension index 0. If it + * is a coordinate var, is it a coordinate var in the same + * group as the dim? */ + if ((retval = nc4_find_dim(grp, var->dimids[0], &dim, &dim_grp))) + return retval; + if (!strcmp(dim->hdr.name, name) && dim_grp == grp) + { + /* Reform the coordinate variable. */ + if ((retval = nc4_reform_coord_var(grp, var, dim))) + return retval; + var->became_coord_var = NC_TRUE; + } + } + } + + return retval; +} + +/** + * @internal Write an array of data to a variable. This is called by + * nc_put_vara() and other nc_put_vara_* functions, for netCDF-4 + * files. + * + * @param ncid File ID. + * @param varid Variable ID. + * @param startp Array of start indices. + * @param countp Array of counts. + * @param op pointer that gets the data. + * @param memtype The type of these data in memory. + * + * @returns ::NC_NOERR for success + * @author Ed Hartnett, Dennis Heimbigner + */ +int +NC4_put_vara(int ncid, int varid, const size_t *startp, + const size_t *countp, const void *op, int memtype) +{ + return NC4_put_vars(ncid, varid, startp, countp, NULL, op, memtype); +} + +/** + * @internal Read an array of values. This is called by nc_get_vara() + * for netCDF-4 files, as well as all the other nc_get_vara_* + * functions. + * + * @param ncid File ID. + * @param varid Variable ID. + * @param startp Array of start indices. + * @param countp Array of counts. + * @param ip pointer that gets the data. + * @param memtype The type of these data after it is read into memory. + + * @returns ::NC_NOERR for success + * @author Ed Hartnett, Dennis Heimbigner + */ +int +NC4_get_vara(int ncid, int varid, const size_t *startp, + const size_t *countp, void *ip, int memtype) +{ + return NC4_get_vars(ncid, varid, startp, countp, NULL, ip, memtype); +} + +/** + * @internal Do some common check for NC4_put_vars and + * NC4_get_vars. These checks have to be done when both reading and + * writing data. + * + * @param mem_nc_type Pointer to type of data in memory. + * @param var Pointer to var info struct. + * @param h5 Pointer to HDF5 file info struct. + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +static int +check_for_vara(nc_type *mem_nc_type, NC_VAR_INFO_T *var, NC_FILE_INFO_T *h5) +{ + int retval; + + /* If mem_nc_type is NC_NAT, it means we want to use the file type + * as the mem type as well. */ + assert(mem_nc_type); + if (*mem_nc_type == NC_NAT) + *mem_nc_type = var->type_info->hdr.id; + assert(*mem_nc_type); + + /* No NC_CHAR conversions, you pervert! */ + if (var->type_info->hdr.id != *mem_nc_type && + (var->type_info->hdr.id == NC_CHAR || *mem_nc_type == NC_CHAR)) + return NC_ECHAR; + + /* If we're in define mode, we can't read or write data. */ + if (h5->flags & NC_INDEF) + { + if (h5->cmode & NC_CLASSIC_MODEL) + return NC_EINDEFINE; + if ((retval = nc4_enddef_netcdf4_file(h5))) + return retval; + } + + return NC_NOERR; +} + +#ifdef LOGGING +/** + * @intarnal Print some debug info about dimensions to the log. + */ +static void +log_dim_info(NC_VAR_INFO_T *var, hsize_t *fdims, hsize_t *fmaxdims, + hsize_t *start, hsize_t *count) +{ + int d2; + + /* Print some debugging info... */ + LOG((4, "%s: var name %s ndims %d", __func__, var->hdr.name, var->ndims)); + LOG((4, "File space, and requested:")); + for (d2 = 0; d2 < var->ndims; d2++) + { + LOG((4, "fdims[%d]=%Ld fmaxdims[%d]=%Ld", d2, fdims[d2], d2, + fmaxdims[d2])); + LOG((4, "start[%d]=%Ld count[%d]=%Ld", d2, start[d2], d2, count[d2])); + } +} +#endif /* LOGGING */ + +#ifdef USE_PARALLEL4 +/** + * @internal Set the parallel access for a var (collective + * vs. independent). + * + * @param h5 Pointer to HDF5 file info struct. + * @param var Pointer to var info struct. + * @param xfer_plistid H5FD_MPIO_COLLECTIVE or H5FD_MPIO_INDEPENDENT. + * + * @returns NC_NOERR No error. + * @author Ed Hartnett + */ +static int +set_par_access(NC_FILE_INFO_T *h5, NC_VAR_INFO_T *var, hid_t xfer_plistid) +{ + /* If netcdf is built with parallel I/O, then parallel access can + * be used, and, if this file was opened or created for parallel + * access, we need to set the transfer mode. */ + if (h5->parallel) + { + H5FD_mpio_xfer_t hdf5_xfer_mode; + + /* Decide on collective or independent. */ + hdf5_xfer_mode = (var->parallel_access != NC_INDEPENDENT) ? + H5FD_MPIO_COLLECTIVE : H5FD_MPIO_INDEPENDENT; + + /* Set the mode in the transfer property list. */ + if (H5Pset_dxpl_mpio(xfer_plistid, hdf5_xfer_mode) < 0) + return NC_EPARINIT; + + LOG((4, "%s: %d H5FD_MPIO_COLLECTIVE: %d H5FD_MPIO_INDEPENDENT: %d", + __func__, (int)hdf5_xfer_mode, H5FD_MPIO_COLLECTIVE, + H5FD_MPIO_INDEPENDENT)); + } + return NC_NOERR; +} +#endif /* USE_PARALLEL4 */ + +/** + * @internal Write a strided array of data to a variable. This is + * called by nc_put_vars() and other nc_put_vars_* functions, for + * netCDF-4 files. Also the nc_put_vara() calls end up calling this + * with a NULL stride parameter. + * + * @param ncid File ID. + * @param varid Variable ID. + * @param startp Array of start indices. Must always be provided by + * caller for non-scalar vars. + * @param countp Array of counts. Will default to counts of full + * dimension size if NULL. + * @param stridep Array of strides. Will default to strides of 1 if + * NULL. + * @param data The data to be written. + * @param mem_nc_type The type of the data in memory. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Var not found. + * @returns ::NC_EHDFERR HDF5 function returned error. + * @returns ::NC_EINVALCOORDS Incorrect start. + * @returns ::NC_EEDGE Incorrect start/count. + * @returns ::NC_ENOMEM Out of memory. + * @returns ::NC_EMPI MPI library error (parallel only) + * @returns ::NC_ECANTEXTEND Can't extend dimension for write. + * @returns ::NC_ERANGE Data conversion error. + * @author Ed Hartnett, Dennis Heimbigner + */ +int +NC4_put_vars(int ncid, int varid, const size_t *startp, const size_t *countp, + const ptrdiff_t *stridep, const void *data, nc_type mem_nc_type) +{ + NC_GRP_INFO_T *grp; + NC_FILE_INFO_T *h5; + NC_VAR_INFO_T *var; + NC_DIM_INFO_T *dim; + NC_HDF5_VAR_INFO_T *hdf5_var; + hid_t file_spaceid = 0, mem_spaceid = 0, xfer_plistid = 0; + long long unsigned xtend_size[NC_MAX_VAR_DIMS]; + hsize_t fdims[NC_MAX_VAR_DIMS], fmaxdims[NC_MAX_VAR_DIMS]; + hsize_t start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS]; + hsize_t stride[NC_MAX_VAR_DIMS]; + int need_to_extend = 0; +#ifdef USE_PARALLEL4 + int extend_possible = 0; +#endif + int retval, range_error = 0, i, d2; + void *bufr = NULL; + int need_to_convert = 0; + int zero_count = 0; /* true if a count is zero */ + size_t len = 1; + + /* Find info for this file, group, and var. */ + if ((retval = nc4_hdf5_find_grp_h5_var(ncid, varid, &h5, &grp, &var))) + return retval; + assert(h5 && grp && var && var->hdr.id == varid && var->format_var_info); + + /* Get the HDF5-specific var info. */ + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + /* Cannot convert to user-defined types. */ + if (mem_nc_type >= NC_FIRSTUSERTYPEID) + mem_nc_type = NC_NAT; + + LOG((3, "%s: var->hdr.name %s mem_nc_type %d", __func__, + var->hdr.name, mem_nc_type)); + + /* Check some stuff about the type and the file. If the file must + * be switched from define mode, it happens here. */ + if ((retval = check_for_vara(&mem_nc_type, var, h5))) + return retval; + assert(hdf5_var->hdf_datasetid && (!var->ndims || (startp && countp))); + + /* Convert from size_t and ptrdiff_t to hssize_t, and hsize_t. */ + /* Also do sanity checks */ + for (i = 0; i < var->ndims; i++) + { + /* Check for non-positive stride. */ + if (stridep && stridep[i] <= 0) + return NC_ESTRIDE; + + start[i] = startp[i]; + count[i] = countp ? countp[i] : var->dim[i]->len; + stride[i] = stridep ? stridep[i] : 1; + + /* Check to see if any counts are zero. */ + if (!count[i]) + zero_count++; + } + + /* Get file space of data. */ + if ((file_spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0) + BAIL(NC_EHDFERR); + + /* Get the sizes of all the dims and put them in fdims. */ + if (H5Sget_simple_extent_dims(file_spaceid, fdims, fmaxdims) < 0) + BAIL(NC_EHDFERR); + +#ifdef LOGGING + log_dim_info(var, fdims, fmaxdims, start, count); +#endif + + /* Check dimension bounds. Remember that unlimited dimensions can + * put data beyond their current length. */ + for (d2 = 0; d2 < var->ndims; d2++) + { + hsize_t endindex = start[d2] + stride[d2] * (count[d2] - 1); /* last index written */ + dim = var->dim[d2]; + assert(dim && dim->hdr.id == var->dimids[d2]); + if (count[d2] == 0) + endindex = start[d2]; /* fixup for zero read count */ + if (!dim->unlimited) + { +#ifdef RELAX_COORD_BOUND + /* Allow start to equal dim size if count is zero. */ + if (start[d2] > (hssize_t)fdims[d2] || + (start[d2] == (hssize_t)fdims[d2] && count[d2] > 0)) + BAIL_QUIET(NC_EINVALCOORDS); + if (!zero_count && endindex >= fdims[d2]) + BAIL_QUIET(NC_EEDGE); +#else + if (start[d2] >= (hssize_t)fdims[d2]) + BAIL_QUIET(NC_EINVALCOORDS); + if (endindex >= fdims[d2]) + BAIL_QUIET(NC_EEDGE); +#endif + } + } + + /* Now you would think that no one would be crazy enough to write + a scalar dataspace with one of the array function calls, but you + would be wrong. So let's check to see if the dataset is + scalar. If it is, we won't try to set up a hyperslab. */ + if (H5Sget_simple_extent_type(file_spaceid) == H5S_SCALAR) + { + if ((mem_spaceid = H5Screate(H5S_SCALAR)) < 0) + BAIL(NC_EHDFERR); + } + else + { + if (H5Sselect_hyperslab(file_spaceid, H5S_SELECT_SET, start, stride, + count, NULL) < 0) + BAIL(NC_EHDFERR); + + /* Create a space for the memory, just big enough to hold the slab + we want. */ + if ((mem_spaceid = H5Screate_simple(var->ndims, count, NULL)) < 0) + BAIL(NC_EHDFERR); + } + + /* Are we going to convert any data? (No converting of compound or + * opaque types.) */ + if (mem_nc_type != var->type_info->hdr.id && + mem_nc_type != NC_COMPOUND && mem_nc_type != NC_OPAQUE) + { + size_t file_type_size; + + /* We must convert - allocate a buffer. */ + need_to_convert++; + if (var->ndims) + for (d2=0; d2<var->ndims; d2++) + len *= countp[d2]; + LOG((4, "converting data for var %s type=%d len=%d", var->hdr.name, + var->type_info->hdr.id, len)); + + /* Later on, we will need to know the size of this type in the + * file. */ + assert(var->type_info->size); + file_type_size = var->type_info->size; + + /* If we're reading, we need bufr to have enough memory to store + * the data in the file. If we're writing, we need bufr to be + * big enough to hold all the data in the file's type. */ + if (len > 0) + if (!(bufr = malloc(len * file_type_size))) + BAIL(NC_ENOMEM); + } + else + bufr = (void *)data; + + /* Create the data transfer property list. */ + if ((xfer_plistid = H5Pcreate(H5P_DATASET_XFER)) < 0) + BAIL(NC_EHDFERR); + +#ifdef USE_PARALLEL4 + /* Set up parallel I/O, if needed. */ + if ((retval = set_par_access(h5, var, xfer_plistid))) + BAIL(retval); +#endif + + /* Read this hyperslab from memory. Does the dataset have to be + extended? If it's already extended to the required size, it will + do no harm to reextend it to that size. */ + if (var->ndims) + { + for (d2 = 0; d2 < var->ndims; d2++) + { + hsize_t endindex = start[d2] + stride[d2] * (count[d2] - 1); /* last index written */ + if (count[d2] == 0) + endindex = start[d2]; + dim = var->dim[d2]; + assert(dim && dim->hdr.id == var->dimids[d2]); + if (dim->unlimited) + { +#ifdef USE_PARALLEL4 + extend_possible = 1; +#endif + if (!zero_count && endindex >= fdims[d2]) + { + xtend_size[d2] = (long long unsigned)(endindex+1); + need_to_extend++; + } + else + xtend_size[d2] = (long long unsigned)fdims[d2]; + + if (!zero_count && endindex >= dim->len) + { + dim->len = endindex+1; + dim->extended = NC_TRUE; + } + } + else + { + xtend_size[d2] = (long long unsigned)dim->len; + } + } + +#ifdef USE_PARALLEL4 + /* Check if anyone wants to extend. */ + if (extend_possible && h5->parallel && + NC_COLLECTIVE == var->parallel_access) + { + /* Form consensus opinion among all processes about whether + * to perform collective I/O. */ + if (MPI_SUCCESS != MPI_Allreduce(MPI_IN_PLACE, &need_to_extend, 1, + MPI_INT, MPI_BOR, h5->comm)) + BAIL(NC_EMPI); + } +#endif /* USE_PARALLEL4 */ + + /* If we need to extend it, we also need a new file_spaceid + to reflect the new size of the space. */ + if (need_to_extend) + { + LOG((4, "extending dataset")); +#ifdef USE_PARALLEL4 + if (h5->parallel) + { + if (NC_COLLECTIVE != var->parallel_access) + BAIL(NC_ECANTEXTEND); + + /* Reach consensus about dimension sizes to extend to */ + if (MPI_SUCCESS != MPI_Allreduce(MPI_IN_PLACE, xtend_size, var->ndims, + MPI_UNSIGNED_LONG_LONG, MPI_MAX, + h5->comm)) + BAIL(NC_EMPI); + } +#endif /* USE_PARALLEL4 */ + /* Convert xtend_size back to hsize_t for use with + * H5Dset_extent. */ + for (d2 = 0; d2 < var->ndims; d2++) + fdims[d2] = (hsize_t)xtend_size[d2]; + + if (H5Dset_extent(hdf5_var->hdf_datasetid, fdims) < 0) + BAIL(NC_EHDFERR); + if (file_spaceid > 0 && H5Sclose(file_spaceid) < 0) + BAIL2(NC_EHDFERR); + if ((file_spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0) + BAIL(NC_EHDFERR); + if (H5Sselect_hyperslab(file_spaceid, H5S_SELECT_SET, + start, stride, count, NULL) < 0) + BAIL(NC_EHDFERR); + } + } + + /* Do we need to convert the data? */ + if (need_to_convert) + { + if ((retval = nc4_convert_type(data, bufr, mem_nc_type, var->type_info->hdr.id, + len, &range_error, var->fill_value, + (h5->cmode & NC_CLASSIC_MODEL)))) + BAIL(retval); + } + + /* Write the data. At last! */ + LOG((4, "about to H5Dwrite datasetid 0x%x mem_spaceid 0x%x " + "file_spaceid 0x%x", hdf5_var->hdf_datasetid, mem_spaceid, file_spaceid)); + if (H5Dwrite(hdf5_var->hdf_datasetid, + ((NC_HDF5_TYPE_INFO_T *)var->type_info->format_type_info)->hdf_typeid, + mem_spaceid, file_spaceid, xfer_plistid, bufr) < 0) + BAIL(NC_EHDFERR); + + /* Remember that we have written to this var so that Fill Value + * can't be set for it. */ + if (!var->written_to) + var->written_to = NC_TRUE; + + /* For strict netcdf-3 rules, ignore erange errors between UBYTE + * and BYTE types. */ + if ((h5->cmode & NC_CLASSIC_MODEL) && + (var->type_info->hdr.id == NC_UBYTE || var->type_info->hdr.id == NC_BYTE) && + (mem_nc_type == NC_UBYTE || mem_nc_type == NC_BYTE) && + range_error) + range_error = 0; + +exit: + if (file_spaceid > 0 && H5Sclose(file_spaceid) < 0) + BAIL2(NC_EHDFERR); + if (mem_spaceid > 0 && H5Sclose(mem_spaceid) < 0) + BAIL2(NC_EHDFERR); + if (xfer_plistid && (H5Pclose(xfer_plistid) < 0)) + BAIL2(NC_EPARINIT); + if (need_to_convert && bufr) free(bufr); + + /* If there was an error return it, otherwise return any potential + range error value. If none, return NC_NOERR as usual.*/ + if (retval) + return retval; + if (range_error) + return NC_ERANGE; + return NC_NOERR; +} + +/** + * @internal Read a strided array of data from a variable. This is + * called by nc_get_vars() for netCDF-4 files, as well as all the + * other nc_get_vars_* functions. + * + * @param ncid File ID. + * @param varid Variable ID. + * @param startp Array of start indices. Must be provided for + * non-scalar vars. + * @param countp Array of counts. Will default to counts of extent of + * dimension if NULL. + * @param stridep Array of strides. Will default to strides of 1 if + * NULL. + * @param data The data to be written. + * @param mem_nc_type The type of the data in memory. (Convert to this + * type from file type.) + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Var not found. + * @returns ::NC_EHDFERR HDF5 function returned error. + * @returns ::NC_EINVALCOORDS Incorrect start. + * @returns ::NC_EEDGE Incorrect start/count. + * @returns ::NC_ENOMEM Out of memory. + * @returns ::NC_EMPI MPI library error (parallel only) + * @returns ::NC_ECANTEXTEND Can't extend dimension for write. + * @returns ::NC_ERANGE Data conversion error. + * @author Ed Hartnett, Dennis Heimbigner + */ +int +NC4_get_vars(int ncid, int varid, const size_t *startp, const size_t *countp, + const ptrdiff_t *stridep, void *data, nc_type mem_nc_type) +{ + NC_GRP_INFO_T *grp; + NC_FILE_INFO_T *h5; + NC_VAR_INFO_T *var; + NC_HDF5_VAR_INFO_T *hdf5_var; + NC_DIM_INFO_T *dim; + NC_HDF5_TYPE_INFO_T *hdf5_type; + hid_t file_spaceid = 0, mem_spaceid = 0; + hid_t xfer_plistid = 0; + size_t file_type_size; + hsize_t count[NC_MAX_VAR_DIMS]; + hsize_t fdims[NC_MAX_VAR_DIMS], fmaxdims[NC_MAX_VAR_DIMS]; + hsize_t start[NC_MAX_VAR_DIMS]; + hsize_t stride[NC_MAX_VAR_DIMS]; + void *fillvalue = NULL; + int no_read = 0, provide_fill = 0; + int fill_value_size[NC_MAX_VAR_DIMS]; + int scalar = 0, retval, range_error = 0, i, d2; + void *bufr = NULL; + int need_to_convert = 0; + size_t len = 1; + + /* Find info for this file, group, and var. */ + if ((retval = nc4_hdf5_find_grp_h5_var(ncid, varid, &h5, &grp, &var))) + return retval; + assert(h5 && grp && var && var->hdr.id == varid && var->format_var_info && + var->type_info && var->type_info->size && + var->type_info->format_type_info); + + /* Get the HDF5-specific var and type info. */ + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + hdf5_type = (NC_HDF5_TYPE_INFO_T *)var->type_info->format_type_info; + + LOG((3, "%s: var->hdr.name %s mem_nc_type %d", __func__, + var->hdr.name, mem_nc_type)); + + /* Check some stuff about the type and the file. Also end define + * mode, if needed. */ + if ((retval = check_for_vara(&mem_nc_type, var, h5))) + return retval; + assert(hdf5_var->hdf_datasetid && (!var->ndims || (startp && countp))); + + /* Convert from size_t and ptrdiff_t to hsize_t. Also do sanity + * checks. */ + for (i = 0; i < var->ndims; i++) + { + /* If any of the stride values are non-positive, fail. */ + if (stridep && stridep[i] <= 0) + return NC_ESTRIDE; + + start[i] = startp[i]; + count[i] = countp[i]; + stride[i] = stridep ? stridep[i] : 1; + + /* if any of the count values are zero don't actually read. */ + if (count[i] == 0) + no_read++; + } + + /* Get file space of data. */ + if ((file_spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0) + BAIL(NC_EHDFERR); + + /* Check to ensure the user selection is + * valid. H5Sget_simple_extent_dims gets the sizes of all the dims + * and put them in fdims. */ + if (H5Sget_simple_extent_dims(file_spaceid, fdims, fmaxdims) < 0) + BAIL(NC_EHDFERR); + +#ifdef LOGGING + log_dim_info(var, fdims, fmaxdims, start, count); +#endif + + /* Check dimension bounds. Remember that unlimited dimensions can + * put data beyond their current length. */ + for (d2 = 0; d2 < var->ndims; d2++) + { + hsize_t endindex = start[d2] + stride[d2] * (count[d2] - 1); /* last index read */ + dim = var->dim[d2]; + assert(dim && dim->hdr.id == var->dimids[d2]); + if (count[d2] == 0) + endindex = start[d2]; /* fixup for zero read count */ + if (dim->unlimited) + { + size_t ulen; + + /* We can't go beyond the largest current extent of + the unlimited dim. */ + if ((retval = NC4_inq_dim(ncid, dim->hdr.id, NULL, &ulen))) + BAIL(retval); + + /* Check for out of bound requests. */ +#ifdef RELAX_COORD_BOUND + /* Allow start to equal dim size if count is zero. */ + if (start[d2] > (hssize_t)ulen || + (start[d2] == (hssize_t)ulen && count[d2] > 0)) + BAIL_QUIET(NC_EINVALCOORDS); +#else + if (start[d2] >= (hssize_t)ulen && ulen > 0) + BAIL_QUIET(NC_EINVALCOORDS); +#endif + if (count[d2] && endindex >= ulen) + BAIL_QUIET(NC_EEDGE); + + /* Things get a little tricky here. If we're getting + a GET request beyond the end of this var's + current length in an unlimited dimension, we'll + later need to return the fill value for the + variable. */ + if (start[d2] >= (hssize_t)fdims[d2]) + fill_value_size[d2] = count[d2]; + else if (endindex >= fdims[d2]) + fill_value_size[d2] = count[d2] - ((fdims[d2] - start[d2])/stride[d2]); + else + fill_value_size[d2] = 0; + count[d2] -= fill_value_size[d2]; + if (fill_value_size[d2]) + provide_fill++; + } + else /* Dim is not unlimited. */ + { + /* Check for out of bound requests. */ +#ifdef RELAX_COORD_BOUND + /* Allow start to equal dim size if count is zero. */ + if (start[d2] > (hssize_t)fdims[d2] || + (start[d2] == (hssize_t)fdims[d2] && count[d2] > 0)) + BAIL_QUIET(NC_EINVALCOORDS); +#else + if (start[d2] >= (hssize_t)fdims[d2]) + BAIL_QUIET(NC_EINVALCOORDS); +#endif + if (count[d2] && endindex >= fdims[d2]) + BAIL_QUIET(NC_EEDGE); + + /* Set the fill value boundary */ + fill_value_size[d2] = count[d2]; + } + } + + /* Check the type_info fields. */ + assert(var->type_info && var->type_info->size && + var->type_info->format_type_info); + + /* Later on, we will need to know the size of this type in the + * file. */ + file_type_size = var->type_info->size; + + if (!no_read) + { + /* Now you would think that no one would be crazy enough to write + a scalar dataspace with one of the array function calls, but you + would be wrong. So let's check to see if the dataset is + scalar. If it is, we won't try to set up a hyperslab. */ + if (H5Sget_simple_extent_type(file_spaceid) == H5S_SCALAR) + { + if ((mem_spaceid = H5Screate(H5S_SCALAR)) < 0) + BAIL(NC_EHDFERR); + scalar++; + } + else + { + if (H5Sselect_hyperslab(file_spaceid, H5S_SELECT_SET, + start, stride, count, NULL) < 0) + BAIL(NC_EHDFERR); + /* Create a space for the memory, just big enough to hold the slab + we want. */ + if ((mem_spaceid = H5Screate_simple(var->ndims, count, NULL)) < 0) + BAIL(NC_EHDFERR); + } + + /* Fix bug when reading HDF5 files with variable of type + * fixed-length string. We need to make it look like a + * variable-length string, because that's all netCDF-4 data + * model supports, lacking anonymous dimensions. So + * variable-length strings are in allocated memory that user has + * to free, which we allocate here. */ + if (var->type_info->nc_type_class == NC_STRING && + H5Tget_size(hdf5_type->hdf_typeid) > 1 && + !H5Tis_variable_str(hdf5_type->hdf_typeid)) + { + hsize_t fstring_len; + + if ((fstring_len = H5Tget_size(hdf5_type->hdf_typeid)) == 0) + BAIL(NC_EHDFERR); + if (!(*(char **)data = malloc(1 + fstring_len))) + BAIL(NC_ENOMEM); + bufr = *(char **)data; + } + + /* Are we going to convert any data? (No converting of compound or + * opaque types.) */ + if (mem_nc_type != var->type_info->hdr.id && + mem_nc_type != NC_COMPOUND && mem_nc_type != NC_OPAQUE) + { + /* We must convert - allocate a buffer. */ + need_to_convert++; + if (var->ndims) + for (d2 = 0; d2 < var->ndims; d2++) + len *= countp[d2]; + LOG((4, "converting data for var %s type=%d len=%d", var->hdr.name, + var->type_info->hdr.id, len)); + + /* If we're reading, we need bufr to have enough memory to store + * the data in the file. If we're writing, we need bufr to be + * big enough to hold all the data in the file's type. */ + if (len > 0) + if (!(bufr = malloc(len * file_type_size))) + BAIL(NC_ENOMEM); + } + else + if (!bufr) + bufr = data; + + /* Create the data transfer property list. */ + if ((xfer_plistid = H5Pcreate(H5P_DATASET_XFER)) < 0) + BAIL(NC_EHDFERR); + +#ifdef USE_PARALLEL4 + /* Set up parallel I/O, if needed. */ + if ((retval = set_par_access(h5, var, xfer_plistid))) + BAIL(retval); +#endif + + /* Read this hyperslab into memory. */ + LOG((5, "About to H5Dread some data...")); + if (H5Dread(hdf5_var->hdf_datasetid, + ((NC_HDF5_TYPE_INFO_T *)var->type_info->format_type_info)->native_hdf_typeid, + mem_spaceid, file_spaceid, xfer_plistid, bufr) < 0) + BAIL(NC_EHDFERR); + + /* Convert data type if needed. */ + if (need_to_convert) + { + if ((retval = nc4_convert_type(bufr, data, var->type_info->hdr.id, mem_nc_type, + len, &range_error, var->fill_value, + (h5->cmode & NC_CLASSIC_MODEL)))) + BAIL(retval); + + /* For strict netcdf-3 rules, ignore erange errors between UBYTE + * and BYTE types. */ + if ((h5->cmode & NC_CLASSIC_MODEL) && + (var->type_info->hdr.id == NC_UBYTE || var->type_info->hdr.id == NC_BYTE) && + (mem_nc_type == NC_UBYTE || mem_nc_type == NC_BYTE) && + range_error) + range_error = 0; + } + } /* endif ! no_read */ + else + { +#ifdef USE_PARALLEL4 /* Start block contributed by HDF group. */ + /* For collective IO read, some processes may not have any element for reading. + Collective requires all processes to participate, so we use H5Sselect_none + for these processes. */ + if (var->parallel_access == NC_COLLECTIVE) + { + /* Create the data transfer property list. */ + if ((xfer_plistid = H5Pcreate(H5P_DATASET_XFER)) < 0) + BAIL(NC_EHDFERR); + + if ((retval = set_par_access(h5, var, xfer_plistid))) + BAIL(retval); + + if (H5Sselect_none(file_spaceid) < 0) + BAIL(NC_EHDFERR); + + /* Since no element will be selected, we just get the memory + * space the same as the file space. */ + if ((mem_spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0) + BAIL(NC_EHDFERR); + if (H5Sselect_none(mem_spaceid) < 0) + BAIL(NC_EHDFERR); + + /* Read this hyperslab into memory. */ + LOG((5, "About to H5Dread some data...")); + if (H5Dread(hdf5_var->hdf_datasetid, + ((NC_HDF5_TYPE_INFO_T *)var->type_info->format_type_info)->native_hdf_typeid, + mem_spaceid, file_spaceid, xfer_plistid, bufr) < 0) + BAIL(NC_EHDFERR); + } +#endif /* USE_PARALLEL4 */ + } + /* Now we need to fake up any further data that was asked for, + using the fill values instead. First skip past the data we + just read, if any. */ + if (!scalar && provide_fill) + { + void *filldata; + size_t real_data_size = 0; + size_t fill_len; + + /* Skip past the real data we've already read. */ + if (!no_read) + for (real_data_size = file_type_size, d2 = 0; d2 < var->ndims; d2++) + real_data_size *= (count[d2] - start[d2]); + + /* Get the fill value from the HDF5 variable. Memory will be + * allocated. */ + if (nc4_get_fill_value(h5, var, &fillvalue) < 0) + BAIL(NC_EHDFERR); + + /* How many fill values do we need? */ + for (fill_len = 1, d2 = 0; d2 < var->ndims; d2++) + fill_len *= (fill_value_size[d2] ? fill_value_size[d2] : 1); + + /* Copy the fill value into the rest of the data buffer. */ + filldata = (char *)data + real_data_size; + for (i = 0; i < fill_len; i++) + { + + if (var->type_info->nc_type_class == NC_STRING) + { + if (*(char **)fillvalue) + { + if (!(*(char **)filldata = strdup(*(char **)fillvalue))) + BAIL(NC_ENOMEM); + } + else + *(char **)filldata = NULL; + } + else if (var->type_info->nc_type_class == NC_VLEN) + { + if (fillvalue) + { + memcpy(filldata,fillvalue,file_type_size); + } else { + *(char **)filldata = NULL; + } + } + else + memcpy(filldata, fillvalue, file_type_size); + filldata = (char *)filldata + file_type_size; + } + } + +exit: + if (file_spaceid > 0) + if (H5Sclose(file_spaceid) < 0) + BAIL2(NC_EHDFERR); + if (mem_spaceid > 0) + if (H5Sclose(mem_spaceid) < 0) + BAIL2(NC_EHDFERR); + if (xfer_plistid > 0) + if (H5Pclose(xfer_plistid) < 0) + BAIL2(NC_EHDFERR); + if (need_to_convert && bufr) + free(bufr); + if (fillvalue) + { + if (var->type_info->nc_type_class == NC_VLEN) + nc_free_vlen((nc_vlen_t *)fillvalue); + else if (var->type_info->nc_type_class == NC_STRING && *(char **)fillvalue) + free(*(char **)fillvalue); + free(fillvalue); + } + + /* If there was an error return it, otherwise return any potential + range error value. If none, return NC_NOERR as usual.*/ + if (retval) + return retval; + if (range_error) + return NC_ERANGE; + return NC_NOERR; +} + +/** + * @internal Get all the information about a variable. Pass NULL for + * whatever you don't care about. + * + * @param ncid File ID. + * @param varid Variable ID. + * @param name Gets name. + * @param xtypep Gets type. + * @param ndimsp Gets number of dims. + * @param dimidsp Gets array of dim IDs. + * @param nattsp Gets number of attributes. + * @param shufflep Gets shuffle setting. + * @param deflatep Gets deflate setting. + * @param deflate_levelp Gets deflate level. + * @param fletcher32p Gets fletcher32 setting. + * @param contiguousp Gets contiguous setting. + * @param chunksizesp Gets chunksizes. + * @param no_fill Gets fill mode. + * @param fill_valuep Gets fill value. + * @param endiannessp Gets one of ::NC_ENDIAN_BIG ::NC_ENDIAN_LITTLE + * ::NC_ENDIAN_NATIVE + * @param idp Pointer to memory to store filter id. + * @param nparamsp Pointer to memory to store filter parameter count. + * @param params Pointer to vector of unsigned integers into which + * to store filter parameters. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Bad varid. + * @returns ::NC_ENOMEM Out of memory. + * @returns ::NC_EINVAL Invalid input. + * @author Ed Hartnett, Dennis Heimbigner + */ +int +NC4_HDF5_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, + int *ndimsp, int *dimidsp, int *nattsp, + int *shufflep, int *deflatep, int *deflate_levelp, + int *fletcher32p, int *contiguousp, size_t *chunksizesp, + int *no_fill, void *fill_valuep, int *endiannessp, + unsigned int *idp, size_t *nparamsp, unsigned int *params) +{ + NC_FILE_INFO_T *h5; + NC_GRP_INFO_T *grp; + NC_VAR_INFO_T *var = NULL; + int retval; + + LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); + + /* Find the file, group, and var info, and do lazy att read if + * needed. */ + if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, NULL, 0, 0, NULL, + &h5, &grp, &var, NULL))) + return retval; + assert(grp && h5); + + /* Now that lazy atts have been read, use the libsrc4 function to + * get the answers. */ + return NC4_inq_var_all(ncid, varid, name, xtypep, ndimsp, dimidsp, nattsp, + shufflep, deflatep, deflate_levelp, fletcher32p, + contiguousp, chunksizesp, no_fill, fill_valuep, + endiannessp, idp, nparamsp, params); +} + +/** + * @internal Set chunk cache size for a variable. This is the internal + * function called by nc_set_var_chunk_cache(). + * + * @param ncid File ID. + * @param varid Variable ID. + * @param size Size in bytes to set cache. + * @param nelems Number of elements in cache. + * @param preemption Controls cache swapping. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict + * nc3 netcdf-4 file. + * @returns ::NC_EINVAL Invalid input. + * @returns ::NC_EHDFERR HDF5 error. + * @author Ed Hartnett + */ +int +NC4_HDF5_set_var_chunk_cache(int ncid, int varid, size_t size, size_t nelems, + float preemption) +{ + NC_GRP_INFO_T *grp; + NC_FILE_INFO_T *h5; + NC_VAR_INFO_T *var; + int retval; + + /* Check input for validity. */ + if (preemption < 0 || preemption > 1) + return NC_EINVAL; + + /* Find info for this file and group, and set pointer to each. */ + if ((retval = nc4_find_nc_grp_h5(ncid, NULL, &grp, &h5))) + return retval; + assert(grp && h5); + + /* Find the var. */ + if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid))) + return NC_ENOTVAR; + assert(var && var->hdr.id == varid); + + /* Set the values. */ + var->chunk_cache_size = size; + var->chunk_cache_nelems = nelems; + var->chunk_cache_preemption = preemption; + + /* Reopen the dataset to bring new settings into effect. */ + if ((retval = nc4_reopen_dataset(grp, var))) + return retval; + return NC_NOERR; +} + +/** + * @internal A wrapper for NC4_set_var_chunk_cache(), we need this + * version for fortran. Negative values leave settings as they are. + * + * @param ncid File ID. + * @param varid Variable ID. + * @param size Size in bytes to set cache. + * @param nelems Number of elements in cache. + * @param preemption Controls cache swapping. + * + * @returns ::NC_NOERR for success + * @author Ed Hartnett + */ +int +nc_set_var_chunk_cache_ints(int ncid, int varid, int size, int nelems, + int preemption) +{ + size_t real_size = H5D_CHUNK_CACHE_NBYTES_DEFAULT; + size_t real_nelems = H5D_CHUNK_CACHE_NSLOTS_DEFAULT; + float real_preemption = CHUNK_CACHE_PREEMPTION; + + if (size >= 0) + real_size = ((size_t) size) * MEGABYTE; + + if (nelems >= 0) + real_nelems = nelems; + + if (preemption >= 0) + real_preemption = preemption / 100.; + + return NC4_HDF5_set_var_chunk_cache(ncid, varid, real_size, real_nelems, + real_preemption); +} diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4hdf.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4hdf.c new file mode 100644 index 0000000000000000000000000000000000000000..6b496094c61890f41758034533367ef8c647be53 --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4hdf.c @@ -0,0 +1,2640 @@ +/* Copyright 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 contains functions internal to the netcdf4 library. None of + * the functions in this file are exposed in the exetnal API. These + * functions handle the HDF interface. + * + * @author Ed Hartnett, Dennis Heimbigner, Ward Fisher + */ + +#include "config.h" +#include "hdf5internal.h" +#include <math.h> + +#ifdef HAVE_INTTYPES_H +#define __STDC_FORMAT_MACROS +#include <inttypes.h> +#endif + +#define NC_HDF5_MAX_NAME 1024 /**< @internal Max size of HDF5 name. */ + +/** + * @internal Flag attributes in a linked list as dirty. + * + * @param attlist List of attributes, may be NULL. + * + * @return NC_NOERR No error. + * @author Dennis Heimbigner + */ +static int +flag_atts_dirty(NCindex *attlist) { + + NC_ATT_INFO_T *att = NULL; + int i; + + if(attlist == NULL) { + return NC_NOERR; + } + + for(i=0;i<ncindexsize(attlist);i++) { + att = (NC_ATT_INFO_T*)ncindexith(attlist,i); + if(att == NULL) continue; + att->dirty = NC_TRUE; + } + + return NC_NOERR; +} + +/** + * @internal This function is needed to handle one special case: what + * if the user defines a dim, writes metadata, then goes back into + * define mode and adds a coordinate var for the already existing + * dim. In that case, I need to recreate the dim's dimension scale + * dataset, and then I need to go to every var in the file which uses + * that dimension, and attach the new dimension scale. + * + * @param grp Pointer to group info struct. + * @param dimid Dimension ID. + * @param dimscaleid HDF5 dimension scale ID. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @author Ed Hartnett + */ +int +rec_reattach_scales(NC_GRP_INFO_T *grp, int dimid, hid_t dimscaleid) +{ + NC_VAR_INFO_T *var; + NC_GRP_INFO_T *child_grp; + int d, i; + int retval; + + assert(grp && grp->hdr.name && dimid >= 0 && dimscaleid >= 0); + LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name)); + + /* If there are any child groups, attach dimscale there, if needed. */ + for (i = 0; i < ncindexsize(grp->children); i++) + { + child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children, i); + assert(child_grp); + if ((retval = rec_reattach_scales(child_grp, dimid, dimscaleid))) + return retval; + } + + /* Find any vars that use this dimension id. */ + for (i = 0; i < ncindexsize(grp->vars); i++) + { + NC_HDF5_VAR_INFO_T *hdf5_var; + + var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i); + assert(var && var->format_var_info); + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + for (d = 0; d < var->ndims; d++) + { + if (var->dimids[d] == dimid && !var->dimscale) + { + LOG((2, "%s: attaching scale for dimid %d to var %s", + __func__, var->dimids[d], var->hdr.name)); + if (var->created) + { + if (H5DSattach_scale(hdf5_var->hdf_datasetid, + dimscaleid, d) < 0) + return NC_EHDFERR; + var->dimscale_attached[d] = NC_TRUE; + } + } + } + } + return NC_NOERR; +} + +/** + * @internal This function is needed to handle one special case: what + * if the user defines a dim, writes metadata, then goes back into + * define mode and adds a coordinate var for the already existing + * dim. In that case, I need to recreate the dim's dimension scale + * dataset, and then I need to go to every var in the file which uses + * that dimension, and attach the new dimension scale. + * + * @param grp Pointer to group info struct. + * @param dimid Dimension ID. + * @param dimscaleid HDF5 dimension scale ID. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @author Ed Hartnett + */ +int +rec_detach_scales(NC_GRP_INFO_T *grp, int dimid, hid_t dimscaleid) +{ + NC_VAR_INFO_T *var; + NC_GRP_INFO_T *child_grp; + int d, i; + int retval; + + assert(grp && grp->hdr.name && dimid >= 0 && dimscaleid >= 0); + LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name)); + + /* If there are any child groups, detach dimscale there, if needed. */ + for(i=0;i<ncindexsize(grp->children);i++) { + child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children,i); + if(child_grp == NULL) continue; + if ((retval = rec_detach_scales(child_grp, dimid, dimscaleid))) + return retval; + } + + /* Find any vars that use this dimension id. */ + for (i = 0; i < ncindexsize(grp->vars); i++) + { + NC_HDF5_VAR_INFO_T *hdf5_var; + var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i); + assert(var && var->format_var_info); + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + for (d = 0; d < var->ndims; d++) + { + if (var->dimids[d] == dimid && !var->dimscale) + { + LOG((2, "%s: detaching scale for dimid %d to var %s", + __func__, var->dimids[d], var->hdr.name)); + if (var->created) + { + if (var->dimscale_attached && var->dimscale_attached[d]) + { + if (H5DSdetach_scale(hdf5_var->hdf_datasetid, + dimscaleid, d) < 0) + return NC_EHDFERR; + var->dimscale_attached[d] = NC_FALSE; + } + } + } + } + } + return NC_NOERR; +} + +/** + * @internal Open a HDF5 dataset and leave it open. + * + * @param grp Pointer to group info struct. + * @param varid Variable ID. + * @param dataset Pointer that gets the HDF5 dataset ID. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @author Ed Hartnett + */ +int +nc4_open_var_grp2(NC_GRP_INFO_T *grp, int varid, hid_t *dataset) +{ + NC_VAR_INFO_T *var; + NC_HDF5_VAR_INFO_T *hdf5_var; + + assert(grp && grp->format_grp_info && dataset); + + /* Find the requested varid. */ + if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid))) + return NC_ENOTVAR; + assert(var && var->hdr.id == varid && var->format_var_info); + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + /* Open this dataset if necessary. */ + if (!hdf5_var->hdf_datasetid) + { + NC_HDF5_GRP_INFO_T *hdf5_grp; + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + + if ((hdf5_var->hdf_datasetid = H5Dopen2(hdf5_grp->hdf_grpid, + var->hdr.name, H5P_DEFAULT)) < 0) + return NC_ENOTVAR; + } + + *dataset = hdf5_var->hdf_datasetid; + + return NC_NOERR; +} + +/** + * @internal What fill value should be used for a variable? + * + * @param h5 Pointer to HDF5 file info struct. + * @param var Pointer to variable info struct. + * @param fillp Pointer that gets pointer to fill value. + * + * @returns NC_NOERR No error. + * @returns NC_ENOMEM Out of memory. + * @author Ed Hartnett + */ +int +nc4_get_fill_value(NC_FILE_INFO_T *h5, NC_VAR_INFO_T *var, void **fillp) +{ + size_t size; + int retval; + + /* Find out how much space we need for this type's fill value. */ + if (var->type_info->nc_type_class == NC_VLEN) + size = sizeof(nc_vlen_t); + else if (var->type_info->nc_type_class == NC_STRING) + size = sizeof(char *); + else + { + if ((retval = nc4_get_typelen_mem(h5, var->type_info->hdr.id, &size))) + return retval; + } + assert(size); + + /* Allocate the space. */ + if (!((*fillp) = calloc(1, size))) + return NC_ENOMEM; + + /* If the user has set a fill_value for this var, use, otherwise + * find the default fill value. */ + if (var->fill_value) + { + LOG((4, "Found a fill value for var %s", var->hdr.name)); + if (var->type_info->nc_type_class == NC_VLEN) + { + nc_vlen_t *in_vlen = (nc_vlen_t *)(var->fill_value), *fv_vlen = (nc_vlen_t *)(*fillp); + size_t basetypesize = 0; + + if((retval=nc4_get_typelen_mem(h5, var->type_info->u.v.base_nc_typeid, &basetypesize))) + return retval; + + fv_vlen->len = in_vlen->len; + if (!(fv_vlen->p = malloc(basetypesize * in_vlen->len))) + { + free(*fillp); + *fillp = NULL; + return NC_ENOMEM; + } + memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * basetypesize); + } + else if (var->type_info->nc_type_class == NC_STRING) + { + if (*(char **)var->fill_value) + if (!(**(char ***)fillp = strdup(*(char **)var->fill_value))) + { + free(*fillp); + *fillp = NULL; + return NC_ENOMEM; + } + } + else + memcpy((*fillp), var->fill_value, size); + } + else + { + if (nc4_get_default_fill_value(var->type_info, *fillp)) + { + /* Note: release memory, but don't return error on failure */ + free(*fillp); + *fillp = NULL; + } + } + + return NC_NOERR; +} + +/** + * @internal Given a netcdf type, return appropriate HDF typeid. (All + * hdf_typeid's returned from this routine must be H5Tclosed by the + * caller). + * + * @param h5 Pointer to HDF5 file info struct. + * @param xtype NetCDF type ID. + * @param hdf_typeid Pointer that gets the HDF5 type ID. + * @param endianness Desired endianness in HDF5 type. + * + * @return NC_NOERR No error. + * @return NC_ECHAR Conversions of NC_CHAR forbidden. + * @return NC_EVARMETA HDF5 returning error creating datatype. + * @return NC_EHDFERR HDF5 returning error. + * @return NC_EBADTYE Type not found. + * @author Ed Hartnett + */ +int +nc4_get_hdf_typeid(NC_FILE_INFO_T *h5, nc_type xtype, + hid_t *hdf_typeid, int endianness) +{ + NC_TYPE_INFO_T *type; + hid_t typeid = 0; + int retval = NC_NOERR; + + assert(hdf_typeid && h5); + + *hdf_typeid = -1; + + /* Determine an appropriate HDF5 datatype */ + if (xtype == NC_NAT) + return NC_EBADTYPE; + else if (xtype == NC_CHAR || xtype == NC_STRING) + { + /* NC_CHAR & NC_STRING types create a new HDF5 datatype */ + if (xtype == NC_CHAR) + { + if ((typeid = H5Tcopy(H5T_C_S1)) < 0) + return NC_EHDFERR; + if (H5Tset_strpad(typeid, H5T_STR_NULLTERM) < 0) + BAIL(NC_EVARMETA); + if(H5Tset_cset(typeid, H5T_CSET_ASCII) < 0) + BAIL(NC_EVARMETA); + + /* Take ownership of the newly created HDF5 datatype */ + *hdf_typeid = typeid; + typeid = 0; + } + else + { + if ((typeid = H5Tcopy(H5T_C_S1)) < 0) + return NC_EHDFERR; + if (H5Tset_size(typeid, H5T_VARIABLE) < 0) + BAIL(NC_EVARMETA); + if(H5Tset_cset(typeid, H5T_CSET_UTF8) < 0) + BAIL(NC_EVARMETA); + + /* Take ownership of the newly created HDF5 datatype */ + *hdf_typeid = typeid; + typeid = 0; + } + } + else + { + /* All other types use an existing HDF5 datatype */ + switch (xtype) + { + case NC_BYTE: /* signed 1 byte integer */ + if (endianness == NC_ENDIAN_LITTLE) + typeid = H5T_STD_I8LE; + else if (endianness == NC_ENDIAN_BIG) + typeid = H5T_STD_I8BE; + else + typeid = H5T_NATIVE_SCHAR; + break; + + case NC_SHORT: /* signed 2 byte integer */ + if (endianness == NC_ENDIAN_LITTLE) + typeid = H5T_STD_I16LE; + else if (endianness == NC_ENDIAN_BIG) + typeid = H5T_STD_I16BE; + else + typeid = H5T_NATIVE_SHORT; + break; + + case NC_INT: + if (endianness == NC_ENDIAN_LITTLE) + typeid = H5T_STD_I32LE; + else if (endianness == NC_ENDIAN_BIG) + typeid = H5T_STD_I32BE; + else + typeid = H5T_NATIVE_INT; + break; + + case NC_UBYTE: + if (endianness == NC_ENDIAN_LITTLE) + typeid = H5T_STD_U8LE; + else if (endianness == NC_ENDIAN_BIG) + typeid = H5T_STD_U8BE; + else + typeid = H5T_NATIVE_UCHAR; + break; + + case NC_USHORT: + if (endianness == NC_ENDIAN_LITTLE) + typeid = H5T_STD_U16LE; + else if (endianness == NC_ENDIAN_BIG) + typeid = H5T_STD_U16BE; + else + typeid = H5T_NATIVE_USHORT; + break; + + case NC_UINT: + if (endianness == NC_ENDIAN_LITTLE) + typeid = H5T_STD_U32LE; + else if (endianness == NC_ENDIAN_BIG) + typeid = H5T_STD_U32BE; + else + typeid = H5T_NATIVE_UINT; + break; + + case NC_INT64: + if (endianness == NC_ENDIAN_LITTLE) + typeid = H5T_STD_I64LE; + else if (endianness == NC_ENDIAN_BIG) + typeid = H5T_STD_I64BE; + else + typeid = H5T_NATIVE_LLONG; + break; + + case NC_UINT64: + if (endianness == NC_ENDIAN_LITTLE) + typeid = H5T_STD_U64LE; + else if (endianness == NC_ENDIAN_BIG) + typeid = H5T_STD_U64BE; + else + typeid = H5T_NATIVE_ULLONG; + break; + + case NC_FLOAT: + if (endianness == NC_ENDIAN_LITTLE) + typeid = H5T_IEEE_F32LE; + else if (endianness == NC_ENDIAN_BIG) + typeid = H5T_IEEE_F32BE; + else + typeid = H5T_NATIVE_FLOAT; + break; + + case NC_DOUBLE: + if (endianness == NC_ENDIAN_LITTLE) + typeid = H5T_IEEE_F64LE; + else if (endianness == NC_ENDIAN_BIG) + typeid = H5T_IEEE_F64BE; + else + typeid = H5T_NATIVE_DOUBLE; + break; + + default: + /* Maybe this is a user defined type? */ + if (nc4_find_type(h5, xtype, &type)) + return NC_EBADTYPE; + if (!type) + return NC_EBADTYPE; + typeid = ((NC_HDF5_TYPE_INFO_T *)type->format_type_info)->hdf_typeid; + break; + } + assert(typeid); + + /* Copy the HDF5 datatype, so the function operates uniformly */ + if ((*hdf_typeid = H5Tcopy(typeid)) < 0) + return NC_EHDFERR; + typeid = 0; + } + assert(*hdf_typeid != -1); + +exit: + if (typeid > 0 && H5Tclose(typeid) < 0) + BAIL2(NC_EHDFERR); + return retval; +} + +/** + * @internal Write an attribute. + * + * @param grp Pointer to group info struct. + * @param varid Variable ID or NC_GLOBAL. + * @param att Pointer to attribute info struct. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_ENOTVAR Variable not found. + * @returns ::NC_EPERM Read-only file. + * @returns ::NC_EHDFERR HDF5 returned error. + * @returns ::NC_EATTMETA HDF5 returned error with attribute calls. + * @author Ed Hartnett + */ +static int +put_att_grpa(NC_GRP_INFO_T *grp, int varid, NC_ATT_INFO_T *att) +{ + NC_HDF5_GRP_INFO_T *hdf5_grp; + hid_t datasetid = 0, locid; + hid_t attid = 0, spaceid = 0, file_typeid = 0; + hid_t existing_att_typeid = 0, existing_attid = 0, existing_spaceid = 0; + hsize_t dims[1]; /* netcdf attributes always 1-D. */ + htri_t attr_exists; + int reuse_att = 0; /* Will be true if we can re-use an existing att. */ + void *data; + int phoney_data = 99; + int retval = NC_NOERR; + + assert(att->hdr.name && grp && grp->format_grp_info); + LOG((3, "%s: varid %d att->hdr.id %d att->hdr.name %s att->nc_typeid %d " + "att->len %d", __func__, varid, att->hdr.id, att->hdr.name, + att->nc_typeid, att->len)); + + /* Get HDF5-specific group info. */ + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + + /* If the file is read-only, return an error. */ + if (grp->nc4_info->no_write) + BAIL(NC_EPERM); + + /* Get the hid to attach the attribute to, or read it from. */ + if (varid == NC_GLOBAL) + locid = hdf5_grp->hdf_grpid; + else + { + if ((retval = nc4_open_var_grp2(grp, varid, &datasetid))) + BAIL(retval); + locid = datasetid; + } + + /* Get the length ready, and find the HDF type we'll be + * writing. */ + dims[0] = att->len; + if ((retval = nc4_get_hdf_typeid(grp->nc4_info, att->nc_typeid, + &file_typeid, 0))) + BAIL(retval); + + /* Even if the length is zero, HDF5 won't let me write with a + * NULL pointer. So if the length of the att is zero, point to + * some phoney data (which won't be written anyway.)*/ + if (!dims[0]) + data = &phoney_data; + else if (att->data) + data = att->data; + else if (att->stdata) + data = att->stdata; + else + data = att->vldata; + + /* NC_CHAR types require some extra work. The space ID is set to + * scalar, and the type is told how long the string is. If it's + * really zero length, set the size to 1. (The fact that it's + * really zero will be marked by the NULL dataspace, but HDF5 + * doesn't allow me to set the size of the type to zero.)*/ + if (att->nc_typeid == NC_CHAR) + { + size_t string_size = dims[0]; + if (!string_size) + { + string_size = 1; + if ((spaceid = H5Screate(H5S_NULL)) < 0) + BAIL(NC_EATTMETA); + } + else + { + if ((spaceid = H5Screate(H5S_SCALAR)) < 0) + BAIL(NC_EATTMETA); + } + if (H5Tset_size(file_typeid, string_size) < 0) + BAIL(NC_EATTMETA); + if (H5Tset_strpad(file_typeid, H5T_STR_NULLTERM) < 0) + BAIL(NC_EATTMETA); + } + else + { + if (!att->len) + { + if ((spaceid = H5Screate(H5S_NULL)) < 0) + BAIL(NC_EATTMETA); + } + else + { + if ((spaceid = H5Screate_simple(1, dims, NULL)) < 0) + BAIL(NC_EATTMETA); + } + } + + /* Does the att exists already? */ + if ((attr_exists = H5Aexists(locid, att->hdr.name)) < 0) + BAIL(NC_EHDFERR); + if (attr_exists) + { + hssize_t npoints; + + /* Open the attribute. */ + if ((existing_attid = H5Aopen(locid, att->hdr.name, H5P_DEFAULT)) < 0) + BAIL(NC_EATTMETA); + + /* Find the type of the existing attribute. */ + if ((existing_att_typeid = H5Aget_type(existing_attid)) < 0) + BAIL(NC_EATTMETA); + + /* How big is the attribute? */ + if ((existing_spaceid = H5Aget_space(existing_attid)) < 0) + BAIL(NC_EATTMETA); + if ((npoints = H5Sget_simple_extent_npoints(existing_spaceid)) < 0) + BAIL(NC_EATTMETA); + + /* Delete the attribute. */ + if (file_typeid != existing_att_typeid || npoints != att->len) + { + if (H5Adelete(locid, att->hdr.name) < 0) + BAIL(NC_EHDFERR); + } + else + { + reuse_att++; + } + } + + /* Create the attribute. */ + if ((attid = H5Acreate(locid, att->hdr.name, file_typeid, spaceid, + H5P_DEFAULT)) < 0) + BAIL(NC_EATTMETA); + + /* Write the values, (even if length is zero). */ + if (H5Awrite(attid, file_typeid, data) < 0) + BAIL(NC_EATTMETA); + +exit: + if (file_typeid && H5Tclose(file_typeid)) + BAIL2(NC_EHDFERR); + if (attid > 0 && H5Aclose(attid) < 0) + BAIL2(NC_EHDFERR); + if (existing_att_typeid && H5Tclose(existing_att_typeid)) + BAIL2(NC_EHDFERR); + if (existing_attid > 0 && H5Aclose(existing_attid) < 0) + BAIL2(NC_EHDFERR); + if (spaceid > 0 && H5Sclose(spaceid) < 0) + BAIL2(NC_EHDFERR); + if (existing_spaceid > 0 && H5Sclose(existing_spaceid) < 0) + BAIL2(NC_EHDFERR); + return retval; +} + +/** + * @internal Write all the dirty atts in an attlist. + * + * @param attlist Pointer to the list if attributes. + * @param varid Variable ID. + * @param grp Pointer to group info struct. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @author Ed Hartnett + */ +static int +write_attlist(NCindex *attlist, int varid, NC_GRP_INFO_T *grp) +{ + NC_ATT_INFO_T *att; + int retval; + int i; + + for(i = 0; i < ncindexsize(attlist); i++) + { + att = (NC_ATT_INFO_T *)ncindexith(attlist, i); + assert(att); + if (att->dirty) + { + LOG((4, "%s: writing att %s to varid %d", __func__, att->hdr.name, varid)); + if ((retval = put_att_grpa(grp, varid, att))) + return retval; + att->dirty = NC_FALSE; + att->created = NC_TRUE; + } + } + return NC_NOERR; +} + +/** + * @internal HDF5 dimension scales cannot themselves have scales + * attached. This is a problem for multidimensional coordinate + * variables. So this function writes a special attribute for such a + * variable, which has the ids of all the dimensions for that + * coordinate variable. + * + * @param var Pointer to var info struct. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @author Ed Hartnett + */ +static int +write_coord_dimids(NC_VAR_INFO_T *var) +{ + NC_HDF5_VAR_INFO_T *hdf5_var; + hsize_t coords_len[1]; + hid_t c_spaceid = -1, c_attid = -1; + int retval = NC_NOERR; + + assert(var && var->format_var_info); + + /* Get HDF5-specific var info. */ + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + /* Set up space for attribute. */ + coords_len[0] = var->ndims; + if ((c_spaceid = H5Screate_simple(1, coords_len, coords_len)) < 0) + BAIL(NC_EHDFERR); + + /* Create the attribute. */ + if ((c_attid = H5Acreate(hdf5_var->hdf_datasetid, COORDINATES, + H5T_NATIVE_INT, c_spaceid, H5P_DEFAULT)) < 0) + BAIL(NC_EHDFERR); + + /* Write our attribute. */ + if (H5Awrite(c_attid, H5T_NATIVE_INT, var->dimids) < 0) + BAIL(NC_EHDFERR); + +exit: + if (c_spaceid >= 0 && H5Sclose(c_spaceid) < 0) + BAIL2(NC_EHDFERR); + if (c_attid >= 0 && H5Aclose(c_attid) < 0) + BAIL2(NC_EHDFERR); + return retval; +} + +/** + * @internal Write a special attribute for the netCDF-4 dimension ID. + * + * @param datasetid HDF5 datasset ID. + * @param dimid NetCDF dimension ID. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @author Ed Hartnett + */ +static int +write_netcdf4_dimid(hid_t datasetid, int dimid) +{ + hid_t dimid_spaceid = -1, dimid_attid = -1; + htri_t attr_exists; + int retval = NC_NOERR; + + /* Create the space. */ + if ((dimid_spaceid = H5Screate(H5S_SCALAR)) < 0) + BAIL(NC_EHDFERR); + + /* Does the attribute already exist? If so, don't try to create it. */ + if ((attr_exists = H5Aexists(datasetid, NC_DIMID_ATT_NAME)) < 0) + BAIL(NC_EHDFERR); + if (attr_exists) + dimid_attid = H5Aopen_by_name(datasetid, ".", NC_DIMID_ATT_NAME, + H5P_DEFAULT, H5P_DEFAULT); + else + /* Create the attribute if needed. */ + dimid_attid = H5Acreate(datasetid, NC_DIMID_ATT_NAME, + H5T_NATIVE_INT, dimid_spaceid, H5P_DEFAULT); + if (dimid_attid < 0) + BAIL(NC_EHDFERR); + + + /* Write it. */ + LOG((4, "%s: writing secret dimid %d", __func__, dimid)); + if (H5Awrite(dimid_attid, H5T_NATIVE_INT, &dimid) < 0) + BAIL(NC_EHDFERR); + +exit: + /* Close stuff*/ + if (dimid_spaceid >= 0 && H5Sclose(dimid_spaceid) < 0) + BAIL2(NC_EHDFERR); + if (dimid_attid >= 0 && H5Aclose(dimid_attid) < 0) + BAIL2(NC_EHDFERR); + + return retval; +} + +/** + * @internal This function creates the HDF5 dataset for a variable. + * + * @param grp Pointer to group info struct. + * @param var Pointer to variable info struct. + * @param write_dimid True to write dimid. + * + * @return ::NC_NOERR + * @returns NC_ECHAR Conversions of NC_CHAR forbidden. + * @returns NC_EVARMETA HDF5 returning error creating datatype. + * @returns NC_EHDFERR HDF5 returning error. + * @returns NC_EBADTYE Type not found. + * @author Ed Hartnett + */ +static int +var_create_dataset(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, nc_bool_t write_dimid) +{ + NC_HDF5_GRP_INFO_T *hdf5_grp; + NC_HDF5_VAR_INFO_T *hdf5_var; + hid_t plistid = 0, access_plistid = 0, typeid = 0, spaceid = 0; + hsize_t chunksize[H5S_MAX_RANK], dimsize[H5S_MAX_RANK], maxdimsize[H5S_MAX_RANK]; + int d; + void *fillp = NULL; + NC_DIM_INFO_T *dim = NULL; + char *name_to_use; + int retval; + + assert(grp && grp->format_grp_info && var && var->format_var_info); + + LOG((3, "%s:: name %s", __func__, var->hdr.name)); + + /* Get HDF5-specific group and var info. */ + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + /* Scalar or not, we need a creation property list. */ + if ((plistid = H5Pcreate(H5P_DATASET_CREATE)) < 0) + BAIL(NC_EHDFERR); + if ((access_plistid = H5Pcreate(H5P_DATASET_ACCESS)) < 0) + BAIL(NC_EHDFERR); + + /* Turn off object tracking times in HDF5. */ + if (H5Pset_obj_track_times(plistid, 0) < 0) + BAIL(NC_EHDFERR); + + /* Find the HDF5 type of the dataset. */ + if ((retval = nc4_get_hdf_typeid(grp->nc4_info, var->type_info->hdr.id, &typeid, + var->type_info->endianness))) + BAIL(retval); + + /* Figure out what fill value to set, if any. */ + if (var->no_fill) + { + /* Required to truly turn HDF5 fill values off */ + if (H5Pset_fill_time(plistid, H5D_FILL_TIME_NEVER) < 0) + BAIL(NC_EHDFERR); + } + else + { + if ((retval = nc4_get_fill_value(grp->nc4_info, var, &fillp))) + BAIL(retval); + + /* If there is a fill value, set it. */ + if (fillp) + { + if (var->type_info->nc_type_class == NC_STRING) + { + if (H5Pset_fill_value(plistid, typeid, fillp) < 0) + BAIL(NC_EHDFERR); + } + else + { + /* The fill value set in HDF5 must always be presented as + * a native type, even if the endianness for this dataset + * is non-native. HDF5 will translate the fill value to + * the target endiannesss. */ + hid_t fill_typeid = 0; + + if ((retval = nc4_get_hdf_typeid(grp->nc4_info, var->type_info->hdr.id, &fill_typeid, + NC_ENDIAN_NATIVE))) + BAIL(retval); + if (H5Pset_fill_value(plistid, fill_typeid, fillp) < 0) + { + if (H5Tclose(fill_typeid) < 0) + BAIL(NC_EHDFERR); + BAIL(NC_EHDFERR); + } + if (H5Tclose(fill_typeid) < 0) + BAIL(NC_EHDFERR); + } + } + } + + /* If the user wants to shuffle the data, set that up now. */ + if (var->shuffle) { + if (H5Pset_shuffle(plistid) < 0) + BAIL(NC_EHDFERR); + } + + /* If the user wants to deflate the data, set that up now. */ + if (var->deflate) { + if (H5Pset_deflate(plistid, var->deflate_level) < 0) + BAIL(NC_EHDFERR); + } else if(var->filterid) { + /* Handle szip case here */ + if(var->filterid == H5Z_FILTER_SZIP) { + int options_mask; + int bits_per_pixel; + if(var->nparams != 2) + BAIL(NC_EFILTER); + options_mask = (int)var->params[0]; + bits_per_pixel = (int)var->params[1]; + if(H5Pset_szip(plistid, options_mask, bits_per_pixel) < 0) + BAIL(NC_EFILTER); + } else { + herr_t code = H5Pset_filter(plistid, var->filterid, H5Z_FLAG_MANDATORY, var->nparams, var->params); + if(code < 0) { + BAIL(NC_EFILTER); + } + } + } + + /* If the user wants to fletcher error correcton, set that up now. */ + if (var->fletcher32) + if (H5Pset_fletcher32(plistid) < 0) + BAIL(NC_EHDFERR); + + /* If ndims non-zero, get info for all dimensions. We look up the + dimids and get the len of each dimension. We need this to create + the space for the dataset. In netCDF a dimension length of zero + means an unlimited dimension. */ + if (var->ndims) + { + int unlimdim = 0; + + /* Check to see if any unlimited dimensions are used in this var. */ + for (d = 0; d < var->ndims; d++) { + dim = var->dim[d]; + assert(dim && dim->hdr.id == var->dimids[d]); + if (dim->unlimited) + unlimdim++; + } + + /* If there are no unlimited dims, and no filters, and the user + * has not specified chunksizes, use contiguous variable for + * better performance. */ + if (!var->shuffle && !var->deflate && !var->fletcher32 && + (var->chunksizes == NULL || !var->chunksizes[0]) && !unlimdim) + var->contiguous = NC_TRUE; + + /* Gather current & maximum dimension sizes, along with chunk sizes */ + for (d = 0; d < var->ndims; d++) + { + dim = var->dim[d]; + assert(dim && dim->hdr.id == var->dimids[d]); + dimsize[d] = dim->unlimited ? NC_HDF5_UNLIMITED_DIMSIZE : dim->len; + maxdimsize[d] = dim->unlimited ? H5S_UNLIMITED : (hsize_t)dim->len; + if (!var->contiguous) { + if (var->chunksizes[d]) + chunksize[d] = var->chunksizes[d]; + else + { + size_t type_size; + if (var->type_info->nc_type_class == NC_STRING) + type_size = sizeof(char *); + else + type_size = var->type_info->size; + + /* Unlimited dim always gets chunksize of 1. */ + if (dim->unlimited) + chunksize[d] = 1; + else + chunksize[d] = pow((double)DEFAULT_CHUNK_SIZE/type_size, + 1/(double)(var->ndims - unlimdim)); + + /* If the chunksize is greater than the dim + * length, make it the dim length. */ + if (!dim->unlimited && chunksize[d] > dim->len) + chunksize[d] = dim->len; + + /* Remember the computed chunksize */ + var->chunksizes[d] = chunksize[d]; + } + } + } + + if (var->contiguous) + { + if (H5Pset_layout(plistid, H5D_CONTIGUOUS) < 0) + BAIL(NC_EHDFERR); + } + else + { + if (H5Pset_chunk(plistid, var->ndims, chunksize) < 0) + BAIL(NC_EHDFERR); + } + + /* Create the dataspace. */ + if ((spaceid = H5Screate_simple(var->ndims, dimsize, maxdimsize)) < 0) + BAIL(NC_EHDFERR); + } + else + { + if ((spaceid = H5Screate(H5S_SCALAR)) < 0) + BAIL(NC_EHDFERR); + } + + /* Turn on creation order tracking. */ + if (H5Pset_attr_creation_order(plistid, H5P_CRT_ORDER_TRACKED| + H5P_CRT_ORDER_INDEXED) < 0) + BAIL(NC_EHDFERR); + + /* Set per-var chunk cache, for chunked datasets. */ + if (!var->contiguous && var->chunk_cache_size) + if (H5Pset_chunk_cache(access_plistid, var->chunk_cache_nelems, + var->chunk_cache_size, var->chunk_cache_preemption) < 0) + BAIL(NC_EHDFERR); + + /* At long last, create the dataset. */ + name_to_use = var->hdf5_name ? var->hdf5_name : var->hdr.name; + LOG((4, "%s: about to H5Dcreate2 dataset %s of type 0x%x", __func__, + name_to_use, typeid)); + if ((hdf5_var->hdf_datasetid = H5Dcreate2(hdf5_grp->hdf_grpid, name_to_use, typeid, + spaceid, H5P_DEFAULT, plistid, access_plistid)) < 0) + BAIL(NC_EHDFERR); + var->created = NC_TRUE; + var->is_new_var = NC_FALSE; + + /* Always write the hidden coordinates attribute, which lists the + * dimids of this var. When present, this speeds opens. When no + * present, dimscale matching is used. */ + if (var->ndims > 1) + if ((retval = write_coord_dimids(var))) + BAIL(retval); + + /* If this is a dimscale, mark it as such in the HDF5 file. Also + * find the dimension info and store the dataset id of the dimscale + * dataset. */ + if (var->dimscale) + { + if (H5DSset_scale(hdf5_var->hdf_datasetid, var->hdr.name) < 0) + BAIL(NC_EHDFERR); + + /* If this is a multidimensional coordinate variable, write a + * coordinates attribute. */ + /* if (var->ndims > 1) */ + /* if ((retval = write_coord_dimids(var))) */ + /* BAIL(retval); */ + + /* If desired, write the netCDF dimid. */ + if (write_dimid) + if ((retval = write_netcdf4_dimid(hdf5_var->hdf_datasetid, var->dimids[0]))) + BAIL(retval); + } + + /* Write attributes for this var. */ + if ((retval = write_attlist(var->att, var->hdr.id, grp))) + BAIL(retval); + var->attr_dirty = NC_FALSE; + +exit: + if (typeid > 0 && H5Tclose(typeid) < 0) + BAIL2(NC_EHDFERR); + if (plistid > 0 && H5Pclose(plistid) < 0) + BAIL2(NC_EHDFERR); + if (access_plistid > 0 && H5Pclose(access_plistid) < 0) + BAIL2(NC_EHDFERR); + if (spaceid > 0 && H5Sclose(spaceid) < 0) + BAIL2(NC_EHDFERR); + if (fillp) + { + if (var->type_info->nc_type_class == NC_VLEN) + nc_free_vlen((nc_vlen_t *)fillp); + else if (var->type_info->nc_type_class == NC_STRING && *(char **)fillp) + free(*(char **)fillp); + free(fillp); + } + + return retval; +} + +/** + * @internal Adjust the chunk cache of a var for better + * performance. + * + * @param grp Pointer to group info struct. + * @param var Pointer to var info struct. + * + * @return NC_NOERR No error. + * @author Ed Hartnett + */ +int +nc4_adjust_var_cache(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var) +{ + size_t chunk_size_bytes = 1; + int d; + int retval; + + /* Nothing to be done. */ + if (var->contiguous) + return NC_NOERR; +#ifdef USE_PARALLEL4 + return NC_NOERR; +#endif + + /* How many bytes in the chunk? */ + for (d = 0; d < var->ndims; d++) + chunk_size_bytes *= var->chunksizes[d]; + if (var->type_info->size) + chunk_size_bytes *= var->type_info->size; + else + chunk_size_bytes *= sizeof(char *); + + /* If the chunk cache is too small, and the user has not changed + * the default value of the chunk cache size, then increase the + * size of the cache. */ + if (var->chunk_cache_size == CHUNK_CACHE_SIZE) + if (chunk_size_bytes > var->chunk_cache_size) + { + var->chunk_cache_size = chunk_size_bytes * DEFAULT_CHUNKS_IN_CACHE; + if (var->chunk_cache_size > MAX_DEFAULT_CACHE_SIZE) + var->chunk_cache_size = MAX_DEFAULT_CACHE_SIZE; + if ((retval = nc4_reopen_dataset(grp, var))) + return retval; + } + + return NC_NOERR; +} + +/** + * @internal Create a HDF5 defined type from a NC_TYPE_INFO_T struct, + * and commit it to the file. + * + * @param grp Pointer to group info struct. + * @param type Pointer to type info struct. + * + * @return NC_NOERR No error. + * @return NC_EHDFERR HDF5 error. + * @return NC_ECHAR Conversions of NC_CHAR forbidden. + * @return NC_EVARMETA HDF5 returning error creating datatype. + * @return NC_EHDFERR HDF5 returning error. + * @return NC_EBADTYE Type not found. + * @author Ed Hartnett + */ +static int +commit_type(NC_GRP_INFO_T *grp, NC_TYPE_INFO_T *type) +{ + NC_HDF5_GRP_INFO_T *hdf5_grp; + NC_HDF5_TYPE_INFO_T *hdf5_type; + hid_t base_hdf_typeid; + int retval; + + assert(grp && grp->format_grp_info && type && type->format_type_info); + + /* Get HDF5-specific group and type info. */ + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + hdf5_type = (NC_HDF5_TYPE_INFO_T *)type->format_type_info; + + /* Did we already record this type? */ + if (type->committed) + return NC_NOERR; + + /* Is this a compound type? */ + if (type->nc_type_class == NC_COMPOUND) + { + NC_FIELD_INFO_T *field; + hid_t hdf_base_typeid, hdf_typeid; + int i; + + if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_COMPOUND, type->size)) < 0) + return NC_EHDFERR; + LOG((4, "creating compound type %s hdf_typeid 0x%x", type->hdr.name, + hdf5_type->hdf_typeid)); + + for(i=0;i<nclistlength(type->u.c.field);i++) + { + field = (NC_FIELD_INFO_T *)nclistget(type->u.c.field, i); + assert(field); + if ((retval = nc4_get_hdf_typeid(grp->nc4_info, field->nc_typeid, + &hdf_base_typeid, type->endianness))) + return retval; + + /* If this is an array, create a special array type. */ + if (field->ndims) + { + int d; + hsize_t dims[NC_MAX_VAR_DIMS]; + + for (d = 0; d < field->ndims; d++) + dims[d] = field->dim_size[d]; + if ((hdf_typeid = H5Tarray_create(hdf_base_typeid, field->ndims, + dims, NULL)) < 0) + { + if (H5Tclose(hdf_base_typeid) < 0) + return NC_EHDFERR; + return NC_EHDFERR; + } + if (H5Tclose(hdf_base_typeid) < 0) + return NC_EHDFERR; + } + else + hdf_typeid = hdf_base_typeid; + LOG((4, "inserting field %s offset %d hdf_typeid 0x%x", field->hdr.name, + field->offset, hdf_typeid)); + if (H5Tinsert(hdf5_type->hdf_typeid, field->hdr.name, field->offset, + hdf_typeid) < 0) + return NC_EHDFERR; + if (H5Tclose(hdf_typeid) < 0) + return NC_EHDFERR; + } + } + else if (type->nc_type_class == NC_VLEN) + { + /* Find the HDF typeid of the base type of this vlen. */ + if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.v.base_nc_typeid, + &base_hdf_typeid, type->endianness))) + return retval; + + /* Create a vlen type. */ + if ((hdf5_type->hdf_typeid = H5Tvlen_create(base_hdf_typeid)) < 0) + return NC_EHDFERR; + } + else if (type->nc_type_class == NC_OPAQUE) + { + /* Create the opaque type. */ + if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_OPAQUE, type->size)) < 0) + return NC_EHDFERR; + } + else if (type->nc_type_class == NC_ENUM) + { + NC_ENUM_MEMBER_INFO_T *enum_m; + int i; + + if (nclistlength(type->u.e.enum_member) == 0) + return NC_EINVAL; + + /* Find the HDF typeid of the base type of this enum. */ + if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.e.base_nc_typeid, + &base_hdf_typeid, type->endianness))) + return retval; + + /* Create an enum type. */ + if ((hdf5_type->hdf_typeid = H5Tenum_create(base_hdf_typeid)) < 0) + return NC_EHDFERR; + + /* Add all the members to the HDF5 type. */ + for(i=0;i<nclistlength(type->u.e.enum_member);i++) { + enum_m = (NC_ENUM_MEMBER_INFO_T*)nclistget(type->u.e.enum_member,i); + if (H5Tenum_insert(hdf5_type->hdf_typeid, enum_m->name, enum_m->value) < 0) + return NC_EHDFERR; + } + } + else + { + LOG((0, "Unknown class: %d", type->nc_type_class)); + return NC_EBADTYPE; + } + + /* Commit the type. */ + if (H5Tcommit(hdf5_grp->hdf_grpid, type->hdr.name, hdf5_type->hdf_typeid) < 0) + return NC_EHDFERR; + type->committed = NC_TRUE; + LOG((4, "just committed type %s, HDF typeid: 0x%x", type->hdr.name, + hdf5_type->hdf_typeid)); + + /* Later we will always use the native typeid. In this case, it is + * a copy of the same type pointed to by hdf_typeid, but it's + * easier to maintain a copy. */ + if ((hdf5_type->native_hdf_typeid = H5Tget_native_type(hdf5_type->hdf_typeid, + H5T_DIR_DEFAULT)) < 0) + return NC_EHDFERR; + + return NC_NOERR; +} + +/** + * @internal Write an attribute, with value 1, to indicate that strict + * NC3 rules apply to this file. + * + * @param hdf_grpid HDF5 group ID. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @author Ed Hartnett + */ +static int +write_nc3_strict_att(hid_t hdf_grpid) +{ + hid_t attid = 0, spaceid = 0; + int one = 1; + int retval = NC_NOERR; + htri_t attr_exists; + + /* If the attribute already exists, call that a success and return + * NC_NOERR. */ + if ((attr_exists = H5Aexists(hdf_grpid, NC3_STRICT_ATT_NAME)) < 0) + return NC_EHDFERR; + if (attr_exists) + return NC_NOERR; + + /* Create the attribute to mark this as a file that needs to obey + * strict netcdf-3 rules. */ + if ((spaceid = H5Screate(H5S_SCALAR)) < 0) + BAIL(NC_EFILEMETA); + if ((attid = H5Acreate(hdf_grpid, NC3_STRICT_ATT_NAME, + H5T_NATIVE_INT, spaceid, H5P_DEFAULT)) < 0) + BAIL(NC_EFILEMETA); + if (H5Awrite(attid, H5T_NATIVE_INT, &one) < 0) + BAIL(NC_EFILEMETA); + +exit: + if (spaceid > 0 && (H5Sclose(spaceid) < 0)) + BAIL2(NC_EFILEMETA); + if (attid > 0 && (H5Aclose(attid) < 0)) + BAIL2(NC_EFILEMETA); + return retval; +} + +/** + * @internal Create a HDF5 group that is not the root group. HDF5 + * properties are set in the group to ensure that objects and + * attributes are kept in creation order, instead of alphebetical + * order (the HDF5 default). + * + * @param grp Pointer to group info struct. + * + * @return NC_NOERR No error. + * @return NC_EHDFERR HDF5 error. + * @author Ed Hartnett + */ +static int +create_group(NC_GRP_INFO_T *grp) +{ + NC_HDF5_GRP_INFO_T *hdf5_grp, *parent_hdf5_grp; + hid_t gcpl_id = -1; + int retval = NC_NOERR;; + + assert(grp && grp->format_grp_info && grp->parent && + grp->parent->format_grp_info); + + /* Get HDF5 specific group info for group and parent. */ + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + parent_hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->parent->format_grp_info; + assert(parent_hdf5_grp->hdf_grpid); + + /* Create group, with link_creation_order set in the group + * creation property list. */ + if ((gcpl_id = H5Pcreate(H5P_GROUP_CREATE)) < 0) + BAIL(NC_EHDFERR); + + /* Set track_times to be FALSE. */ + if (H5Pset_obj_track_times(gcpl_id, 0) < 0) + BAIL(NC_EHDFERR); + + /* Tell HDF5 to keep track of objects in creation order. */ + if (H5Pset_link_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0) + BAIL(NC_EHDFERR); + + /* Tell HDF5 to keep track of attributes in creation order. */ + if (H5Pset_attr_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0) + BAIL(NC_EHDFERR); + + /* Create the group. */ + if ((hdf5_grp->hdf_grpid = H5Gcreate2(parent_hdf5_grp->hdf_grpid, grp->hdr.name, + H5P_DEFAULT, gcpl_id, H5P_DEFAULT)) < 0) + BAIL(NC_EHDFERR); + +exit: + if (gcpl_id > -1 && H5Pclose(gcpl_id) < 0) + BAIL2(NC_EHDFERR); + if (retval) + if (hdf5_grp->hdf_grpid > 0 && H5Gclose(hdf5_grp->hdf_grpid) < 0) + BAIL2(NC_EHDFERR); + return retval; +} + +/** + * @internal After all the datasets of the file have been read, it's + * time to sort the wheat from the chaff. Which of the datasets are + * netCDF dimensions, and which are coordinate variables, and which + * are non-coordinate variables. + * + * @param grp Pointer to group info struct. + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +static int +attach_dimscales(NC_GRP_INFO_T *grp) +{ + NC_VAR_INFO_T *var; + NC_HDF5_VAR_INFO_T *hdf5_var; + int d, v; + + /* Attach dimension scales. */ + for (v = 0; v < ncindexsize(grp->vars); v++) + { + /* Get pointer to var and HDF5-specific var info. */ + var = (NC_VAR_INFO_T *)ncindexith(grp->vars, v); + assert(var && var->format_var_info); + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + /* Scales themselves do not attach. But I really wish they + * would. */ + if (var->dimscale) + continue; + + /* Find the scale for each dimension, if any, and attach it. */ + for (d = 0; d < var->ndims; d++) + { + /* Is there a dimscale for this dimension? */ + if (var->dimscale_attached) + { + if (!var->dimscale_attached[d]) + { + hid_t dsid; /* Dataset ID for dimension */ + assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] && + var->dim[d]->format_dim_info); + + LOG((2, "%s: attaching scale for dimid %d to var %s", + __func__, var->dimids[d], var->hdr.name)); + + /* Find dataset ID for dimension */ + if (var->dim[d]->coord_var) + dsid = ((NC_HDF5_VAR_INFO_T *)(var->dim[d]->coord_var->format_var_info))->hdf_datasetid; + else + dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid; + assert(dsid > 0); + + /* Attach the scale. */ + if (H5DSattach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0) + return NC_EHDFERR; + var->dimscale_attached[d] = NC_TRUE; + } + } + } + } + + return NC_NOERR; +} + +/** + * @internal Does a variable exist? + * + * @param grpid HDF5 group ID. + * @param name Name of variable. + * @param exists Pointer that gets 1 of the variable exists, 0 otherwise. + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +static int +var_exists(hid_t grpid, char *name, nc_bool_t *exists) +{ + htri_t link_exists; + + /* Reset the boolean */ + *exists = NC_FALSE; + + /* Check if the object name exists in the group */ + if ((link_exists = H5Lexists(grpid, name, H5P_DEFAULT)) < 0) + return NC_EHDFERR; + if (link_exists) + { + H5G_stat_t statbuf; + + /* Get info about the object */ + if (H5Gget_objinfo(grpid, name, 1, &statbuf) < 0) + return NC_EHDFERR; + + if (H5G_DATASET == statbuf.type) + *exists = NC_TRUE; + } + + return NC_NOERR; +} + +/** + * @internal Convert a coordinate variable HDF5 dataset into one that + * is not a coordinate variable. This happens during renaming of vars + * and dims. This function removes the HDF5 NAME and CLASS attributes + * associated with dimension scales, and also the NC_DIMID_ATT_NAME + * attribute which may be present, and, if it does, holds the dimid of + * the coordinate variable. + * + * @param hdf_datasetid The HDF5 dataset ID of the coordinate variable + * dataset. + * + * @return ::NC_NOERR No error. + * @return ::NC_EHDFERR HDF5 error. + * @author Ed Hartnett + */ +static int +remove_coord_atts(hid_t hdf_datasetid) +{ + htri_t attr_exists; + + /* If the variable dataset has an optional NC_DIMID_ATT_NAME + * attribute, delete it. */ + if ((attr_exists = H5Aexists(hdf_datasetid, NC_DIMID_ATT_NAME)) < 0) + return NC_EHDFERR; + if (attr_exists) + { + if (H5Adelete(hdf_datasetid, NC_DIMID_ATT_NAME) < 0) + return NC_EHDFERR; + } + + /* Remove the dimension scale 'CLASS' & 'NAME' attributes. */ + if ((attr_exists = H5Aexists(hdf_datasetid, + HDF5_DIMSCALE_CLASS_ATT_NAME)) < 0) + return NC_EHDFERR; + if (attr_exists) + { + if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_CLASS_ATT_NAME) < 0) + return NC_EHDFERR; + } + if ((attr_exists = H5Aexists(hdf_datasetid, + HDF5_DIMSCALE_NAME_ATT_NAME)) < 0) + return NC_EHDFERR; + if (attr_exists) + { + if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_NAME_ATT_NAME) < 0) + return NC_EHDFERR; + } + return NC_NOERR; +} + +/** + * @internal This function writes a variable. The principle difficulty + * comes from the possibility that this is a coordinate variable, and + * was already written to the file as a dimension-only dimscale. If + * this occurs, then it must be deleted and recreated. + * + * @param var Pointer to variable info struct. + * @param grp Pointer to group info struct. + * @param write_dimid + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @author Ed Hartnett, Quincey Koziol + */ +static int +write_var(NC_VAR_INFO_T *var, NC_GRP_INFO_T *grp, nc_bool_t write_dimid) +{ + NC_HDF5_GRP_INFO_T *hdf5_grp; + NC_HDF5_VAR_INFO_T *hdf5_var; + nc_bool_t replace_existing_var = NC_FALSE; + int retval; + + assert(var && var->format_var_info && grp && grp->format_grp_info); + + LOG((4, "%s: writing var %s", __func__, var->hdr.name)); + + /* Get HDF5-specific group and var info. */ + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + /* If the variable has already been created & the fill value changed, + * indicate that the existing variable should be replaced. */ + if (var->created && var->fill_val_changed) + { + replace_existing_var = NC_TRUE; + var->fill_val_changed = NC_FALSE; + /* If the variable is going to be replaced, we need to flag any + other attributes associated with the variable as 'dirty', or + else *only* the fill value attribute will be copied over and + the rest will be lost. See + https://github.com/Unidata/netcdf-c/issues/239 */ + flag_atts_dirty(var->att); + } + + /* Is this a coordinate var that has already been created in + * the HDF5 file as a dimscale dataset? Check for dims with the + * same name in this group. If there is one, check to see if + * this object exists in the HDF group. */ + if (var->became_coord_var) + { + if ((NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name)) + { + nc_bool_t exists; + + if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists))) + return retval; + if (exists) + { + /* Indicate that the variable already exists, and should + * be replaced. */ + replace_existing_var = NC_TRUE; + flag_atts_dirty(var->att); + } + } + } + + /* Check dims if the variable will be replaced, so that the + * dimensions will be de-attached and re-attached correctly. */ + if (replace_existing_var) + { + NC_DIM_INFO_T *d1; + + /* Is there a dim with this var's name? */ + if ((d1 = (NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name))) + { + nc_bool_t exists; + assert(d1->format_dim_info && d1->hdr.name); + + if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists))) + return retval; + if (exists) + { + hid_t dsid; + + /* Find dataset ID for dimension */ + if (d1->coord_var) + dsid = ((NC_HDF5_VAR_INFO_T *)d1->coord_var->format_var_info)->hdf_datasetid; + else + dsid = ((NC_HDF5_DIM_INFO_T *)d1->format_dim_info)->hdf_dimscaleid; + assert(dsid > 0); + + /* If we're replacing an existing dimscale dataset, go to + * every var in the file and detach this dimension scale, + * because we have to delete it. */ + if ((retval = rec_detach_scales(grp->nc4_info->root_grp, + var->dimids[0], dsid))) + return retval; + } + } + } + + /* If this is not a dimension scale, remove any attached scales, + * and delete dimscale attributes from the var. */ + if (var->was_coord_var && var->dimscale_attached) + { + int d; + + /* If the variable already exists in the file, Remove any dimension scale + * attributes from it, if they exist. */ + if (var->created) + if ((retval = remove_coord_atts(hdf5_var->hdf_datasetid))) + return retval; + + /* If this is a regular var, detach all its dim scales. */ + for (d = 0; d < var->ndims; d++) + { + if (var->dimscale_attached[d]) + { + hid_t dsid; /* Dataset ID for dimension */ + assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] && + var->dim[d]->format_dim_info); + + /* Find dataset ID for dimension */ + if (var->dim[d]->coord_var) + dsid = ((NC_HDF5_VAR_INFO_T *)var->dim[d]->coord_var->format_var_info)->hdf_datasetid; + else + dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid; + assert(dsid > 0); + + /* Detach this dim scale. */ + if (H5DSdetach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0) + return NC_EHDFERR; + var->dimscale_attached[d] = NC_FALSE; + } + } + } + + /* Delete the HDF5 dataset that is to be replaced. */ + if (replace_existing_var) + { + /* Free the HDF5 dataset id. */ + if (hdf5_var->hdf_datasetid && H5Dclose(hdf5_var->hdf_datasetid) < 0) + return NC_EHDFERR; + hdf5_var->hdf_datasetid = 0; + + /* Now delete the variable. */ + if (H5Gunlink(hdf5_grp->hdf_grpid, var->hdr.name) < 0) + return NC_EDIMMETA; + } + + /* Create the dataset. */ + if (var->is_new_var || replace_existing_var) + { + if ((retval = var_create_dataset(grp, var, write_dimid))) + return retval; + } + else + { + if (write_dimid && var->ndims) + if ((retval = write_netcdf4_dimid(hdf5_var->hdf_datasetid, + var->dimids[0]))) + return retval; + } + + if (replace_existing_var) + { + /* If this is a dimension scale, reattach the scale everywhere it + * is used. (Recall that netCDF dimscales are always 1-D). */ + if(var->dimscale) + { + if ((retval = rec_reattach_scales(grp->nc4_info->root_grp, + var->dimids[0], hdf5_var->hdf_datasetid))) + return retval; + } + /* If it's not a dimension scale, clear the dimscale attached flags, + * so the dimensions are re-attached. */ + else + { + if (var->dimscale_attached) + memset(var->dimscale_attached, 0, sizeof(nc_bool_t) * var->ndims); + } + } + + /* Clear coord. var state transition flags */ + var->was_coord_var = NC_FALSE; + var->became_coord_var = NC_FALSE; + + /* Now check the attributes for this var. */ + if (var->attr_dirty) + { + /* Write attributes for this var. */ + if ((retval = write_attlist(var->att, var->hdr.id, grp))) + return retval; + var->attr_dirty = NC_FALSE; + } + + return NC_NOERR; +} + +/** + * @internal Write a HDF5 dataset which is a dimension without a + * coordinate variable. This is a special 1-D dataset. + * + * @param dim Pointer to dim info struct. + * @param grp Pointer to group info struct. + * @param write_dimid + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EPERM Read-only file. + * @returns ::NC_EHDFERR HDF5 returned error. + * @author Ed Hartnett + */ +int +nc4_create_dim_wo_var(NC_DIM_INFO_T *dim) +{ + NC_HDF5_DIM_INFO_T *hdf5_dim; + NC_HDF5_GRP_INFO_T *hdf5_grp; + hid_t spaceid = -1, create_propid = -1; + hsize_t dims[1], max_dims[1], chunk_dims[1] = {1}; + char dimscale_wo_var[NC_MAX_NAME]; + int retval = NC_NOERR; + + LOG((4, "%s: creating dim %s", __func__, dim->hdr.name)); + + /* Sanity check */ + assert(!dim->coord_var); + + /* Get HDF5-specific dim and group info. */ + hdf5_grp = (NC_HDF5_GRP_INFO_T *)dim->container->format_grp_info; + hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; + + /* Create a property list. */ + if ((create_propid = H5Pcreate(H5P_DATASET_CREATE)) < 0) + BAIL(NC_EHDFERR); + + /* Turn off recording of times associated with this object. */ + if (H5Pset_obj_track_times(create_propid, 0) < 0) + BAIL(NC_EHDFERR); + + /* Set size of dataset to size of dimension. */ + dims[0] = dim->len; + max_dims[0] = dim->len; + + /* If this dimension scale is unlimited (i.e. it's an unlimited + * dimension), then set up chunking, with a chunksize of 1. */ + if (dim->unlimited) + { + max_dims[0] = H5S_UNLIMITED; + if (H5Pset_chunk(create_propid, 1, chunk_dims) < 0) + BAIL(NC_EHDFERR); + } + + /* Set up space. */ + if ((spaceid = H5Screate_simple(1, dims, max_dims)) < 0) + BAIL(NC_EHDFERR); + + /* Turn on creation-order tracking. */ + if (H5Pset_attr_creation_order(create_propid, H5P_CRT_ORDER_TRACKED| + H5P_CRT_ORDER_INDEXED) < 0) + BAIL(NC_EHDFERR); + + /* Create the dataset that will be the dimension scale. */ + LOG((4, "%s: about to H5Dcreate1 a dimscale dataset %s", __func__, + dim->hdr.name)); + if ((hdf5_dim->hdf_dimscaleid = H5Dcreate2(hdf5_grp->hdf_grpid, dim->hdr.name, + H5T_IEEE_F32BE, spaceid, + H5P_DEFAULT, create_propid, + H5P_DEFAULT)) < 0) + BAIL(NC_EHDFERR); + + /* Indicate that this is a scale. Also indicate that not + * be shown to the user as a variable. It is hidden. It is + * a DIM WITHOUT A VARIABLE! */ + sprintf(dimscale_wo_var, "%s%10d", DIM_WITHOUT_VARIABLE, (int)dim->len); + if (H5DSset_scale(hdf5_dim->hdf_dimscaleid, dimscale_wo_var) < 0) + BAIL(NC_EHDFERR); + + /* Since this dimension was created out of order, we cannot rely on + * it getting the correct dimid on file open. We must assign it + * explicitly. */ + if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id))) + BAIL(retval); + +exit: + if (spaceid > 0 && H5Sclose(spaceid) < 0) + BAIL2(NC_EHDFERR); + if (create_propid > 0 && H5Pclose(create_propid) < 0) + BAIL2(NC_EHDFERR); + return retval; +} + +/** + * @internal Write a dimension. + * + * @param dim Pointer to dim info struct. + * @param grp Pointer to group info struct. + * @param write_dimid + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EPERM Read-only file. + * @returns ::NC_EHDFERR HDF5 returned error. + * @author Ed Hartnett + */ +static int +write_dim(NC_DIM_INFO_T *dim, NC_GRP_INFO_T *grp, nc_bool_t write_dimid) +{ + NC_HDF5_DIM_INFO_T *hdf5_dim; + int retval = NC_NOERR; + + assert(dim && dim->format_dim_info && grp && grp->format_grp_info); + + /* Get HDF5-specific dim and group info. */ + hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; + + /* If there's no dimscale dataset for this dim, create one, + * and mark that it should be hidden from netCDF as a + * variable. (That is, it should appear as a dimension + * without an associated variable.) */ + if (!hdf5_dim->hdf_dimscaleid) + if ((retval = nc4_create_dim_wo_var(dim))) + BAIL(retval); + + /* Did we extend an unlimited dimension? */ + if (dim->extended) + { + NC_VAR_INFO_T *v1 = NULL; + + assert(dim->unlimited); + /* If this is a dimension without a variable, then update + * the secret length information at the end of the NAME + * attribute. */ + v1 = (NC_VAR_INFO_T *)ncindexlookup(grp->vars, dim->hdr.name); + if (v1) + { + NC_HDF5_VAR_INFO_T *hdf5_v1; + hsize_t *new_size; + int d1; + + hdf5_v1 = (NC_HDF5_VAR_INFO_T *)v1->format_var_info; + + /* Extend the dimension scale dataset to reflect the new + * length of the dimension. */ + if (!(new_size = malloc(v1->ndims * sizeof(hsize_t)))) + BAIL(NC_ENOMEM); + for (d1 = 0; d1 < v1->ndims; d1++) + { + assert(v1->dim[d1] && v1->dim[d1]->hdr.id == v1->dimids[d1]); + new_size[d1] = v1->dim[d1]->len; + } + if (H5Dset_extent(hdf5_v1->hdf_datasetid, new_size) < 0) + BAIL(NC_EHDFERR); + free(new_size); + } + } + + /* If desired, write the secret dimid. This will be used instead of + * the dimid that the dimension would otherwise receive based on + * creation order. This can be necessary when dims and their + * coordinate variables were created in different order. */ + if (write_dimid && hdf5_dim->hdf_dimscaleid) + if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id))) + BAIL(retval); + +exit: + + return retval; +} + +/** + * @internal Recursively write all the metadata in a group. Groups and + * types have all already been written. Propagate bad cooordinate + * order to subgroups, if detected. + * + * @param grp Pointer to group info struct. + * @param bad_coord_order 1 if there is a bad coordinate order. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @author Ed Hartnett + */ +int +nc4_rec_write_metadata(NC_GRP_INFO_T *grp, nc_bool_t bad_coord_order) +{ + NC_DIM_INFO_T *dim = NULL; + NC_VAR_INFO_T *var = NULL; + NC_GRP_INFO_T *child_grp = NULL; + int coord_varid = -1; + int var_index = 0; + int dim_index = 0; + int retval; + int i; + + assert(grp && grp->hdr.name && + ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid); + LOG((3, "%s: grp->hdr.name %s, bad_coord_order %d", __func__, grp->hdr.name, + bad_coord_order)); + + /* Write global attributes for this group. */ + if ((retval = write_attlist(grp->att, NC_GLOBAL, grp))) + return retval; + + /* Set the pointers to the beginning of the list of dims & vars in this + * group. */ + dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, dim_index); + var = (NC_VAR_INFO_T *)ncindexith(grp->vars, var_index); + + /* Because of HDF5 ordering the dims and vars have to be stored in + * this way to ensure that the dims and coordinate vars come out in + * the correct order. */ + while (dim || var) + { + nc_bool_t found_coord, wrote_coord; + + /* Write non-coord dims in order, stopping at the first one that + * has an associated coord var. */ + for (found_coord = NC_FALSE; dim && !found_coord; ) + { + if (!dim->coord_var) + { + if ((retval = write_dim(dim, grp, bad_coord_order))) + return retval; + } + else + { + coord_varid = dim->coord_var->hdr.id; + found_coord = NC_TRUE; + } + dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, ++dim_index); + } + + /* Write each var. When we get to the coord var we are waiting + * for (if any), then we break after writing it. */ + for (wrote_coord = NC_FALSE; var && !wrote_coord; ) + { + if ((retval = write_var(var, grp, bad_coord_order))) + return retval; + if (found_coord && var->hdr.id == coord_varid) + wrote_coord = NC_TRUE; + var = (NC_VAR_INFO_T *)ncindexith(grp->vars, ++var_index); + } + } /* end while */ + + /* Attach dimscales to vars in this group. */ + if ((retval = attach_dimscales(grp))) + return retval; + + /* If there are any child groups, write their metadata. */ + for (i = 0; i < ncindexsize(grp->children); i++) + { + child_grp = (NC_GRP_INFO_T *)ncindexith(grp->children, i); + assert(child_grp); + if ((retval = nc4_rec_write_metadata(child_grp, bad_coord_order))) + return retval; + } + return NC_NOERR; +} + +/** + * @internal Recursively write all groups and types. + * + * @param grp Pointer to group info struct. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @author Ed Hartnett + */ +int +nc4_rec_write_groups_types(NC_GRP_INFO_T *grp) +{ + NC_GRP_INFO_T *child_grp; + NC_HDF5_GRP_INFO_T *hdf5_grp; + NC_TYPE_INFO_T *type; + int retval; + int i; + + assert(grp && grp->hdr.name && grp->format_grp_info); + LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name)); + + /* Get HDF5-specific group info. */ + hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; + + /* Create the group in the HDF5 file if it doesn't exist. */ + if (!hdf5_grp->hdf_grpid) + if ((retval = create_group(grp))) + return retval; + + /* If this is the root group of a file with strict NC3 rules, write + * an attribute. But don't leave the attribute open. */ + if (!grp->parent && (grp->nc4_info->cmode & NC_CLASSIC_MODEL)) + if ((retval = write_nc3_strict_att(hdf5_grp->hdf_grpid))) + return retval; + + /* If there are any user-defined types, write them now. */ + for(i=0;i<ncindexsize(grp->type);i++) { + type = (NC_TYPE_INFO_T *)ncindexith(grp->type, i); + assert(type); + if ((retval = commit_type(grp, type))) + return retval; + } + + /* If there are any child groups, write their groups and types. */ + for(i=0;i<ncindexsize(grp->children);i++) { + if((child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children,i)) == NULL) continue; + if ((retval = nc4_rec_write_groups_types(child_grp))) + return retval; + } + return NC_NOERR; +} + +/** + * @internal In our first pass through the data, we may have + * encountered variables before encountering their dimscales, so go + * through the vars in this file and make sure we've got a dimid for + * each. + * + * @param grp Pointer to group info struct. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned an error. + * @returns NC_ENOMEM Out of memory. + * @author Ed Hartnett + */ +int +nc4_rec_match_dimscales(NC_GRP_INFO_T *grp) +{ + NC_GRP_INFO_T *g; + NC_VAR_INFO_T *var; + NC_DIM_INFO_T *dim; + int retval = NC_NOERR; + int i; + + assert(grp && grp->hdr.name); + LOG((4, "%s: grp->hdr.name %s", __func__, grp->hdr.name)); + + /* Perform var dimscale match for child groups. */ + for (i = 0; i < ncindexsize(grp->children); i++) + { + g = (NC_GRP_INFO_T*)ncindexith(grp->children, i); + assert(g); + if ((retval = nc4_rec_match_dimscales(g))) + return retval; + } + + /* Check all the vars in this group. If they have dimscale info, + * try and find a dimension for them. */ + for (i = 0; i < ncindexsize(grp->vars); i++) + { + NC_HDF5_VAR_INFO_T *hdf5_var; + int ndims; + int d; + + /* Get pointer to var and to the HDF5-specific var info. */ + var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i); + assert(var && var->format_var_info); + hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; + + /* Check all vars and see if dim[i] != NULL if dimids[i] valid. */ + /* This loop is very odd. Under normal circumstances, var->dimid[d] is zero + (from the initial calloc) which is a legitimate dimid. The code does not + distinquish this case from the dimscale case where the id might actually + be defined. + The original nc4_find_dim searched up the group tree looking for the given + dimid in one of the dim lists associated with each ancestor group. + I changed nc4_fnd_dim to use the dimid directly using h5->alldims. + However, here that is incorrect because it will find the dimid 0 always + (if any dimensions were defined). Except that when dimscale dimids have + been defined, one or more of the values in var->dimids will have a + legitimate value. + The solution I choose is to modify nc4_var_list_add to initialize dimids to + illegal values (-1). This is another example of the problems with dimscales. + */ + ndims = var->ndims; + for (d = 0; d < ndims; d++) + { + if (var->dim[d] == NULL) { + nc4_find_dim(grp, var->dimids[d], &var->dim[d], NULL); + } + /* assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d]); */ + } + + /* Skip dimension scale variables */ + if (!var->dimscale) + { + int d; + int j; + + /* Are there dimscales for this variable? */ + if (hdf5_var->dimscale_hdf5_objids) + { + for (d = 0; d < var->ndims; d++) + { + nc_bool_t finished = NC_FALSE; + LOG((5, "%s: var %s has dimscale info...", __func__, var->hdr.name)); + + /* Check this and parent groups. */ + for (g = grp; g && !finished; g = g->parent) + { + /* Check all dims in this group. */ + for (j = 0; j < ncindexsize(g->dim); j++) + { + /* Get the HDF5 specific dim info. */ + NC_HDF5_DIM_INFO_T *hdf5_dim; + dim = (NC_DIM_INFO_T *)ncindexith(g->dim, j); + assert(dim && dim->format_dim_info); + hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; + + /* Check for exact match of fileno/objid arrays + * to find identical objects in HDF5 file. */ + if (hdf5_var->dimscale_hdf5_objids[d].fileno[0] == hdf5_dim->hdf5_objid.fileno[0] && + hdf5_var->dimscale_hdf5_objids[d].objno[0] == hdf5_dim->hdf5_objid.objno[0] && + hdf5_var->dimscale_hdf5_objids[d].fileno[1] == hdf5_dim->hdf5_objid.fileno[1] && + hdf5_var->dimscale_hdf5_objids[d].objno[1] == hdf5_dim->hdf5_objid.objno[1]) + { + LOG((4, "%s: for dimension %d, found dim %s", __func__, + d, dim->hdr.name)); + var->dimids[d] = dim->hdr.id; + var->dim[d] = dim; + finished = NC_TRUE; + break; + } + } /* next dim */ + } /* next grp */ + LOG((5, "%s: dimid for this dimscale is %d", __func__, + var->type_info->hdr.id)); + } /* next var->dim */ + } + /* No dimscales for this var! Invent phony dimensions. */ + else + { + hid_t spaceid = 0; + hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL; + int dataset_ndims; + + /* Find the space information for this dimension. */ + if ((spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0) + return NC_EHDFERR; + + /* Get the len of each dim in the space. */ + if (var->ndims) + { + if (!(h5dimlen = malloc(var->ndims * sizeof(hsize_t)))) + return NC_ENOMEM; + if (!(h5dimlenmax = malloc(var->ndims * sizeof(hsize_t)))) + { + free(h5dimlen); + return NC_ENOMEM; + } + if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, h5dimlen, + h5dimlenmax)) < 0) { + free(h5dimlenmax); + free(h5dimlen); + return NC_EHDFERR; + } + if (dataset_ndims != var->ndims) { + free(h5dimlenmax); + free(h5dimlen); + return NC_EHDFERR; + } + } + else + { + /* Make sure it's scalar. */ + if (H5Sget_simple_extent_type(spaceid) != H5S_SCALAR) + return NC_EHDFERR; + } + + /* Release the space object. */ + if (H5Sclose(spaceid) < 0) { + free(h5dimlen); + free(h5dimlenmax); + return NC_EHDFERR; + } + + /* Create a phony dimension for each dimension in the + * dataset, unless there already is one the correct + * size. */ + for (d = 0; d < var->ndims; d++) + { + int k; + int match; + /* Is there already a phony dimension of the correct size? */ + for(match=-1,k=0;k<ncindexsize(grp->dim);k++) { + if((dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,k)) == NULL) continue; + if ((dim->len == h5dimlen[d]) && + ((h5dimlenmax[d] == H5S_UNLIMITED && dim->unlimited) || + (h5dimlenmax[d] != H5S_UNLIMITED && !dim->unlimited))) + {match = k; break;} + } + + /* Didn't find a phony dim? Then create one. */ + if (match < 0) + { + char phony_dim_name[NC_MAX_NAME + 1]; + sprintf(phony_dim_name, "phony_dim_%d", grp->nc4_info->next_dimid); + LOG((3, "%s: creating phony dim for var %s", __func__, var->hdr.name)); + if ((retval = nc4_dim_list_add(grp, phony_dim_name, h5dimlen[d], -1, &dim))) + { + free(h5dimlenmax); + free(h5dimlen); + return retval; + } + /* Create struct for HDF5-specific dim info. */ + if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T)))) + return NC_ENOMEM; + if (h5dimlenmax[d] == H5S_UNLIMITED) + dim->unlimited = NC_TRUE; + } + + /* The variable must remember the dimid. */ + var->dimids[d] = dim->hdr.id; + var->dim[d] = dim; + } /* next dim */ + + /* Free the memory we malloced. */ + free(h5dimlen); + free(h5dimlenmax); + } + } + } + + return retval; +} + +/** + * @internal Get the class of a type + * + * @param h5 Pointer to the HDF5 file info struct. + * @param xtype NetCDF type ID. + * @param type_class Pointer that gets class of type, NC_INT, + * NC_FLOAT, NC_CHAR, or NC_STRING, NC_ENUM, NC_VLEN, NC_COMPOUND, or + * NC_OPAQUE. + * + * @return ::NC_NOERR No error. + * @author Ed Hartnett + */ +int +nc4_get_typeclass(const NC_FILE_INFO_T *h5, nc_type xtype, int *type_class) +{ + int retval = NC_NOERR; + + LOG((4, "%s xtype: %d", __func__, xtype)); + assert(type_class); + + /* If this is an atomic type, the answer is easy. */ + if (xtype <= NC_STRING) + { + switch (xtype) + { + case NC_BYTE: + case NC_UBYTE: + case NC_SHORT: + case NC_USHORT: + case NC_INT: + case NC_UINT: + case NC_INT64: + case NC_UINT64: + /* NC_INT is class used for all integral types */ + *type_class = NC_INT; + break; + + case NC_FLOAT: + case NC_DOUBLE: + /* NC_FLOAT is class used for all floating-point types */ + *type_class = NC_FLOAT; + break; + + case NC_CHAR: + *type_class = NC_CHAR; + break; + + case NC_STRING: + *type_class = NC_STRING; + break; + + default: + BAIL(NC_EBADTYPE); + } + } + else + { + NC_TYPE_INFO_T *type; + + /* See if it's a used-defined type */ + if ((retval = nc4_find_type(h5, xtype, &type))) + BAIL(retval); + if (!type) + BAIL(NC_EBADTYPE); + + *type_class = type->nc_type_class; + } + +exit: + return retval; +} + +/** + * @internal Report information about an open HDF5 object. This is + * called on any still-open objects when a HDF5 file close is + * attempted. + * + * @param uselog If true, send output to LOG not stderr. + * @param id HDF5 ID of open object. + * @param type Type of HDF5 object, file, dataset, etc. + * + * @author Dennis Heimbigner + */ +void +reportobject(int uselog, hid_t id, unsigned int type) +{ + char name[NC_HDF5_MAX_NAME]; + ssize_t len; + const char* typename = NULL; + long long printid = (long long)id; + + len = H5Iget_name(id, name, NC_HDF5_MAX_NAME); + if(len < 0) return; + name[len] = '\0'; + + switch (type) { + case H5F_OBJ_FILE: typename = "File"; break; + case H5F_OBJ_DATASET: typename = "Dataset"; break; + case H5F_OBJ_GROUP: typename = "Group"; break; + case H5F_OBJ_DATATYPE: typename = "Datatype"; break; + case H5F_OBJ_ATTR: + typename = "Attribute"; + len = H5Aget_name(id, NC_HDF5_MAX_NAME, name); + if(len < 0) len = 0; + name[len] = '\0'; + break; + default: typename = "<unknown>"; break; + } +#ifdef LOGGING + if(uselog) { + LOG((0,"Type = %s(%lld) name='%s'",typename,printid,name)); + } else +#endif + { + fprintf(stderr,"Type = %s(%lld) name='%s'",typename,printid,name); + } + +} + +/** + * @internal + * + * @param uselog + * @param fid HDF5 ID. + * @param ntypes Number of types. + * @param otypes Pointer that gets number of open types. + * + * @author Dennis Heimbigner + */ +static void +reportopenobjectsT(int uselog, hid_t fid, int ntypes, unsigned int* otypes) +{ + int t,i; + ssize_t ocount; + size_t maxobjs = -1; + hid_t* idlist = NULL; + + /* Always report somehow */ +#ifdef LOGGING + if(uselog) + LOG((0,"\nReport: open objects on %lld",(long long)fid)); + else +#endif + fprintf(stdout,"\nReport: open objects on %lld\n",(long long)fid); + maxobjs = H5Fget_obj_count(fid,H5F_OBJ_ALL); + if(idlist != NULL) free(idlist); + idlist = (hid_t*)malloc(sizeof(hid_t)*maxobjs); + for(t=0;t<ntypes;t++) { + unsigned int ot = otypes[t]; + ocount = H5Fget_obj_ids(fid,ot,maxobjs,idlist); + for(i=0;i<ocount;i++) { + hid_t o = idlist[i]; + reportobject(uselog,o,ot); + } + } + if(idlist != NULL) free(idlist); +} + +/** + * @internal Report open objects. + * + * @param uselog + * @param fid HDF5 file ID. + * + * @author Dennit Heimbigner + */ +void +reportopenobjects(int uselog, hid_t fid) +{ + unsigned int OTYPES[5] = {H5F_OBJ_FILE, H5F_OBJ_DATASET, H5F_OBJ_GROUP, + H5F_OBJ_DATATYPE, H5F_OBJ_ATTR}; + + reportopenobjectsT(uselog, fid ,5, OTYPES); +} + +/** + * @internal Report open objects given a pointer to NC_FILE_INFO_T object + * + * @param h5 file object + * + * @author Dennis Heimbigner + */ +void +showopenobjects5(NC_FILE_INFO_T* h5) +{ + NC_HDF5_FILE_INFO_T *hdf5_info; + + assert(h5 && h5->format_file_info); + hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info; + + fprintf(stderr,"===== begin showopenobjects =====\n"); + reportopenobjects(0,hdf5_info->hdfid); + fprintf(stderr,"===== end showopenobjects =====\n"); + fflush(stderr); +} + +/** + * @internal Report open objects given an ncid + * Defined to support user or gdb level call. + * + * @param ncid file id + * + * @author Dennis Heimbigner + */ +void +showopenobjects(int ncid) +{ + NC_FILE_INFO_T* h5 = NULL; + + /* Find our metadata for this file. */ + if (nc4_find_nc_grp_h5(ncid, NULL, NULL, &h5) != NC_NOERR) + fprintf(stderr,"failed\n"); + else + showopenobjects5(h5); + fflush(stderr); +} + +/** + * @internal Get HDF5 library version. + * + * @param major Pointer that gets major version number. + * @param minor Pointer that gets minor version number. + * @param release Pointer that gets release version number. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned error. + * @author Dennis Heimbigner + */ +int +NC4_hdf5get_libversion(unsigned* major,unsigned* minor,unsigned* release) +{ + if(H5get_libversion(major,minor,release) < 0) + return NC_EHDFERR; + return NC_NOERR; +} + +/** + * @internal Get HDF5 superblock version. + * + * @param h5 Pointer to HDF5 file info struct. + * @param idp Pointer that gets superblock version. + * + * @returns NC_NOERR No error. + * @returns NC_EHDFERR HDF5 returned error. + * @author Dennis Heimbigner + */ +int +NC4_hdf5get_superblock(struct NC_FILE_INFO* h5, int* idp) +{ + NC_HDF5_FILE_INFO_T *hdf5_info; + int stat = NC_NOERR; + unsigned super; + hid_t plist = -1; + + assert(h5 && h5->format_file_info); + hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info; + + if((plist = H5Fget_create_plist(hdf5_info->hdfid)) < 0) + {stat = NC_EHDFERR; goto done;} + if(H5Pget_version(plist, &super, NULL, NULL, NULL) < 0) + {stat = NC_EHDFERR; goto done;} + if(idp) *idp = (int)super; +done: + if(plist >= 0) H5Pclose(plist); + return stat; +} + +static int NC4_get_strict_att(NC_FILE_INFO_T*); +static int NC4_walk(hid_t, int*); + +/** + * @internal Determine whether file is netCDF-4. + * + * We define a file as being from netcdf-4 if any of the following + * are true: + * 1. NCPROPS attribute exists in root group + * 2. NC3_STRICT_ATT_NAME exists in root group + * 3. any of NC_ATT_REFERENCE_LIST, NC_ATT_CLASS, + * NC_ATT_DIMENSION_LIST, NC_ATT_NAME, + * NC_ATT_COORDINATES, NC_DIMID_ATT_NAME + * exist anywhere in the file; note that this + * requires walking the file. + + * @note WARNINGS: + * 1. False negatives are possible for a small subset of netcdf-4 + * created files. + * 2. Deliberate falsification in the file can be used to cause + * a false positive. + * + * @param h5 Pointer to HDF5 file info struct. + * + * @returns NC_NOERR No error. + * @author Dennis Heimbigner. + */ +int +NC4_isnetcdf4(struct NC_FILE_INFO* h5) +{ + int stat; + int isnc4 = 0; + int count; + + /* Look for NC3_STRICT_ATT_NAME */ + isnc4 = NC4_get_strict_att(h5); + if(isnc4 > 0) + goto done; + /* attribute did not exist */ + /* => last resort: walk the HDF5 file looking for markers */ + count = 0; + stat = NC4_walk(((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid, + &count); + if(stat != NC_NOERR) + isnc4 = 0; + else /* Threshold is at least two matches */ + isnc4 = (count >= 2); + +done: + return isnc4; +} + +/** + * @internal Get the NC3 strict attribute. + * + * @param h5 Pointer to HDF5 file info struct. + * + * @returns NC_NOERR No error. + * @author Dennis Heimbigner. + */ +static int +NC4_get_strict_att(NC_FILE_INFO_T *h5) +{ + hid_t grpid = -1; + hid_t attid = -1; + + /* Get root group ID. */ + grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid; + + /* Try to extract the NC3_STRICT_ATT_NAME attribute */ + attid = H5Aopen_name(grpid, NC3_STRICT_ATT_NAME); + H5Aclose(attid); + return attid; +} + +/** + * @internal Walk group struct. + * + * @param gid HDF5 ID of starting group. + * @param countp Pointer that gets count. + * + * @returns NC_NOERR No error. + * @author Dennis Heimbigner + */ +static int +NC4_walk(hid_t gid, int* countp) +{ + int ncstat = NC_NOERR; + int i,j,na; + ssize_t len; + hsize_t nobj; + herr_t err; + int otype; + hid_t grpid, dsid; + char name[NC_HDF5_MAX_NAME]; + + /* walk group members of interest */ + err = H5Gget_num_objs(gid, &nobj); + if(err < 0) return err; + + for(i = 0; i < nobj; i++) { + /* Get name & kind of object in the group */ + len = H5Gget_objname_by_idx(gid,(hsize_t)i,name,(size_t)NC_HDF5_MAX_NAME); + if(len < 0) return len; + + otype = H5Gget_objtype_by_idx(gid,(size_t)i); + switch(otype) { + case H5G_GROUP: + grpid = H5Gopen(gid,name); + NC4_walk(grpid,countp); + H5Gclose(grpid); + break; + case H5G_DATASET: /* variables */ + /* Check for phony_dim */ + if(strcmp(name,"phony_dim")==0) + *countp = *countp + 1; + dsid = H5Dopen(gid,name); + na = H5Aget_num_attrs(dsid); + for(j = 0; j < na; j++) { + hid_t aid = H5Aopen_idx(dsid,(unsigned int) j); + if(aid >= 0) { + const NC_reservedatt* ra; + ssize_t len = H5Aget_name(aid, NC_HDF5_MAX_NAME, name); + if(len < 0) return len; + /* Is this a netcdf-4 marker attribute */ + /* Is this a netcdf-4 marker attribute */ + ra = NC_findreserved(name); + if(ra != NULL) + *countp = *countp + 1; + } + H5Aclose(aid); + } + H5Dclose(dsid); + break; + default:/* ignore */ + break; + } + } + return ncstat; +} diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4info.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4info.c new file mode 100644 index 0000000000000000000000000000000000000000..5b79f68b4cf79a1846f008a9d5687fed9096cdc3 --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4info.c @@ -0,0 +1,691 @@ +/** + * @file + * @internal Add provenance info for netcdf-4 files. + * + * Copyright 2018, UCAR/Unidata See netcdf/COPYRIGHT file for copying + * and redistribution conditions. + * @author Dennis Heimbigner + */ + +#include "config.h" +#include "nc4internal.h" +#include "hdf5internal.h" +#include "nc_provenance.h" +#include "nclist.h" +#include "ncbytes.h" + +/* Provide a hack to suppress the writing of _NCProperties attribute. + This is for creating a file without _NCProperties for testing purposes. +*/ +#undef SUPPRESSNCPROPS + +/* Various Constants */ +#define NCPROPS_MAX_NAME 1024 /* max key name size */ +#define NCPROPS_MAX_VALUE 1024 /* max value size */ +#define HDF5_MAX_NAME 1024 /**< HDF5 max name. */ + +#define ESCAPECHARS "\\=|," + +/** @internal Check NetCDF return code. */ +#define NCHECK(expr) {if((expr)!=NC_NOERR) {goto done;}} + +/** @internal Check HDF5 return code. */ +#define HCHECK(expr) {if((expr)<0) {ncstat = NC_EHDFERR; goto done;}} + +static int NC4_read_ncproperties(NC_FILE_INFO_T* h5, char** propstring); +static int NC4_write_ncproperties(NC_FILE_INFO_T* h5); + +static int globalpropinitialized = 0; +static NC4_Provenance globalprovenance; + +/** + * @internal Initialize default provenance info + * This will only be used for newly created files + * or for opened files that do not contain an _NCProperties + * attribute. + * + * @return ::NC_NOERR No error. + * @author Dennis Heimbigner + */ +int +NC4_provenance_init(void) +{ + int stat = NC_NOERR; + char* name = NULL; + char* value = NULL; + unsigned major,minor,release; + NCbytes* buffer = NULL; /* for constructing the global _NCProperties */ + char printbuf[1024]; + const char* p = NULL; + + if(globalpropinitialized) + return stat; + + /* Build _NCProperties info */ + + /* Initialize globalpropinfo */ + memset((void*)&globalprovenance,0,sizeof(NC4_Provenance)); + globalprovenance.version = NCPROPS_VERSION; + + buffer = ncbytesnew(); + + /* Insert version as first entry */ + ncbytescat(buffer,NCPVERSION); + ncbytescat(buffer,"="); + + snprintf(printbuf,sizeof(printbuf),"%d",globalprovenance.version); + ncbytescat(buffer,printbuf); + + /* Insert the netcdf version */ + ncbytesappend(buffer,NCPROPSSEP2); + ncbytescat(buffer,NCPNCLIB2); + ncbytescat(buffer,"="); + ncbytescat(buffer,PACKAGE_VERSION); + + /* Insert the HDF5 as underlying storage format library */ + ncbytesappend(buffer,NCPROPSSEP2); + ncbytescat(buffer,NCPHDF5LIB2); + ncbytescat(buffer,"="); + if((stat = NC4_hdf5get_libversion(&major,&minor,&release))) goto done; + snprintf(printbuf,sizeof(printbuf),"%1u.%1u.%1u",major,minor,release); + ncbytescat(buffer,printbuf); + +#ifdef NCPROPERTIES_EXTRA + /* Add any extra fields */ + p = NCPROPERTIES_EXTRA; + if(p[0] == NCPROPSSEP2) p++; /* If leading separator */ + ncbytesappend(buffer,NCPROPSSEP2); + ncbytescat(buffer,p); +#endif + ncbytesnull(buffer); + globalprovenance.ncproperties = ncbytesextract(buffer); + +done: + ncbytesfree(buffer); + if(name != NULL) free(name); + if(value != NULL) free(value); + if(stat == NC_NOERR) + globalpropinitialized = 1; /* avoid repeating it */ + return stat; +} + +/** + * @internal finalize default provenance info + * + * @return ::NC_NOERR No error. + * @author Dennis Heimbigner + */ +int +NC4_provenance_finalize(void) +{ + return NC4_clear_provenance(&globalprovenance); +} + +/** + * @internal + * + * Construct the provenance information for a newly created file. + * Note that creation of the _NCProperties attribute is deferred + * to the sync_netcdf4_file function. + * + * @param file Pointer to file object. + * + * @return ::NC_NOERR No error. + * [Note: other errors are reported via LOG()] + * @author Dennis Heimbigner + */ +int +NC4_new_provenance(NC_FILE_INFO_T* file) +{ + int ncstat = NC_NOERR; + NC4_Provenance* provenance = NULL; + int superblock = -1; + + LOG((5, "%s: ncid 0x%x", __func__, file->root_grp->hdr.id)); + + assert(file->provenance.ncproperties == NULL); /* not yet defined */ + + provenance = &file->provenance; + memset(provenance,0,sizeof(NC4_Provenance)); /* make sure */ + + /* Set the version */ + provenance->version = globalprovenance.version; + + /* Set the superblock number */ + if((ncstat = NC4_hdf5get_superblock(file,&superblock))) goto done; + provenance->superblockversion = superblock; + + if(globalprovenance.ncproperties != NULL) { + if((provenance->ncproperties = strdup(globalprovenance.ncproperties)) == NULL) + {ncstat = NC_ENOMEM; goto done;} + } + +done: + if(ncstat) { + LOG((0,"Could not create _NCProperties attribute")); + } + return NC_NOERR; +} + +/** + * @internal + * + * Construct the provenance information for an existing file. + * + * @param file Pointer to file object. + * + * @return ::NC_NOERR No error. + * [Note: other errors are reported via LOG()] + * @author Dennis Heimbigner + */ +int +NC4_read_provenance(NC_FILE_INFO_T* file) +{ + int ncstat = NC_NOERR; + NC4_Provenance* provenance = NULL; + int superblock = -1; + char* propstring = NULL; + + LOG((5, "%s: ncid 0x%x", __func__, file->root_grp->hdr.id)); + + assert(file->provenance.version == 0); /* not yet defined */ + + provenance = &file->provenance; + memset(provenance,0,sizeof(NC4_Provenance)); /* make sure */ + + /* Set the superblock number */ + if((ncstat = NC4_hdf5get_superblock(file,&superblock))) goto done; + provenance->superblockversion = superblock; + + /* Read the _NCProperties value from the file */ + if((ncstat = NC4_read_ncproperties(file,&propstring))) goto done; + provenance->ncproperties = propstring; + propstring = NULL; + +done: + nullfree(propstring); + if(ncstat) { + LOG((0,"Could not create _NCProperties attribute")); + } + return NC_NOERR; +} + +/** + * @internal + * + * Add the provenance information to a newly created file. + * + * @param file Pointer to file object. + * + * @return ::NC_NOERR No error. + * [Note: other errors are reported via LOG()] + * @author Dennis Heimbigner + */ +int +NC4_write_provenance(NC_FILE_INFO_T* file) +{ + int ncstat = NC_NOERR; + if((ncstat = NC4_write_ncproperties(file))) + goto done; +done: + return ncstat; +} + +/* HDF5 Specific attribute read/write of _NCProperties */ +static int +NC4_read_ncproperties(NC_FILE_INFO_T* h5, char** propstring) +{ + int retval = NC_NOERR; + hid_t hdf5grpid = -1; + hid_t attid = -1; + hid_t aspace = -1; + hid_t atype = -1; + hid_t ntype = -1; + char* text = NULL; + H5T_class_t t_class; + hsize_t size; + + LOG((5, "%s", __func__)); + + hdf5grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid; + + if(H5Aexists(hdf5grpid,NCPROPS) <= 0) { /* Does not exist */ + /* File did not contain a _NCProperties attribute; leave empty */ + goto done; + } + + /* NCPROPS Attribute exists, make sure it is legitimate */ + attid = H5Aopen_name(hdf5grpid, NCPROPS); + assert(attid > 0); + aspace = H5Aget_space(attid); + atype = H5Aget_type(attid); + /* Verify atype and size */ + t_class = H5Tget_class(atype); + if(t_class != H5T_STRING) + {retval = NC_EINVAL; goto done;} + size = H5Tget_size(atype); + if(size == 0) + {retval = NC_EINVAL; goto done;} + text = (char*)malloc(1+(size_t)size); + if(text == NULL) + {retval = NC_ENOMEM; goto done;} + if((ntype = H5Tget_native_type(atype, H5T_DIR_DEFAULT)) < 0) + {retval = NC_EHDFERR; goto done;} + if((H5Aread(attid, ntype, text)) < 0) + {retval = NC_EHDFERR; goto done;} + /* Make sure its null terminated */ + text[(size_t)size] = '\0'; + if(propstring) {*propstring = text; text = NULL;} + +done: + if(text != NULL) free(text); + /* Close out the HDF5 objects */ + if(attid > 0 && H5Aclose(attid) < 0) retval = NC_EHDFERR; + if(aspace > 0 && H5Sclose(aspace) < 0) retval = NC_EHDFERR; + if(atype > 0 && H5Tclose(atype) < 0) retval = NC_EHDFERR; + if(ntype > 0 && H5Tclose(ntype) < 0) retval = NC_EHDFERR; + + /* For certain errors, actually fail, else log that attribute was invalid and ignore */ + if(retval != NC_NOERR) { + if(retval != NC_ENOMEM && retval != NC_EHDFERR) { + LOG((0,"Invalid _NCProperties attribute: ignored")); + retval = NC_NOERR; + } + } + return retval; +} + +static int +NC4_write_ncproperties(NC_FILE_INFO_T* h5) +{ +#ifdef SUPPRESSNCPROPERTY + return NC_NOERR; +#else /*!SUPPRESSNCPROPERTY*/ + int retval = NC_NOERR; + hid_t hdf5grpid = -1; + hid_t attid = -1; + hid_t aspace = -1; + hid_t atype = -1; + size_t len = 0; + NC4_Provenance* prov = &h5->provenance; + + LOG((5, "%s", __func__)); + + /* If the file is read-only, return an error. */ + if (h5->no_write) + {retval = NC_EPERM; goto done;} + + hdf5grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid; + + if(H5Aexists(hdf5grpid,NCPROPS) > 0) /* Already exists, no overwrite */ + goto done; + + /* Build the property if we have legit value */ + if(prov->ncproperties != NULL) { + /* Build the HDF5 string type */ + if ((atype = H5Tcopy(H5T_C_S1)) < 0) + {retval = NC_EHDFERR; goto done;} + if (H5Tset_strpad(atype, H5T_STR_NULLTERM) < 0) + {retval = NC_EHDFERR; goto done;} + if(H5Tset_cset(atype, H5T_CSET_ASCII) < 0) + {retval = NC_EHDFERR; goto done;} + len = strlen(prov->ncproperties); + if(H5Tset_size(atype, len) < 0) + {retval = NC_EFILEMETA; goto done;} + /* Create NCPROPS attribute */ + if((aspace = H5Screate(H5S_SCALAR)) < 0) + {retval = NC_EFILEMETA; goto done;} + if ((attid = H5Acreate(hdf5grpid, NCPROPS, atype, aspace, H5P_DEFAULT)) < 0) + {retval = NC_EFILEMETA; goto done;} + if (H5Awrite(attid, atype, prov->ncproperties) < 0) + {retval = NC_EFILEMETA; goto done;} +/* Verify */ +#if 0 + { + hid_t spacev, typev; + hsize_t dsize, tsize; + typev = H5Aget_type(attid); + spacev = H5Aget_space(attid); + dsize = H5Aget_storage_size(attid); + tsize = H5Tget_size(typev); + fprintf(stderr,"dsize=%lu tsize=%lu\n",(unsigned long)dsize,(unsigned long)tsize); + } +#endif + } + +done: + /* Close out the HDF5 objects */ + if(attid > 0 && H5Aclose(attid) < 0) retval = NC_EHDFERR; + if(aspace > 0 && H5Sclose(aspace) < 0) retval = NC_EHDFERR; + if(atype > 0 && H5Tclose(atype) < 0) retval = NC_EHDFERR; + + /* For certain errors, actually fail, else log that attribute was invalid and ignore */ + switch (retval) { + case NC_ENOMEM: + case NC_EHDFERR: + case NC_EPERM: + case NC_EFILEMETA: + case NC_NOERR: + break; + default: + LOG((0,"Invalid _NCProperties attribute")); + retval = NC_NOERR; + break; + } + return retval; +#endif /*!SUPPRESSNCPROPERTY*/ +} + +/**************************************************/ +/* Utilities */ + +/* Debugging */ + +void +ncprintprovenance(NC4_Provenance* info) +{ + fprintf(stderr,"[%p] version=%d superblockversion=%d ncproperties=|%s|\n", + info, + info->version, + info->superblockversion, + (info->ncproperties==NULL?"":info->ncproperties)); +} + +/** + * @internal + * + * Clear the NCPROVENANCE object; do not free it + * @param prov Pointer to provenance object + * + * @return ::NC_NOERR No error. + * @author Dennis Heimbigner + */ +int +NC4_clear_provenance(NC4_Provenance* prov) +{ + LOG((5, "%s", __func__)); + + if(prov == NULL) return NC_NOERR; + nullfree(prov->ncproperties); + memset(prov,0,sizeof(NC4_Provenance)); + return NC_NOERR; +} + +#if 0 +/* Unused functions */ + +/** + * @internal Parse file properties. + * + * @param text0 Text properties. + * @param pairs list of parsed (key,value) pairs + * + * @return ::NC_NOERR No error. + * @author Dennis Heimbigner + */ +static int +properties_parse(const char* text0, NClist* pairs) +{ + int ret = NC_NOERR; + char* p; + char* q; + char* text = NULL; + + if(text0 == NULL || strlen(text0) == 0) + goto done; + + text = strdup(text0); + if(text == NULL) return NC_ENOMEM; + + /* For back compatibility with version 1, translate '|' -> ',' */ + for(p=text;*p;p++) { + if(*p == NCPROPSSEP1) + *p = NCPROPSSEP2; + } + + /* Walk and fill in ncinfo */ + p = text; + while(*p) { + char* name = p; + char* value = NULL; + char* next = NULL; + + /* Delimit whole (key,value) pair */ + q = locate(p,NCPROPSSEP2); + if(*q != '\0') /* Never go beyond the final nul term */ + *q++ = '\0'; + next = q; + /* split key and value */ + q = locate(p,'='); + name = p; + *q++ = '\0'; + value = q; + /* Set up p for next iteration */ + p = next; + nclistpush(pairs,strdup(name)); + nclistpush(pairs,strdup(value)); + } +done: + if(text) free(text); + return ret; +} + +/* Locate a specific character and return its pointer + or EOS if not found + take \ escapes into account */ +static char* +locate(char* p, char tag) +{ + char* next; + int c; + assert(p != NULL); + for(next = p;(c = *next);next++) { + if(c == tag) + return next; + else if(c == '\\' && next[1] != '\0') + next++; /* skip escaped char */ + } + return next; /* not found */ +} + +/* Utility to transfer a string to a buffer with escaping */ +static void +escapify(NCbytes* buffer, const char* s) +{ + const char* p; + for(p=s;*p;p++) { + if(strchr(ESCAPECHARS,*p) != NULL) + ncbytesappend(buffer,'\\'); + ncbytesappend(buffer,*p); + } +} + +/** + * @internal Build _NCProperties attribute value. + * + * Convert a NCPROPINFO instance to a single string. + * Will always convert to current format + * + * @param version + * @param list Properties list + * @param spropp Pointer that gets properties string. + * @return ::NC_NOERR No error. + * @return ::NC_EINVAL failed. + * @author Dennis Heimbigner + */ +static int +build_propstring(int version, NClist* list, char** spropp) +{ + int stat = NC_NOERR; + int i; + NCbytes* buffer = NULL; + char sversion[64]; + + LOG((5, "%s version=%d", __func__, version)); + + if(spropp != NULL) *spropp = NULL; + + if(version == 0 || version > NCPROPS_VERSION) /* unknown case */ + goto done; + if(list == NULL) + {stat = NC_EINVAL; goto done;} + + if((buffer = ncbytesnew()) == NULL) + {stat = NC_ENOMEM; goto done;} + + /* start with version */ + ncbytescat(buffer,NCPVERSION); + ncbytesappend(buffer,'='); + /* Use current version */ + snprintf(sversion,sizeof(sversion),"%d",NCPROPS_VERSION); + ncbytescat(buffer,sversion); + + for(i=0;i<nclistlength(list);i+=2) { + char* value, *name; + name = nclistget(list,i); + if(name == NULL) continue; + value = nclistget(list,i+1); + ncbytesappend(buffer,NCPROPSSEP2); /* terminate last entry */ + escapify(buffer,name); + ncbytesappend(buffer,'='); + escapify(buffer,value); + } + /* Force null termination */ + ncbytesnull(buffer); + if(spropp) *spropp = ncbytesextract(buffer); + +done: + if(buffer != NULL) ncbytesfree(buffer); + return stat; +} + +static int +properties_getversion(const char* propstring, int* versionp) +{ + int ncstat = NC_NOERR; + int version = 0; + /* propstring should begin with "version=dddd" */ + if(propstring == NULL || strlen(propstring) < strlen("version=") + strlen("1")) + {ncstat = NC_EINVAL; goto done;} /* illegal version */ + if(memcmp(propstring,"version=",strlen("version=")) != 0) + {ncstat = NC_EINVAL; goto done;} /* illegal version */ + propstring += strlen("version="); + /* get version */ + version = atoi(propstring); + if(version < 0) + {ncstat = NC_EINVAL; goto done;} /* illegal version */ + if(versionp) *versionp = version; +done: + return ncstat; +} + +/** + * @internal + * + * Construct the parsed provenance information + * + * @param prov Pointer to provenance object + * + * @return ::NC_NOERR No error. + * @return ::NC_ENOMEM + * @return ::NC_EINVAL + * @author Dennis Heimbigner + */ +static int +parse_provenance(NC4_Provenance* prov) +{ + int ncstat = NC_NOERR; + char *name = NULL; + char *value = NULL; + int version = 0; + NClist* list = NULL; + + LOG((5, "%s: prov 0x%x", __func__, prov)); + + if(prov->ncproperty == NULL || strlen(prov->ncproperty) < strlen("version=")) + {ncstat = NC_EINVAL; goto done;} + if((list = nclistnew()) == NULL) + {ncstat = NC_ENOMEM; goto done;} + + /* Do we understand the version? */ + if(prov->version > 0 && prov->version <= NCPROPS_VERSION) {/* recognized version */ + if((ncstat=properties_parse(prov->ncproperty,list))) + goto done; + /* Remove version pair from properties list*/ + if(nclistlength(list) < 2) + {ncstat = NC_EINVAL; goto done;} /* bad _NCProperties attribute */ + /* Throw away the purported version=... */ + nclistremove(list,0); /* version key */ + nclistremove(list,0); /* version value */ + + /* Now, rebuild to the latest version */ + switch (version) { + default: break; /* do nothing */ + case 1: { + int i; + for(i=0;i<nclistlength(list);i+=2) { + char* newname = NULL; + name = nclistget(list,i); + if(name == NULL) continue; /* ignore */ + if(strcmp(name,NCPNCLIB1) == 0) + newname = NCPNCLIB2; /* change name */ + else if(strcmp(name,NCPHDF5LIB1) == 0) + newname = NCPHDF5LIB2; + else continue; /* ignore */ + /* Do any rename */ + nclistset(list,i,strdup(newname)); + if(name) {free(name); name = NULL;} + } + } break; + } /*switch*/ + } + prov->properties = list; + list = NULL; + +done: + nclistfreeall(list); + if(name != NULL) free(name); + if(value != NULL) free(value); + return ncstat; +} + +/** + * @internal + * + * Clear and Free the NC4_Provenance object + * @param prov Pointer to provenance object + * + * @return ::NC_NOERR No error. + * @author Dennis Heimbigner + */ +static int +NC4_free_provenance(NC4_Provenance* prov) +{ + LOG((5, "%s", __func__)); + + if(prov == NULL) return NC_NOERR; + NC4_clear_provenance(prov); + free(prov); + return NC_NOERR; +} + +/* Utility to copy contents of the dfalt into an NCPROPINFO object */ +static int +propinfo_default(NC4_Properties* dst, const NC4_Properties* dfalt) +{ + int i; + if(dst->properties == NULL) { + dst->properties = nclistnew(); + if(dst->properties == NULL) return NC_ENOMEM; + } + dst->version = dfalt->version; + for(i=0;i<nclistlength(dfalt->properties);i++) { + char* s = nclistget(dfalt->properties,i); + s = strdup(s); + if(s == NULL) return NC_ENOMEM; + nclistpush(dst->properties,s); + } + return NC_NOERR; +} + +#endif /*0*/ diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4mem.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4mem.c new file mode 100644 index 0000000000000000000000000000000000000000..41a2934bd8261bb031141f23f14b556d4de9ef30 --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4mem.c @@ -0,0 +1,81 @@ +/********************************************************************* +* Copyright 2018, UCAR/Unidata +* See netcdf/COPYRIGHT file for copying and redistribution conditions. +* ********************************************************************/ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* Copyright by The HDF Group. * +* Copyright by the Board of Trustees of the University of Illinois. * +* All rights reserved. * +* * +* This file is part of HDF5. The full HDF5 copyright notice, including * +* terms governing use, modification, and redistribution, is contained in * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "config.h" +#include "hdf5internal.h" +#include <H5DSpublic.h> /* must be after nc4internal.h */ +#include <H5Fpublic.h> +#include <H5LTpublic.h> + +#include "netcdf.h" +#include "nc4internal.h" + +#undef DEBUG + +#ifndef HDrealloc + #define HDrealloc(M,Z) realloc(M,Z) +#endif /* HDrealloc */ + +int +NC4_open_image_file(NC_FILE_INFO_T* h5) +{ + int stat = NC_NOERR; + hid_t hdfid; + + /* check arguments */ + if(h5->mem.memio.memory == NULL || h5->mem.memio.size == 0) + {stat = NC_EINVAL; goto done;} + + /* Figure out the image flags */ + h5->mem.imageflags = 0; + if(h5->mem.locked) { + h5->mem.imageflags |= (H5LT_FILE_IMAGE_DONT_COPY | H5LT_FILE_IMAGE_DONT_RELEASE); + } + if(!h5->no_write) + h5->mem.imageflags |= H5LT_FILE_IMAGE_OPEN_RW; + + /* Create the file but using our version of H5LTopen_file_image */ + hdfid = NC4_image_init(h5); + if(hdfid < 0) + {stat = NC_EHDFERR; goto done;} + + /* Remember HDF5 file identifier. */ + ((NC_HDF5_FILE_INFO_T *)h5->format_file_info)->hdfid = hdfid; + +done: + return stat; +} + +int +NC4_create_image_file(NC_FILE_INFO_T* h5, size_t initialsz) +{ + int stat = NC_NOERR; + hid_t hdfid; + + /* Create the file but using our version of H5LTopen_file_image */ + h5->mem.created = 1; + h5->mem.initialsize = initialsz; + h5->mem.imageflags |= H5LT_FILE_IMAGE_OPEN_RW; + hdfid = NC4_image_init(h5); + if(hdfid < 0) + {stat = NC_EHDFERR; goto done;} + /* Remember HDF5 file identifier. */ + ((NC_HDF5_FILE_INFO_T *)h5->format_file_info)->hdfid = hdfid; +done: + return stat; +} diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4memcb.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4memcb.c new file mode 100644 index 0000000000000000000000000000000000000000..472f6cac7a915a582565a6ef037239b5503d0f94 --- /dev/null +++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4memcb.c @@ -0,0 +1,1042 @@ +/********************************************************************* +* Copyright 2018, UCAR/Unidata +* See netcdf/COPYRIGHT file for copying and redistribution conditions. +* ********************************************************************/ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* Copyright by The HDF Group. * +* Copyright by the Board of Trustees of the University of Illinois. * +* All rights reserved. * +* * +* This file is part of HDF5. The full HDF5 copyright notice, including * +* terms governing use, modification, and redistribution, is contained in * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * @internal Inmemory support + * + * This code is derived from H5LT.c#H5LTopen_file_image. + * In order to make the netcdf inmemory code work, it is necessary + * to modify some of the callback functions; specifically + * image_malloc, image_realloc, and image_memcpy. + * <p> + * The changes are directed at allowing the caller to + * specify two things. + * <ol> + * <li> To specify (indirectly) the H5LT_FILE_IMAGE_DONT_COPY flag. + * This means that no attempt to realloc the caller provided memory + * should be made. This also means that the memory block pointer + * provided by the caller will be thesame returned by <em>nc_close_memio()</em>. + * <li> The caller overallocates the memory so that there is space + * to allow the file to be modified in place. + * </ol> + * <p> + * The existing implementation of H5LTopen_file_image has two flaws + * with respect to these properties. + * <ol> + * <li> The image_realloc callback fails if + * H5LT_FILE_IMAGE_DONT_COPY flag is set even if there is room + * to allow the memory block to pretend to expand (because + * of overallocation). + * <li> When the caller attempts to get the final memory block, + * the HDF5 library makes a copy, unless theH5LT_FILE_IMAGE_DONT_COPY + * flag is set. This is unnecessary. Note that in this situation, + * the HDF5 library will use + * <em>image_malloc()</em> + * followed by + * <em>image_memcpy()</em> + * </ol> + * <p> + * So, the callback changes to support this properly are as follows. + * <dl> + * <dt>image_realloc</dt> + * <dd>If there is sufficient space (because of overallocation), + * the pretend to realloc and return the incoming memory block + * instead of taking the chance of doing a real realloc.</dd> + * <dt>image_malloc</dt> + * <dd>If the operation being performed is to obtain + * the space to copy the final memory, then just return + * the original memory block. Note that this case is detectable + * because the callback is given the value + * H5FD_FILE_IMAGE_OP_PROPERTY_LIST_GET.</dd> + * <dt>image_memcpy</dt> + * <dd>Similar to the image_malloc change. + * Namely, if the operation being performed is to copy + * out the final memory contents, and the final memory block + * is the same as that provided by the caller originally, then + * just do nothing. Again, this case can be detected + * by the occurrence of H5FD_FILE_IMAGE_OP_PROPERTY_LIST_GET.</dd> + * </dl> + * @author Dennis Heimbigner +*/ + +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <stdio.h> + +#include <hdf5.h> +#include <hdf5_hl.h> + +#include "nc4internal.h" + +#ifndef HDrealloc +#define HDrealloc(x,y) realloc(x,y) +#endif +#ifndef SUCCEED +#define SUCCEED 0 +#define FAIL -1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#undef TRACE +#undef CATCH +#undef TRACE_UDATA + +#ifdef TRACE +#define CATCH +#endif + +#ifdef TRACE +#include <stdarg.h> +static void trace(const char* fcn, H5FD_file_image_op_t op, void* _udata, ...); +static void traceend(const char* fcn, void* _udata, uintptr_t retval); +static const char* traceop(H5FD_file_image_op_t op); +static char* traceflags(int flags); + +/* In case we do not have variadic macros */ +#define TRACE0(fcn,op,udata) trace(fcn,op,udata) +#define TRACE1(fcn,op,udata,x1) trace(fcn,op,udata,x1) +#define TRACE2(fcn,op,udata,x1,x2) trace(fcn,op,udata,x1,x2) +#define TRACE3(fcn,op,udata,x1,x2,x3) trace(fcn,op,udata,x1,x2,x3) +#define TRACEEND(fcn,udata,retval) traceend(fcn,udata,(uintptr_t)retval); +#else /*!TRACE*/ +#define TRACE0(fcn,op,udata) +#define TRACE1(fcn,op,udata,x1) +#define TRACE2(fcn,op,udata,x1,x2) +#define TRACE3(fcn,op,udata,x1,x2,x3) +#define TRACEEND(fcn,udata,retval) +#endif + +#ifdef CATCH +static void tracefail(const char* fcn); +#define TRACEFAIL(fcn) tracefail(fcn) +#else +#define TRACEFAIL(fcn) +#endif + +#define DEFAULT_CREATE_MEMSIZE ((size_t)1<<16) + +#ifndef H5LT_FILE_IMAGE_DONT_COPY + +/* Flag definitions for H5LTopen_file_image() */ +#define H5LT_FILE_IMAGE_OPEN_RW 0x0001 /* Open image for read-write */ +#define H5LT_FILE_IMAGE_DONT_COPY 0x0002 /* The HDF5 lib won't copy */ +/* user supplied image buffer. The same image is open with the core driver. */ +#define H5LT_FILE_IMAGE_DONT_RELEASE 0x0004 /* The HDF5 lib won't */ +/* deallocate user supplied image buffer. The user application is reponsible */ +/* for doing so. */ +#define H5LT_FILE_IMAGE_ALL 0x0007 + +#endif /*H5LT_FILE_IMAGE_DONT_COPY*/ + + +#if 0 +/* For Lex and Yacc */ +#define COL 3 +#define LIMIT 512 +#define INCREMENT 1024 +#define TMP_LEN 256 +#define MAX(a,b) (((a)>(b)) ? (a) : (b)) +size_t input_len; +char *myinput; +size_t indent = 0; +#endif /*0*/ + +/* File Image operations + + A file image is a representation of an HDF5 file in a memory + buffer. In order to perform operations on an image in a similar way + to a file, the application buffer is copied to a FAPL buffer, which + in turn is copied to a VFD buffer. Buffer copying can decrease + performance, especially when using large file images. A solution to + this issue is to simulate the copying of the application buffer, + when actually the same buffer is used for the FAPL and the VFD. + This is implemented by using callbacks that simulate the standard + functions for memory management (additional callbacks are used for + the management of associated data structures). From the application + standpoint, a file handle can be obtained from a file image by using + the API routine H5LTopen_file_image(). This function takes a flag + argument that indicates the HDF5 library how to handle the given image; + several flag values can be combined by using the bitwise OR operator. + Valid flag values include: + + H5LT_FILE_IMAGE_OPEN_RW indicates the HDF5 library to open the file + image in read/write mode. Default is read-only mode. + + H5LT_FILE_IMAGE_DONT_COPY indicates the HDF5 library to not copy the + supplied user buffer; the same buffer will be handled by the FAPL and + the VFD driver. Default operation copies the user buffer to the FAPL and + VFD driver. + + H5LT_FILE_IMAGE_DONT_RELEASE indicates the HDF5 library to not release + the buffer handled by the FAPL and the VFD upon closing. This flag value + is only applicable when the flag value H5LT_FILE_IMAGE_DONT_COPY is set as + well. The application is responsible to release the image buffer. +*/ + +/* Data structure to pass application data to callbacks. */ +/* Modified to add NC_FILE_INFO_T ptr */ +typedef struct { + void *app_image_ptr; /* Pointer to application buffer */ + size_t app_image_size; /* Size of application buffer */ + void *fapl_image_ptr; /* Pointer to FAPL buffer */ + size_t fapl_image_size; /* Size of FAPL buffer */ + int fapl_ref_count; /* Reference counter for FAPL buffer */ + void *vfd_image_ptr; /* Pointer to VFD buffer */ + size_t vfd_image_size; /* Size of VFD buffer */ + int vfd_ref_count; /* Reference counter for VFD buffer */ + unsigned flags; /* Flags indicate how the file image will */ + /* be open */ + int ref_count; /* Reference counter on udata struct */ + NC_FILE_INFO_T* h5; +} H5LT_file_image_ud_t; + +/* callbacks prototypes for file image ops */ +static void *local_image_malloc(size_t size, H5FD_file_image_op_t file_image_op, void *udata); +static void *local_image_memcpy(void *dest, const void *src, size_t size, H5FD_file_image_op_t file_image_op, void *udata); +static herr_t local_image_free(void *ptr, H5FD_file_image_op_t file_image_op, void *udata); +static void *local_udata_copy(void *udata); +static herr_t local_udata_free(void *udata); + +/* Definition of callbacks for file image operations. */ + + +/*------------------------------------------------------------------------- +* Function: image_malloc +* +* Purpose: Simulates malloc() function to avoid copying file images. +* The application buffer is set to the buffer on only one FAPL. +* Then the FAPL buffer can be copied to other FAPL buffers or +* to only one VFD buffer. +* +* Return: Address of "allocated" buffer, if successful. Otherwise, it returns +* NULL. +* +* Programmer: Christian Chilan +* +* Date: October 3, 2011 +* +*------------------------------------------------------------------------- +*/ +static void * +local_image_malloc(size_t size, H5FD_file_image_op_t file_image_op, void *_udata) +{ + H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata; + void * return_value = NULL; + + TRACE1("malloc", file_image_op, _udata, size); + +#if 0 + /* callback is only used if the application buffer is not actually copied */ + if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY)) + goto out; +#endif + + switch ( file_image_op ) { + /* the app buffer is "copied" to only one FAPL. Afterwards, FAPLs can be "copied" */ + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_SET: + /* It appears that the fapl memory is never created as we use it, so + we expect the udata ptr to be either null or same as the app buffer.*/ + assert(udata->fapl_image_ptr == NULL || udata->fapl_image_ptr == udata->app_image_ptr); + + if (udata->app_image_ptr == NULL) + goto out; + if (udata->app_image_size != size) + goto out; + if (udata->fapl_image_ptr != NULL) + goto out; + if (udata->fapl_image_size != 0) + goto out; + if (udata->fapl_ref_count != 0) + goto out; + + udata->fapl_image_ptr = udata->app_image_ptr; + udata->fapl_image_size = udata->app_image_size; + return_value = udata->fapl_image_ptr; + udata->fapl_ref_count++; + return_value = udata->fapl_image_ptr; + break; + + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_COPY: + if (udata->fapl_image_ptr == NULL) + goto out; + if (udata->fapl_image_size != size) + goto out; + if (udata->fapl_ref_count == 0) + goto out; + + return_value = udata->fapl_image_ptr; + udata->fapl_ref_count++; + break; + + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_GET: + if (udata->fapl_image_ptr == NULL) + goto out; + /* fake the malloc by returning the current memory */ + return_value = udata->fapl_image_ptr; + break; + + case H5FD_FILE_IMAGE_OP_FILE_OPEN: + /* FAPL buffer is "copied" to only one VFD buffer */ + if (udata->vfd_image_ptr != NULL) + goto out; + if (udata->vfd_image_size != 0) + goto out; + if (udata->vfd_ref_count != 0) + goto out; + if (udata->fapl_image_ptr == NULL) + goto out; + if (udata->fapl_image_size != size) + goto out; + if (udata->fapl_ref_count == 0) + goto out; + + udata->vfd_image_ptr = udata->fapl_image_ptr; + udata->vfd_image_size = size; + udata->vfd_ref_count++; + return_value = udata->vfd_image_ptr; + break; + + /* added unused labels to shut the compiler up */ + case H5FD_FILE_IMAGE_OP_NO_OP: + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_CLOSE: + case H5FD_FILE_IMAGE_OP_FILE_RESIZE: + case H5FD_FILE_IMAGE_OP_FILE_CLOSE: + default: + goto out; + } /* end switch */ + + TRACEEND("malloc",_udata,return_value); + + return(return_value); + +out: + TRACEFAIL("malloc"); + return NULL; +} /* end image_malloc() */ + + +/*------------------------------------------------------------------------- +* Function: image_memcpy +* +* Purpose: Simulates memcpy() function to avoid copying file images. +* The image buffer can be set to only one FAPL buffer, and +* "copied" to only one VFD buffer. The FAPL buffer can be +* "copied" to other FAPLs buffers. +* +* Return: The address of the destination buffer, if successful. Otherwise, it +* returns NULL. +* +* Programmer: Christian Chilan +* +* Date: October 3, 2011 +* +*------------------------------------------------------------------------- +*/ +static void * +local_image_memcpy(void *dest, const void *src, size_t size, H5FD_file_image_op_t file_image_op, + void *_udata) +{ + H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata; + + TRACE3("memcpy", file_image_op, _udata,dest,src,size); + +#if 0 + /* callback is only used if the application buffer is not actually copied */ + if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY)) + goto out; +#endif + + switch(file_image_op) { + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_SET: + if (dest != udata->fapl_image_ptr) + goto out; + if (src != udata->app_image_ptr) + goto out; + if (size != udata->fapl_image_size) + goto out; + if (size != udata->app_image_size) + goto out; + if (udata->fapl_ref_count == 0) + goto out; + if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY)) { + if(src != dest) { + memcpy(dest,src,size); +#ifdef TRACE + fprintf(stderr,"\t>>>> memcpy(%p,%p,%ld)\n", + dest,src,(unsigned long)size); +#endif + } + } + break; + + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_COPY: + if (dest != udata->fapl_image_ptr) + goto out; + if (src != udata->fapl_image_ptr) + goto out; + if (size != udata->fapl_image_size) + goto out; + if (udata->fapl_ref_count < 2) + goto out; + break; + + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_GET: + if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY)) + goto out; + /* test: src == dest == original */ + if(src != dest || src != udata->fapl_image_ptr) + goto out; + break; + + case H5FD_FILE_IMAGE_OP_FILE_OPEN: + if (dest != udata->vfd_image_ptr) + goto out; + if (src != udata->fapl_image_ptr) + goto out; + if (size != udata->vfd_image_size) + goto out; + if (size != udata->fapl_image_size) + goto out; + if (udata->fapl_ref_count == 0) + goto out; + if (udata->vfd_ref_count != 1) + goto out; + break; + + /* added unused labels to shut the compiler up */ + case H5FD_FILE_IMAGE_OP_NO_OP: + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_CLOSE: + case H5FD_FILE_IMAGE_OP_FILE_RESIZE: + case H5FD_FILE_IMAGE_OP_FILE_CLOSE: + default: + goto out; + } /* end switch */ + + TRACEEND("memcpy",_udata,dest); + return(dest); + +out: + TRACEFAIL("memcpy"); + return NULL; +} /* end image_memcpy() */ + + +/*------------------------------------------------------------------------- +* Function: image_realloc +* +* Purpose: Reallocates the shared application image buffer and updates data +* structures that manage buffer "copying". +* +* Return: Address of reallocated buffer, if successful. Otherwise, it returns +* NULL. +* +* Programmer: Christian Chilan +* +* Date: October 3, 2011 +* +*------------------------------------------------------------------------- +*/ + +/* This warning is from H5FDcore.c" + Be careful of non-Posix realloc() that doesn't understand + what to do when the first argument is null. +*/ + +/* Modified: +1. If the realloc new size is <= existing size, + then pretend we did a realloc and return success. + This avoids unneccessary heap operations. +2. If the H5LT_FILE_IMAGE_DONT_COPY or + H5LT_FILE_IMAGE_DONT_RELEASE flag is set and the + realloc new size is > existing size, then fail + because the realloc() call may change the address + of the buffer. The new address cannot be + communicated to the application to release it. +3. Otherwise, use realloc(). Note that this may have the + side effect of freeing the previous memory chunk. +*/ +static void * +local_image_realloc(void *ptr, size_t size, H5FD_file_image_op_t file_image_op, void *_udata) +{ + H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata; + void * return_value = NULL; + + TRACE2("realloc", file_image_op, _udata, ptr, size); + +#if 0 + /* callback is only used if the application buffer is not actually copied */ + if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY)) + goto out; +#endif + + /* realloc() is not allowed if the image is open in read-only mode */ + if (!(udata->flags & H5LT_FILE_IMAGE_OPEN_RW)) + goto out; + + /* DONT_COPY => DONT_RELEASE */ + assert(((udata->flags & H5LT_FILE_IMAGE_DONT_COPY)?(udata->flags & H5LT_FILE_IMAGE_DONT_RELEASE):1)); + + /* Note that the fapl pointer is never realloc'd */ + if (file_image_op == H5FD_FILE_IMAGE_OP_FILE_RESIZE) { + + if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY)) { /* buffer modification is allowed */ + /* Divide code based on whether ptr == NULL or not */ + if(ptr == NULL) { + /* From realloc man page: If ptr is NULL, then the call is equivalent to malloc(size), + for all values of size; if size is equal to zero, and ptr is not NULL, then the call + is equivalent to free(ptr). */ + /* if the app_image != NULL then free it to simulate effect of realloc */ + if(udata->app_image_ptr != NULL) + free(udata->app_image_ptr); + udata->vfd_image_ptr = malloc(size); + udata->vfd_ref_count++; + } else { /* ptr != NULL */ + if(udata->vfd_image_ptr != ptr) + goto out; + if (udata->vfd_ref_count != 1) + goto out; + udata->vfd_image_ptr = realloc(ptr, size); + if(NULL == udata->vfd_image_ptr) { + LOG((0,"image_realloc: unable to allocate memory block of size: %lu bytes",(unsigned long)size)); + goto out; + } +#ifdef TRACE + fprintf(stderr,"\t>>>> realloc(%p,%ld)=>%p\n",ptr,(unsigned long)size,udata->vfd_image_ptr); +#endif + } + udata->vfd_image_size = size; + + /* Make sure other pointers are consistent */ + udata->app_image_ptr = udata->vfd_image_ptr; + udata->fapl_image_ptr = udata->vfd_image_ptr; + + } else { /* Cannot realloc, so fake it */ + if(size <= udata->vfd_image_size) { + /* Ok, pretend we did a realloc but just change size*/ + udata->vfd_image_size = size; + } else + goto out; + } + return_value = udata->vfd_image_ptr; + } /* end if */ + else + goto out; + + TRACEEND("realloc",_udata,return_value); + return(return_value); + +out: + TRACEFAIL("realloc"); + return NULL; +} /* end local_image_realloc() */ + +/*------------------------------------------------------------------------- +* Function: image_free +* +* Purpose: Simulates deallocation of FAPL and VFD buffers by decreasing +* reference counters. Shared application buffer is actually +* deallocated if there are no outstanding references. +* +* Return: SUCCEED or FAIL +* +* Programmer: Christian Chilan +* +* Date: October 3, 2011 +* +*------------------------------------------------------------------------- +*/ +static herr_t +local_image_free(void *ptr, H5FD_file_image_op_t file_image_op, void *_udata) +{ + H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata; + + TRACE1("free", file_image_op, _udata, ptr); + +#if 0 + /* callback is only used if the application buffer is not actually copied */ + if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY)) + goto out; +#endif + + switch(file_image_op) { + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_CLOSE: + if (udata->fapl_image_ptr != ptr) + goto out; + if (udata->fapl_ref_count == 0) + goto out; + + udata->fapl_ref_count--; + + /* For the way we use it, it should still be the case that + the fapl pointer is same as image_ptr, so we do not need + to do anything */ + assert(udata->fapl_image_ptr == udata->app_image_ptr); + /* clean up */ +#if 0 + udata->app_image_ptr = NULL; + udata->fapl_image_ptr = NULL; +#endif + break; + + case H5FD_FILE_IMAGE_OP_FILE_CLOSE: + if (udata->vfd_image_ptr != ptr) + goto out; + if (udata->vfd_ref_count != 1) + goto out; + + udata->vfd_ref_count--; + + break; + + /* added unused labels to keep the compiler quiet */ + case H5FD_FILE_IMAGE_OP_NO_OP: + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_SET: + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_COPY: + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_GET: + case H5FD_FILE_IMAGE_OP_FILE_OPEN: + case H5FD_FILE_IMAGE_OP_FILE_RESIZE: + default: + goto out; + } /* end switch */ + + TRACEEND("free",_udata,1); + return(SUCCEED); + +out: + TRACEFAIL("free"); + return(FAIL); +} /* end image_free() */ + + +/*------------------------------------------------------------------------- +* Function: udata_copy +* +* Purpose: Simulates the copying of the user data structure utilized in the +* management of the "copying" of file images. +* +* Return: Address of "newly allocated" structure, if successful. Otherwise, it +* returns NULL. +* +* Programmer: Christian Chilan +* +* Date: October 3, 2011 +* +*------------------------------------------------------------------------- +*/ +static void * +local_udata_copy(void *_udata) +{ + H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata; + + TRACE0("udata_copy", 0, _udata); + +#if 0 + /* callback is only used if the application buffer is not actually copied */ + if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY)) + goto out; +#endif + + /* never copy so we only have one instance */ + if (udata->ref_count == 0) + goto out; + udata->ref_count++; + + TRACEEND("udata_copy",udata,1); + return(udata); + +out: + TRACEFAIL("udata_copy"); + return NULL; +} /* end udata_copy */ + + +/*------------------------------------------------------------------------- +* Function: udata_free +* +* Purpose: Simulates deallocation of the user data structure utilized in the +* management of the "copying" of file images. The data structure is +* actually deallocated when there are no outstanding references. +* +* Return: SUCCEED or FAIL +* +* Programmer: Christian Chilan +* +* Date: October 3, 2011 +* +*------------------------------------------------------------------------- +*/ +static herr_t +local_udata_free(void *_udata) +{ + H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata; + + TRACE0("udata_free", 0, _udata); + +#if 0 + /* callback is only used if the application buffer is not actually copied */ + if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY)) + goto out; +#endif + + if (udata->ref_count == 0) + goto out; + udata->ref_count--; + + TRACEEND("udata_free",udata,1); + return(SUCCEED); + +out: + TRACEFAIL("udata_free"); + return(FAIL); +} /* end udata_free */ + + +/* End of callbacks definitions for file image operations */ + +hid_t +NC4_image_init(NC_FILE_INFO_T* h5) +{ + hid_t fapl = -1, file_id = -1; /* HDF5 identifiers */ + unsigned file_open_flags = 0;/* Flags for hdf5 open */ + char file_name[64]; /* Filename buffer */ + size_t alloc_incr; /* Buffer allocation increment */ + size_t min_incr = 65536; /* Minimum buffer increment */ + double buf_prcnt = 0.1f; /* Percentage of buffer size to set + as increment */ + unsigned imageflags; + int create = 0; + H5LT_file_image_ud_t *udata = NULL; /* Pointer to udata structure */ + + H5FD_file_image_callbacks_t callbacks = {&local_image_malloc, &local_image_memcpy, + &local_image_realloc, &local_image_free, + &local_udata_copy, &local_udata_free, + (void *)NULL}; + static long file_name_counter; + + imageflags = h5->mem.imageflags; + create = h5->mem.created; + + /* check arguments */ + if (h5->mem.memio.memory == NULL) { + if(create) { + if(h5->mem.memio.size == 0) h5->mem.memio.size = DEFAULT_CREATE_MEMSIZE; + h5->mem.memio.memory = malloc(h5->mem.memio.size); + } else + goto out; /* open requires an input buffer */ + } else if(h5->mem.memio.size == 0) + goto out; + + /* Create FAPL to transmit file image */ + if ((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0) + goto out; + + /* set allocation increment to a percentage of the supplied buffer size, or + * a pre-defined minimum increment value, whichever is larger + */ + if ((buf_prcnt * h5->mem.memio.size) > min_incr) + alloc_incr = (size_t)(buf_prcnt * h5->mem.memio.size); + else + alloc_incr = min_incr; + + /* Configure FAPL to use the core file driver */ + if (H5Pset_fapl_core(fapl, alloc_incr, FALSE) < 0) + goto out; + + /* Set callbacks for file image ops always */ + { + /* Allocate buffer to communicate user data to callbacks */ + if (NULL == (udata = (H5LT_file_image_ud_t *)calloc(1,sizeof(H5LT_file_image_ud_t)))) + goto out; + + /* Initialize udata with info about app buffer containing file image and flags */ + udata->app_image_ptr = h5->mem.memio.memory; + udata->app_image_size = h5->mem.memio.size; + h5->mem.memio.memory = NULL; /* move control */ + h5->mem.memio.size = 0; + udata->fapl_image_ptr = NULL; + udata->fapl_image_size = 0; + udata->fapl_ref_count = 0; + udata->vfd_image_ptr = NULL; + udata->vfd_image_size = 0; + udata->vfd_ref_count = 0; + udata->flags = imageflags; + udata->ref_count = 1; /* corresponding to the first FAPL */ + udata->h5 = h5; + + /* copy address of udata into callbacks */ + callbacks.udata = (void *)udata; + /* Set file image callbacks */ + if (H5Pset_file_image_callbacks(fapl, &callbacks) < 0) + goto out; + } + + /* Assign file image in user buffer to FAPL */ + if (H5Pset_file_image(fapl, udata->app_image_ptr, udata->app_image_size) < 0) + goto out; + + /* define a unique file name */ + snprintf(file_name, (sizeof(file_name) - 1), "file_image_%ld", file_name_counter++); + + /* set file open/create flags */ + if(create) + file_open_flags = H5F_ACC_TRUNC; /* H5Fcreate does not like H5F_ACC_RDWR */ + else if (imageflags & H5LT_FILE_IMAGE_OPEN_RW) + file_open_flags = H5F_ACC_RDWR; + else + file_open_flags = H5F_ACC_RDONLY; + + /* Assign file image in FAPL to the core file driver */ + if(create) { + if ((file_id = H5Fcreate(file_name, file_open_flags, H5P_DEFAULT, fapl)) < 0) + goto out; + } else { + if ((file_id = H5Fopen(file_name, file_open_flags, fapl)) < 0) + goto out; + } + + /* Maintain a backward link */ + h5->mem.udata = (void*)udata; + udata = NULL; + +done: + /* Reclaim the fapl object */ + H5E_BEGIN_TRY { + if(fapl >= 0) + H5Pclose(fapl); + } H5E_END_TRY; + /* Return file identifier */ + return file_id; + +out: + /* free up udata only on error */ + if(udata != NULL) free(udata); + file_id = -1; + goto done; +} /* end H5LTopen_file_image() */ + +void +NC4_image_finalize(void* _udata) +{ + if(_udata != NULL) { + H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t*)_udata; + /* checks reference counts before deallocating udata */ +#if 0 + assert(udata->ref_count == 1 && udata->fapl_ref_count == 0 && udata->vfd_ref_count == 0); + if(udata->app_image_ptr != NULL) free(udata->app_image_ptr); + if(udata->fapl_image_ptr != NULL) free(udata->fapl_image_ptr); + if(udata->vfd_image_ptr != NULL) free(udata->vfd_image_ptr); +#endif + free(udata); +#ifdef TRACE_UDATA + fprintf(stderr,"\t>>>> freed: udata=%p\n",udata); +#endif + } +} + +int +NC4_extract_file_image(NC_FILE_INFO_T* h5) +{ + int stat = NC_NOERR; + H5LT_file_image_ud_t *udata; + + udata = (H5LT_file_image_ud_t *)h5->mem.udata; + assert(udata != NULL); + + /* Fill in h5->mem.memio from udata */ + h5->mem.memio.memory = udata->vfd_image_ptr; + h5->mem.memio.size = udata->vfd_image_size; + + /* Move control */ + udata->vfd_image_ptr = NULL; + udata->vfd_image_size = 0; + + return stat; +} + +#ifdef TRACE + +static char* +printudata(H5LT_file_image_ud_t* udata) +{ + char buf[8192]; + char tmp[8192]; + char* flags = ""; + + buf[0] = '\0'; + if(udata == NULL) return strdup(""); + strlcat(buf,"flags=",sizeof(buf)); + flags = traceflags(udata->flags); + strlcat(buf,flags,sizeof(buf)); + if(flags != NULL) free(flags); + snprintf(tmp,sizeof(tmp)," ref_count=%d",udata->ref_count); + strlcat(buf,tmp,sizeof(tmp)); + snprintf(tmp,sizeof(tmp)," app=(%p,%lld)",udata->app_image_ptr,(long long)udata->app_image_size); + strlcat(buf,tmp,sizeof(tmp)); + snprintf(tmp,sizeof(tmp)," fapl=(%p,%lld)[%d]",udata->fapl_image_ptr,(long long)udata->fapl_image_size,udata->fapl_ref_count); + strlcat(buf,tmp,sizeof(tmp)); + snprintf(tmp,sizeof(tmp)," vfd=(%p,%lld)[%d]",udata->vfd_image_ptr,(long long)udata->vfd_image_size,udata->vfd_ref_count); + strlcat(buf,tmp,sizeof(tmp)); + return strdup(buf); +} + +static void +trace(const char* fcn, H5FD_file_image_op_t op, void* _udata, ...) +{ + H5LT_file_image_ud_t *udata = NULL; + va_list ap; + char buf[16000]; + char tmp[8192]; + char* ud; + + va_start(ap, _udata); /* Requires the last fixed parameter (to get the address) */ + udata = (H5LT_file_image_ud_t *)_udata; + buf[0] = '\0'; + snprintf(tmp,sizeof(tmp),"trace [ %s: op=%s: ",fcn,traceop(op)); + strlcat(buf,tmp,sizeof(tmp)); + if(strcmp("malloc",fcn)==0) { + size_t size = va_arg(ap,size_t); + snprintf(tmp,sizeof(tmp),"size=%lld",(long long)size); + } else if(strcmp("realloc",fcn)==0) { + void* ptr = va_arg(ap,void*); + size_t size = va_arg(ap,size_t); + snprintf(tmp,sizeof(tmp),"ptr=%p, size=%lld",ptr,(long long)size); + } else if(strcmp("free",fcn)==0) { + void* ptr = va_arg(ap,void*); + snprintf(tmp,sizeof(tmp),"ptr=%p",ptr); + } else if(strcmp("memcpy",fcn)==0) { + void* dest = va_arg(ap,void*); + void* src = va_arg(ap,void*); + size_t size = va_arg(ap,size_t); + snprintf(tmp,sizeof(tmp),"dest=%p, src=%p, size=%lld",dest,src,(long long)size); + } else if(strcmp("udata_copy",fcn)==0) { +#ifdef TRACE_UDATA + snprintf(tmp,sizeof(tmp),"udata=%p",udata); +#endif + } else if(strcmp("udata_free",fcn)==0) { +#ifdef TRACE_UDATA + snprintf(tmp,sizeof(tmp),"udata=%p",udata); +#endif + } else { + snprintf(tmp,sizeof(tmp),"unknown fcn: %s",fcn); + } + strlcat(buf,tmp,sizeof(buf)); + ud = printudata(udata); + strlcat(buf,"\n\tudata=",sizeof(buf)); + strlcat(buf,ud,sizeof(tmp)); + free(ud); + strlcat(buf,"\n",sizeof(buf)); + va_end(ap); + fprintf(stderr,"%s",buf); + fflush(stderr); +} + +static void +traceend(const char* fcn, void* _udata, uintptr_t retval) +{ + char buf[16000]; + char tmp[8192]; + char* ud; + const char* tab = " "; + + buf[0] = '\0'; + H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata; + snprintf(tmp,sizeof(tmp),"%s]: retval=%p",tab,(void*)retval); + strlcat(buf,tmp,sizeof(buf)); + strlcat(buf," udata=",sizeof(buf)); + ud = printudata(udata); + strlcat(buf,ud,sizeof(tmp)); + free(ud); + strlcat(buf,"\n",sizeof(buf)); + fprintf(stderr,"%s",buf); + fflush(stderr); +} + +#endif /*TRACE*/ + +#ifdef CATCH +static void +tracefail(const char* fcn) +{ + fprintf(stderr,"fail: %s\n",fcn); + fflush(stderr); +} +#endif /*CATCH*/ + +#ifdef TRACE +static char* +traceflags(int flags) +{ + int i; + char buf[8192]; + char tmp[8192]; + buf[0] = '\0'; + for(i=0;i<16;i++) { + tmp[0] = '\0'; + if((flags & 1<<i) == 0) continue; + if(i > 0) strlcat(tmp,"|",sizeof(tmp)); + switch(1<<i) { + case H5LT_FILE_IMAGE_OPEN_RW: /* 0x0001 Open image for read-write */ + strlcat(tmp,"OPEN_RW",sizeof(tmp)); + break; + case H5LT_FILE_IMAGE_DONT_COPY: /*0x0002 the HDF5 lib won't copy */ + strlcat(tmp,"DONT_COPY",sizeof(tmp)); + break; + case H5LT_FILE_IMAGE_DONT_RELEASE: /* 0x0004 The HDF5 lib won't + deallocate user supplied image + buffer. The user application + is reponsible for doing so. */ + strlcat(tmp,"DONT_RELEASE",sizeof(tmp)); + break; + default: break; + } + strlcat(buf,tmp,sizeof(buf)); + } + return strdup(buf); +} + +static const char* +traceop(H5FD_file_image_op_t op) +{ + const char* sop = NULL; + switch ( op ) { + case H5FD_FILE_IMAGE_OP_NO_OP: + sop = "NO_OP"; break; + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_SET: + sop = "PROPERTY_LIST_SET"; break; + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_COPY: + sop = "PROPERTY_LIST_COPY"; break; + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_GET: + sop = "PROPERTY_LIST_GET"; break; + case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_CLOSE: + sop = "PROPERTY_LIST_CLOSE"; break; + case H5FD_FILE_IMAGE_OP_FILE_OPEN: + sop = "FILE_OPEN"; break; + case H5FD_FILE_IMAGE_OP_FILE_RESIZE: + sop = "FILE_RESIZE"; break; + case H5FD_FILE_IMAGE_OP_FILE_CLOSE: + sop = "FILE_CLOSE"; break; + default: break; + } + return sop; +} + +#endif +