re: esupport MQO-415619

and https://github.com/Unidata/netcdf-c/issues/708

Expand the NC_INMEMORY capabilities to support writing and accessing
the final modified memory.

Three new functions have been added:
nc_open_memio, nc_create_mem, and nc_close_memio.

The following new capabilities were added.
1. nc_open_memio() allows the NC_WRITE mode flag
   so a chunk of memory can be passed in and be modified
2. nc_create_mem() allows the NC_INMEMORY flag to be set
   to cause the created file to be kept in memory.
3. nc_close_mem() allows the final in-memory contents to be
   retrieved at the time the file is closed.
4. A special flag, NC_MEMIO_LOCK, is provided to ensure that
   the provided memory will not be freed or reallocated.

Note the following.
1. If nc_open_memio() is called with NC_WRITE, and NC_MEMIO_LOCK is not set,
   then the netcdf-c library will take control of the incoming memory.
   This means that the original memory block should not be freed
   but the block returned by nc_close_mem() must be freed.
2. If nc_open_memio() is called with NC_WRITE, and NC_MEMIO_LOCK is set,
   then modifications to the original memory may fail if the space available
   is insufficient.

Documentation is provided in the file docs/inmemory.md.
A test case is provided: nc_test/tst_inmemory.c driven by
nc_test/run_inmemory.sh

WARNING: changes were made to the dispatch table for
the close entry. From int (*close)(int) to int (*close)(int,void*).
This commit is contained in:
Dennis Heimbigner 2018-02-25 21:45:31 -07:00
parent fc6ab9881f
commit ccc70d640b
42 changed files with 2755 additions and 314 deletions

2
cf
View File

@ -105,7 +105,7 @@ FLAGS="$FLAGS --enable-extreme-numbers"
#FLAGS="$FLAGS --disable-testsets"
#FLAGS="$FLAGS --disable-dap-remote-tests"
#FLAGS="$FLAGS --enable-dap-auth-tests" -- requires a new remotetest server
#FLAGS="$FLAGS --enable-doxygen --enable-internal-docs"
FLAGS="$FLAGS --enable-doxygen --enable-internal-docs"
FLAGS="$FLAGS --enable-logging"
#FLAGS="$FLAGS --disable-diskless"
#FLAGS="$FLAGS --enable-mmap"

View File

@ -799,7 +799,7 @@ fi
# Setup the diskless and mmap conditionals
if test "x$enable_diskless" = xyes ; then
AC_DEFINE([USE_DISKLESS], [1], [if true, include NC_DISKLESS code])
AC_DEFINE([USE_DISKLESS], [1], [if true, include NC_DISKLESS and NC_INMEMORY code])
if test "x$enable_mmap" = xyes; then
AC_DEFINE([USE_MMAP], [1], [if true, use mmap for in-memory files])
fi

View File

@ -74,6 +74,15 @@ IF(ENABLE_DOXYGEN)
ENDIF(ENABLE_DOXYGEN)
SET(CUR_EXTRA_DIST ${CUR_EXTRA_DIST} CMakeLists.txt Makefile.am netcdf.m4 DoxygenLayout.xml Doxyfile.in Doxyfile.guide.in footer.html mainpage.dox tutorial.dox guide.dox types.dox notes.md cdl.dox architecture.dox internal.dox install-fortran.dox Doxyfile.in.cmake windows-binaries.md building-with-cmake.md install.md)
SET(CUR_EXTRA_DIST ${CUR_EXTRA_DIST}
netcdf.m4 DoxygenLayout.xml Doxyfile.in footer.html
mainpage.dox tutorial.dox guide.dox types.dox cdl.dox
architecture.dox internal.dox windows-binaries.md
building-with-cmake.md CMakeLists.txt groups.dox install.md notes.md
install-fortran.md all-error-codes.md credits.md auth.md
obsolete/fan_utils.html bestpractices.md filters.md inmemory.md
DAP4.dox OPeNDAP.dox attribute_conventions.md FAQ.md
file_format_specifications.md known_problems.md
COPYRIGHT.dox)
ADD_EXTRA_DIST("${CUR_EXTRA_DIST}")

View File

@ -750,23 +750,23 @@ INPUT = \
@abs_top_srcdir@/docs/install-fortran.md \
@abs_top_srcdir@/docs/types.dox \
@abs_top_srcdir@/docs/internal.dox \
@abs_top_srcdir@/docs/indexing.dox \
@abs_top_srcdir@/docs/windows-binaries.md \
@abs_top_srcdir@/docs/guide.dox \
@abs_top_srcdir@/docs/OPeNDAP.dox \
@abs_top_srcdir@/docs/DAP4.dox \
@abs_top_srcdir@/docs/attribute_conventions.md \
@abs_top_srcdir@/docs/file_format_specifications.md \
@abs_top_srcdir@/docs/tutorial.dox \
@abs_top_srcdir@/docs/notes.md \
@abs_top_srcdir@/docs/auth.md \
@abs_top_srcdir@/docs/filters.md \
@abs_top_srcdir@/docs/inmemory.md \
@abs_top_srcdir@/docs/notes.md \
@abs_top_srcdir@/docs/all-error-codes.md \
@abs_top_srcdir@/docs/FAQ.md \
@abs_top_srcdir@/docs/known_problems.md \
@abs_top_srcdir@/docs/COPYRIGHT.dox \
@abs_top_srcdir@/docs/credits.md \
@abs_top_srcdir@/docs/bestpractices.md \
@abs_top_srcdir@/docs/tutorial.dox \
@abs_top_srcdir@/include/netcdf.h \
@abs_top_srcdir@/include/netcdf_mem.h \
@abs_top_srcdir@/include/netcdf_par.h \

View File

@ -9,7 +9,10 @@ mainpage.dox tutorial.dox guide.dox types.dox cdl.dox \
architecture.dox internal.dox windows-binaries.md \
building-with-cmake.md CMakeLists.txt groups.dox install.md notes.md \
install-fortran.md all-error-codes.md credits.md auth.md \
obsolete/fan_utils.html bestpractices.md filters.md indexing.dox
obsolete/fan_utils.html bestpractices.md filters.md inmemory.md \
DAP4.dox OPeNDAP.dox attribute_conventions.md FAQ.md \
file_format_specifications.md known_problems.md \
COPYRIGHT.dox
# Turn off parallel builds in this directory.
.NOTPARALLEL:

View File

@ -4,10 +4,6 @@ netCDF Authorization Support
# netCDF Authorization Support {#Header}
__Author__: Dennis Heimbigner<br>
__Initial Version__: 11/21/2014<br>
__Last Revised__: 08/24/2017
[TOC]
## Introduction {#Introduction}
@ -515,3 +511,11 @@ what you changed to the author so this document can be updated.
yes | keytool -trustcacerts -storepass "$PWD" -v -keystore ./$TRUSTSTORE -alias $alias -importcert -file "${c}"
done
exit
## Point of Contact
__Author__: Dennis Heimbigner<br>
__Email__: dmh at ucar dot edu
__Initial Version__: 11/21/2014<br>
__Last Revised__: 08/24/2017

View File

@ -1,8 +1,8 @@
Filter Support in netCDF-4 (Enhanced)
NetCDF-4 Filter Support
============================
<!-- double header is needed to workaround doxygen bug -->
Filter Support in netCDF-4 (Enhanced) {#compress}
NetCDF-4 Filter Support {#compress}
=================================
[TOC]
@ -411,10 +411,19 @@ static const unsigned char b[4] = {0x0,0x0,0x0,0x1}; /* value 1 in big-endian*/
int endianness = (1 == *(unsigned int*)b); /* 1=>big 0=>little endian
````
References {#References}
References {#Filter_References}
==========
1. https://support.hdfgroup.org/HDF5/doc/Advanced/DynamicallyLoadedFilters/HDF5DynamicallyLoadedFilters.pdf
2. https://support.hdfgroup.org/HDF5/doc/TechNotes/TechNote-HDF5-CompressionTroubleshooting.pdf
3. https://support.hdfgroup.org/services/filters.html
4. https://support.hdfgroup.org/services/contributions.html#filters
Point of Contact
================
__Author__: Dennis Heimbigner<br>
__Email__: dmh at ucar dot edu
__Initial Version__: 1/10/2018<br>
__Last Revised__: 2/5/2018

215
docs/inmemory.md Normal file
View File

@ -0,0 +1,215 @@
NetCDF In-Memory Support
====================================
<!-- double header is needed to workaround doxygen bug -->
NetCDF In-Memory Support {#inmemory}
====================================
[TOC]
Introduction {#inmemory_intro}
--------------
It can be convenient to operate on a netcdf file whose
content is held in memory instead of in a disk file.
The netcdf API has been modified in a number of ways
to support this capability.
Actually, three distinct but related capabilities are provided.
1. DISKLESS -- Read a file into memory, operate on it, and optionally
write it back out to disk when nc_close() is called.
2. INMEMORY -- Tell the netcdf-c library to treat a provided block
of memory as if it were a netcdf file. At close, it is possible to ask
for the final contents of the memory chunk. Be warned that there is
some complexity to this as described below.
4. MMAP -- Tell the netcdf-c library to use the *mmap()* operating
system functionality to access a file.
The first two capabilities are intertwined in the sense that the *diskless*
capability makes use internally of the *inmemory* capability. But, the
*inmemory* capability can be used independently of the *diskless* capability.
The *mmap()* capability provides a capability similar to *diskless* but
using special capabilities of the underlying operating system.
Note also that *diskless* and *inmemory* can be used for both
*netcdf-3* (classic) and *netcdf-4* (enhanced) data. The *mmap*
capability can only be used with *netcdf-3*.
Enabling Diskless File Access {#Enable_Diskless}
--------------
The *diskless* capability can be used relatively transparently
using the *NC_DISKLESS* mode flag.
Note that since the file is stored in memory, size limitations apply.
If you are on using a 32-bit pointer then the file size must be less than 2^32
bytes in length. On a 64-bit machine, the size must be less than 2^64 bytes.
### Diskless File Open
Calling *nc_open()* using the mode flag *NC_DISKLESS* will cause
the file being opened to be read into memory. When calling *nc_close()*,
the file will optionally be re-written (aka "persisted") to disk. This
persist capability will be invoked if and only if *NC_WRITE* is specified
in the mode flags at the call to *nc_open()*.
### Diskless File Create
Calling *nc_create()* using the mode flag *NC_DISKLESS* will cause
the file to initially be created and kept in memory.
When calling *nc_close()*, the file will be written
to disk.
Note that if it is desired to create the file in memory,
but not write to a disk file, then one can either set
the NC_NOCLOBBER mode flag or one can call *nc_abort()*
instead of *nc_close()*.
Enabling Inmemory File Access {#Enable_Inmemory}
--------------
The netcdf API has been extended to support the inmemory capability.
The relevant API is defined in the file `netcdf_mem.h`.
The important data structure to use is `NC_memio`.
````
typedef struct NC_memio {
size_t size;
void* memory;
int flags;
} NC_memio;
````
An instance of this data structure is used when providing or
retrieving a block of data. It specifies the memory and its size
and also some relevant flags that define how to manage the memory.
Current only one flag is defined -- *NC_MEMIO_LOCKED*.
This tells the netcdf library that it should never try to
*realloc()* the memory nor to *free()* the memory. Note
that this does not mean that the memory cannot be modified, but
only that the modifications will be within the confines of the provided
memory. If doing such modifications is impossible without
reallocating the memory, then the modification will fail.
### In-Memory API
The new API consists of the following functions.
````
int nc_open_mem(const char* path, int mode, size_t size, void* memory, int* ncidp);
int nc_create_mem(const char* path, int mode, size_t initialsize, int* ncidp);
int nc_open_memio(const char* path, int mode, NC_memio* info, int* ncidp);
int nc_close_memio(int ncid, NC_memio* info);
````
### The **nc_open_mem** Function
The *nc_open_mem()* function is actually a convenience
function that internally invokes *nc_open_memio()*.
It essentially provides simple read-only access to a chunk of memory
of some specified size.
### The **nc_open_memio** Function
This function provides a more general read/write capability with respect
to a chunk of memory. It has a number of constraints and its
semantics are somewhat complex. This is primarily due to limitations
imposed by the underlying HDF5 library.
The constraints are as follows.
1. If the *NC_MEMIO_LOCKED* flag is set, then the netcdf library will
make no attempt to reallocate or free the provided memory.
If the caller invokes the *nc_close_memio()* function to retrieve the
final memory block, it should be the same
memory block as was provided when *nc_open_memio* was called.
Note that it is still possible to modify the in-memory file if the NC_WRITE
mode flag was set. However, failures can occur if an operation
cannot complete because the memory needs to be expanded.
2. If the *NC_MEMIO_LOCKED* flag is <b>not</b> set, then
the netcdf library will take control of the incoming memory
and will feel free to reallocate the provided
memory block to obtain a larger block when an attempt to modify
the in-memory file requires more space. Note that implicit in this
is that the old block -- the one originally provided -- may be
free'd as a side effect of re-allocating the memory using the
*realloc()* function.
If the caller invokes the *nc_close_memio()* function to retrieve the
final memory block, the returned block must always be freed
by the caller and that the original block should not be freed.
### The **nc_create_mem** Function
This function allows a user to create an in-memory file, write to it,
and then retrieve the final memory using *nc_close_memio()*.
The *initialsize* argument to *nc_create_mem()* tells the library
how much initial memory to allocate. Technically, this is advisory only
because it may be ignored by the underlying HDF5 library.
It is used, however, for netcdf-3 files.
### The **nc_close_memio** Function
The ordinary *nc_close()* function can be called to close an in-memory file.
However, it is often desirable to obtain the final size and memory block
for the in-memory file when that file has been modified.
The *nc_close_memio()* function provides a means to do this.
Its second argument is a pointer to an *NC_memio* object
into which the final memory and size are stored. WARNING,
the returned memory is owned by the caller and so the caller
is responsible for calling *free()* on that returned memory.
### Support for Writing with *NC_MEMIO_LOCKED*
When the NC_MEMIO_LOCKED flag is set in the *NC_memio* object
passed to *nc_open_memio()*, it is still possible to modify
the opened in-memory file (using the NC_WRITE mode flag).
The big problem is that any changes must fit into the memory provided
by the caller via the *NC_memio* object. This problem can be
mitigated, however, by using the "trick" of overallocating
the caller supplied memory. That is, if the original file is, say, 300 bytes,
then it is possible to allocate, say, 65000 bytes and copy the original file
into the first 300 bytes of the larger memory block. This will allow
the netcdf-c library to add to the file up to that 65000 byte limit.
In this way, it is possible to avoid memory reallocation while still
allowing modifications to the file. You will still need to call
*nc_close_memio()* to obtain the size of the final, modified, file.
Enabling MMAP File Access {#Enable_MMAP}
--------------
Some operating systems provide a capability called MMAP.
This allows disk files to automatically be mapped to chunks of memory.
It operates in a fashion somewhat similar to operating system virtual
memory, except with respect to a file.
By setting mode flag NC_MMAP, it is possible to do the equivalent
of NC_DISKLESS but using the operating system's mmap capabilities.
Currently, MMAP support is only available when using netcdf-3 or cdf5
files.
Known Bugs {#Inmemory_Bugs}
--------------
1. If you are modifying a locked memory chunk (using
NC_MEMIO_LOCKED) and are accessing it as a netcdf-4 file, and
you overrun the available space, then the HDF5 library will
fail with a segmentation fault.
References {#Inmemory_References}
--------------
1. https://support.hdfgroup.org/HDF5/doc1.8/Advanced/FileImageOperations/HDF5FileImageOperations.pdf
Point of Contact
--------------
__Author__: Dennis Heimbigner<br>
__Email__: dmh at ucar dot edu
__Initial Version__: 2/3/2018<br>
__Last Revised__: 2/5/2018

View File

@ -95,6 +95,9 @@
<li>
<a href="#Gfdnavi" >Gfdnavi (Geophysical fluid data navigator)</a>
</li>
<li>
<a href="#gliderscope" >Gliderscope</a>
</li>
<li>
<a href="#GMT">GMT (Generic Mapping Tools)</a>
</li>
@ -200,6 +203,9 @@
<li>
<a href="#ncvtk" >ncvtk</a>
</li>
<li>
<a href="#netcdf_ninja" >NetCDF Ninja</a>
</li>
<li>
<a href="#netcdf_tools" >netcdf tools</a>
</li>
@ -927,6 +933,36 @@ or using ECMWF reanalysis on a reduced grid
</p>
<p></p>
<h2><a id="gliderscope" name="gliderscope">Gliderccope</a></h2>
<p>
Dr L. Mun Woo <mun.woo@uwa.edu.au>
at ANFOG (Australian National Facility for Ocean Gliders)
has developed
<a href="http://imos.org.au/facilities/oceangliders/glider-data/gliderscope/">Gliderscope</a>.
Gliderscope is an IMOS (Integrated Marine Observing System)
oceanographic software package allow users quick easy visualisation
of ocean glider data, via a convenient graphical user interface.
Originally developed for use with ANFOG NetCDF data, it has now
been expanded to handle NetCDF data files from IOOS (U.S.
Integrated Ocean Observing System) and EGO (Everyone's Gliding
Observatories) also.
</p><p>
Being interactive, Gliderscope speaks to users via an onscreen dialogue
box, helping the user decide what to do. With a few simple clicks of
the mouse, users can choose and extract segments of data, filter out the
bad data, perform calculations (e.g. for water density, sound velocity
in water, light attenuation, 1% photic depth) and apply a variety of
high level graphical data visualisation techniques to produce elegant
three/four-dimensional plots of water properties, interpolated contour
charts, vertical profile plots, water properties comparison charts
etc. Additionally, users can also export their data to text or NetCDF
files for easy access in other applications. Gliderscope is available
on Windows and Macintosh platforms, as standalone executable software
as well as an App for use within Matlab.
</p>
<p></p>
<h2><a id="GMT" name="GMT">GMT</a></h2>
<p>
<a href="http://gmt.soest.hawaii.edu/">GMT</a> (Generic Mapping Tools) is
@ -1965,6 +2001,21 @@ or using ECMWF reanalysis on a reduced grid
<p></p>
<h2><a id="netcdf_ninja" name="netcdf_ninja">netCDF Ninja</a></h2>
<p>
Dr L. Mun Woo of University of Western Australia <mun.woo@uwa.edu.au>
has developed
<a href="http://imos.org.au/facilities/oceangliders/glider-data/netcdfninja/">NetCDF Ninja</a>,
a graphical user interface that allows users to browse all
the metadata contained in NetCDF files, scrutinise the data using an
interactive graphical plot and even make small alterations or export
the data in text format without having any knowledge of coding. NetCDF
Ninja is available on Windows and Macintosh platforms, as standalone
executable software as well as an App for use within Matlab.
</p>
<p></p>
<h2><a id="netcdf_tools" name="netcdf_tools">Ivan Shmakov's netcdf tools</a></h2>
<p>

View File

@ -24,5 +24,5 @@ libbzip2_la_SOURCES = ${DLLSRC}
libbzip2_la_LDFLAGS = -module -avoid-version -shared -export-dynamic -no-undefined
endif #ENABLE_FILTER_TESTING
EXTRA_DIST = CMakeLists.txt H5Zbzip2.c Makefile.am blocksort.c bzlib.c bzlib.h bzlib_private.h compress.c \
crctable.c decompress.c h5bzip2.h huffman.c randtable.c bzip2.nc
EXTRA_DIST = CMakeLists.txt

View File

@ -80,7 +80,7 @@ extern int
NC3_abort(int ncid);
extern int
NC3_close(int ncid);
NC3_close(int ncid,void*);
extern int
NC3_set_fill(int ncid, int fillmode, int *old_modep);

View File

@ -44,7 +44,7 @@ extern int
NC4_abort(int ncid);
extern int
NC4_close(int ncid);
NC4_close(int ncid,void*);
extern int
NC4_set_fill(int ncid, int fillmode, int *old_modep);

View File

@ -20,6 +20,7 @@
#include "ncdimscale.h"
#include "nc_logging.h"
#include "netcdf_mem.h"
#ifdef USE_PARALLEL
#include "netcdf_par.h"
@ -332,9 +333,17 @@ typedef struct NC_HDF5_FILE_INFO
int sdid;
#endif /* USE_HDF4 */
struct NCFILEINFO* fileinfo;
struct NC4_Memio {
NC_memio memio;
int locked; /* do not copy and do not release */
int persist; /* Should file be persisted out on close? */
int inmemory;
int diskless;
unsigned int flags; /* for H5LTopen_file_image */
int fapl;
} mem;
} NC_HDF5_FILE_INFO_T;
/* Defined in lookup3.c */
extern uint32_t hash_fast(const void *key, size_t length);

View File

@ -105,11 +105,6 @@ typedef struct NC_MPI_INFO {
MPI_Info info;
} NC_MPI_INFO;
typedef struct NC_MEM_INFO {
size_t size;
void* memory;
} NC_MEM_INFO;
/* Define known dispatch tables and initializers */
/*Forward*/
@ -211,7 +206,7 @@ int (*redef)(int);
int (*_enddef)(int,size_t,size_t,size_t,size_t);
int (*sync)(int);
int (*abort)(int);
int (*close)(int);
int (*close)(int,void*);
int (*set_fill)(int,int,int*);
int (*inq_base_pe)(int,int*);
int (*set_base_pe)(int,int);

View File

@ -154,7 +154,7 @@ Use this in mode flags for both nc_create() and nc_open(). */
Use this in mode flags for both nc_create() and nc_open(). */
#define NC_MPIPOSIX 0x4000 /**< \deprecated As of libhdf5 1.8.13. */
#define NC_INMEMORY 0x8000 /**< Read from memory. Mode flag for nc_open() or nc_create(). */
#define NC_INMEMORY 0x8000 /**< Read from memory. Mode flag for nc_open() or nc_create() => NC_DISKLESS */
#define NC_PNETCDF (NC_MPIIO) /**< Use parallel-netcdf library; alias for NC_MPIIO. */
@ -459,7 +459,8 @@ by the desired type. */
#define NC_EFILTER (-132) /**< Filter operation failed. */
#define NC_ERCFILE (-133) /**< RC file failure */
#define NC_ENULLPAD (-134) /**< Header Bytes not Null-Byte padded */
#define NC4_LAST_ERROR (-135) /**< @internal All netCDF errors > this. */
#define NC_EINMEMORY (-135) /**< In-memory file error */
#define NC4_LAST_ERROR (-136) /**< @internal All netCDF errors > this. */
/** @internal This is used in netCDF-4 files for dimensions without
* coordinate vars. */

View File

@ -14,12 +14,30 @@
#include <netcdf.h>
typedef struct NC_memio {
size_t size;
void* memory;
#define NC_MEMIO_LOCKED 1 /* Do not try to realloc or free provided memory */
int flags;
} NC_memio;
#if defined(__cplusplus)
extern "C" {
#endif
/* Treate a memory block as a file; read-only */
EXTERNL int nc_open_mem(const char* path, int mode, size_t size, void* memory, int* ncidp);
EXTERNL int nc_create_mem(const char* path, int mode, size_t initialsize, int* ncidp);
/* Alternative to nc_open_mem with extended capabilites
See docs/inmemory.md
*/
EXTERNL int nc_open_memio(const char* path, int mode, NC_memio* info, int* ncidp);
/* Close memory file and return the final memory state */
EXTERNL int nc_close_memio(int ncid, NC_memio* info);
#if defined(__cplusplus)
}
#endif

View File

@ -225,7 +225,7 @@ NCD2_sync(int ncid)
static int
NCD2_abort(int ncid)
{
return NCD2_close(ncid);
return NCD2_close(ncid,NULL);
}
static int
@ -596,13 +596,13 @@ fprintf(stderr,"ncdap3: final constraint: %s\n",dapcomm->oc.url->query);
return ncstat;
done:
if(drno != NULL) NCD2_close(drno->ext_ncid);
if(drno != NULL) NCD2_close(drno->ext_ncid,NULL);
if(ocstat != OC_NOERR) ncstat = ocerrtoncerr(ocstat);
return THROW(ncstat);
}
int
NCD2_close(int ncid)
NCD2_close(int ncid, void* ignore)
{
NC* drno;
NCDAPCOMMON* dapcomm;

View File

@ -50,7 +50,7 @@ NCD2_open(const char *path, int mode,
struct NC_Dispatch* dispatch, NC* ncp);
extern int
NCD2_close(int ncid);
NCD2_close(int ncid,void*);
extern int
NCD2_inq_format_extended(int ncid, int* formatp, int* modep);

View File

@ -245,7 +245,7 @@ done:
}
int
NCD4_close(int ncid)
NCD4_close(int ncid, void* ignore)
{
int ret = NC_NOERR;
NC* nc;
@ -278,7 +278,7 @@ done:
int
NCD4_abort(int ncid)
{
return NCD4_close(ncid);
return NCD4_close(ncid,NULL);
}
/**************************************************/

View File

@ -21,7 +21,7 @@ NCD4_open(const char *path, int mode,
struct NC_Dispatch* dispatch, NC* ncp);
extern int
NCD4_close(int ncid);
NCD4_close(int ncid,void*);
extern int
NCD4_abort(int ncid);

View File

@ -262,9 +262,11 @@ const char *nc_strerror(int ncerr1)
case NC_EMPI: return "NetCDF: MPI operation failed.";
case NC_ERCFILE:
return "NetCDF: RC File Failure.";
case NC_ENULLPAD:
return "NetCDF: File fails strict Null-Byte Header check.";
default:
case NC_ENULLPAD:
return "NetCDF: File fails strict Null-Byte Header check.";
case NC_EINMEMORY:
return "NetCDF: In-memory File operation failed.";
default:
#ifdef USE_PNETCDF
/* The behavior of ncmpi_strerror here is to return
NULL, not a string. This causes problems in (at least)

View File

@ -176,10 +176,10 @@ NC_check_file_type(const char *path, int flags, void *parameters,
int status = NC_NOERR;
int diskless = ((flags & NC_DISKLESS) == NC_DISKLESS);
int inmemory = (!diskless && ((flags & NC_INMEMORY) == NC_INMEMORY));
#ifdef USE_PARALLEL
int use_parallel = ((flags & NC_MPIIO) == NC_MPIIO);
#endif /* USE_PARALLEL */
int inmemory = (diskless && ((flags & NC_INMEMORY) == NC_INMEMORY));
struct MagicFile file;
*model = 0;
@ -189,7 +189,7 @@ NC_check_file_type(const char *path, int flags, void *parameters,
file.path = path; /* do not free */
file.parameters = parameters;
if(inmemory && parameters == NULL)
{status = NC_EDISKLESS; goto done;}
{status = NC_EINMEMORY; goto done;}
if(inmemory) {
file.inmemory = inmemory;
goto next;
@ -258,8 +258,8 @@ and attributes.
NC_64BIT_DATA (Alias NC_CDF5) (create CDF-5 file),
NC_NETCDF4 (create netCDF-4/HDF5 file),
NC_CLASSIC_MODEL (enforce netCDF classic mode on netCDF-4/HDF5 files),
NC_DISKLESS (store data only in memory),
NC_MMAP (use MMAP for NC_DISKLESS),
NC_DISKLESS (store data in memory),
NC_MMAP (use MMAP for NC_DISKLESS instead of NC_INMEMORY),
and NC_WRITE.
See discussion below.
@ -273,7 +273,10 @@ aspects of how it may be used.
Setting NC_NOCLOBBER means you do not want to clobber (overwrite) an
existing dataset; an error (NC_EEXIST) is returned if the specified
dataset already exists.
dataset already exists. As a slight variation on this, if you
specify NC_DISKLESS and NC_NOCLOBBER, the file will be created
in-memory, but no attempt will be made to persiste the in-memory
data to a disk file.
The NC_SHARE flag is appropriate when one process may be writing the
dataset and one or more other processes reading the dataset
@ -523,6 +526,58 @@ nc__create(const char *path, int cmode, size_t initialsz,
chunksizehintp, 0, NULL, ncidp);
}
/** \ingroup datasets
Create a netCDF file with the contents stored in memory.
\param path Must be non-null, but otherwise only used to set the dataset name.
\param mode the mode flags; Note that this procedure uses a limited set of flags because it forcibly sets NC_INMEMORY.
\param initialsize (advisory) size to allocate for the created file
\param ncidp Pointer to location where returned netCDF ID is to be
stored.
\returns ::NC_NOERR No error.
\returns ::NC_ENOMEM Out of memory.
\returns ::NC_EDISKLESS diskless io is not enabled for fails.
\returns ::NC_EINVAL, etc. other errors also returned by nc_open.
<h1>Examples</h1>
In this example we use nc_create_mem() to create a classic netCDF dataset
named foo.nc. The initial size is set to 4096.
@code
#include <netcdf.h>
...
int status = NC_NOERR;
int ncid;
int mode = 0;
size_t initialsize = 4096;
...
status = nc_create_mem("foo.nc", mode, initialsize, &ncid);
if (status != NC_NOERR) handle_error(status);
@endcode
*/
int
nc_create_mem(const char* path, int mode, size_t initialsize, int* ncidp)
{
#ifdef USE_DISKLESS
if(mode & (NC_MPIIO|NC_MPIPOSIX|NC_MMAP))
return NC_EINVAL;
mode |= (NC_INMEMORY|NC_NOCLOBBER); /* Specifically, do not set NC_DISKLESS */
return NC_create(path, mode, initialsize, 0, NULL, 0, NULL, ncidp);
#else
return NC_EDISKLESS;
#endif
}
/**
* @internal Create a file with special (deprecated) Cray settings.
*
@ -735,7 +790,7 @@ Open a netCDF file with the contents taken from a block of memory.
\param path Must be non-null, but otherwise only used to set the dataset name.
\param mode the mode flags; Note that this procedure uses a limited set of flags because it forcibly sets NC_NOWRITE|NC_DISKLESS|NC_INMEMORY.
\param mode the mode flags; Note that this procedure uses a limited set of flags because it forcibly sets NC_INMEMORY.
\param size The length of the block of memory being passed.
@ -779,22 +834,89 @@ int
nc_open_mem(const char* path, int mode, size_t size, void* memory, int* ncidp)
{
#ifdef USE_DISKLESS
NC_MEM_INFO meminfo;
NC_memio meminfo;
/* Sanity checks */
if(memory == NULL || size < MAGIC_NUMBER_LEN || path == NULL)
return NC_EINVAL;
if(mode & (NC_WRITE|NC_MPIIO|NC_MPIPOSIX|NC_MMAP))
return NC_EINVAL;
mode |= (NC_INMEMORY|NC_DISKLESS);
mode |= (NC_INMEMORY); /* DO not set NC_DISKLESS */
meminfo.size = size;
meminfo.memory = memory;
meminfo.flags = NC_MEMIO_LOCKED;
return NC_open(path, mode, 0, NULL, 0, &meminfo, ncidp);
#else
return NC_EDISKLESS;
#endif
}
/** \ingroup datasets
Open a netCDF file with the contents taken from a block of memory.
Similar to nc_open_mem, but with parameters. Warning: if you do
specify that the provided memory is locked, then <b>never</b>
pass in non-heap allocated memory. Additionally, if not locked,
then do not assume that the memory returned by nc_close_mem
is the same as passed to nc_open_memio. You <b>must</b> check
before attempting to free the original memory.
\param path Must be non-null, but otherwise only used to set the dataset name.
\param mode the mode flags; Note that this procedure uses a limited set of flags because it forcibly sets NC_INMEMORY.
\param params controlling parameters
\param ncidp Pointer to location where returned netCDF ID is to be
stored.
\returns ::NC_NOERR No error.
\returns ::NC_ENOMEM Out of memory.
\returns ::NC_EDISKLESS diskless io is not enabled for fails.
\returns ::NC_EINVAL, etc. other errors also returned by nc_open.
<h1>Examples</h1>
Here is an example using nc_open_memio() to open an existing netCDF dataset
named foo.nc for read-only, non-shared access. It differs from the nc_open_mem()
example in that it uses a parameter block.
@code
#include <netcdf.h>
#include <netcdf_mem.h>
...
int status = NC_NOERR;
int ncid;
NC_memio params;
...
params.size = <compute file size of foo.nc in bytes>;
params.memory = malloc(size);
params.flags = <see netcdf_mem.h>
...
status = nc_open_memio("foo.nc", 0, &params, &ncid);
if (status != NC_NOERR) handle_error(status);
@endcode
*/
int
nc_open_memio(const char* path, int mode, NC_memio* params, int* ncidp)
{
#ifdef USE_DISKLESS
/* Sanity checks */
if(path == NULL || params == NULL)
return NC_EINVAL;
if(params->memory == NULL || params->size < MAGIC_NUMBER_LEN)
return NC_EINVAL;
if(mode & (NC_MPIIO|NC_MPIPOSIX|NC_MMAP))
return NC_EINVAL;
mode |= (NC_INMEMORY);
return NC_open(path, mode, 0, NULL, 0, params, ncidp);
#else
return NC_EINMEMORY;
#endif
}
/**
* @internal Open a netCDF file with extra parameters for Cray.
*
@ -1258,8 +1380,7 @@ nc_close(int ncid)
if(ncp->refcount <= 0)
#endif
{
stat = ncp->dispatch->close(ncid);
stat = ncp->dispatch->close(ncid,NULL);
/* Remove from the nc list */
if (!stat)
{
@ -1270,6 +1391,75 @@ nc_close(int ncid)
return stat;
}
/** \ingroup datasets
Do a normal close (see nc_close()) on an in-memory dataset,
then return a copy of the final memory contents of the dataset.
\param ncid NetCDF ID, from a previous call to nc_open() or nc_create().
\param memio a pointer to an NC_memio object into which the final valid memory
size and memory will be returned.
\returns ::NC_NOERR No error.
\returns ::NC_EBADID Invalid id passed.
\returns ::NC_ENOMEM Out of memory.
\returns ::NC_EDISKLESS if the file was not created as an inmemory file.
\returns ::NC_EBADGRPID ncid did not contain the root group id of this
file. (NetCDF-4 only).
<h1>Example</h1>
Here is an example using nc_close_mem to finish the definitions of a new
netCDF dataset named foo.nc, return the final memory,
and release its netCDF ID:
\code
#include <netcdf.h>
...
int status = NC_NOERR;
int ncid;
NC_memio finalmem;
size_t initialsize = 65000;
...
status = nc_create_mem("foo.nc", NC_NOCLOBBER, initialsize, &ncid);
if (status != NC_NOERR) handle_error(status);
... create dimensions, variables, attributes
status = nc_close_memio(ncid,&finalmem);
if (status != NC_NOERR) handle_error(status);
\endcode
*/
int
nc_close_memio(int ncid, NC_memio* memio)
{
#ifdef USE_DISKLESS
NC* ncp;
int stat = NC_check_id(ncid, &ncp);
if(stat != NC_NOERR) return stat;
#ifdef USE_REFCOUNT
ncp->refcount--;
if(ncp->refcount <= 0)
#endif
{
stat = ncp->dispatch->close(ncid,memio);
/* Remove from the nc list */
if (!stat)
{
del_from_NCList(ncp);
free_NC(ncp);
}
}
return stat;
#else
return NC_EINMEMORY;
#endif
}
/** \ingroup datasets
Change the fill-value mode to improve write performance.
@ -1769,7 +1959,7 @@ NC_create(const char *path0, int cmode, size_t initialsz,
}
#ifndef USE_DISKLESS
cmode &= (~ NC_DISKLESS); /* Force off */
cmode &= (~ (NC_DISKLESS|NC_INMEMORY)); /* Force off */
#endif
#ifdef WINPATH
@ -1959,13 +2149,12 @@ NC_open(const char *path0, int cmode, int basepe, size_t *chunksizehintp,
#ifndef USE_DISKLESS
/* Clean up cmode */
cmode &= (~ NC_DISKLESS);
cmode &= (~ (NC_DISKLESS|NC_INMEMORY));
#endif
inmemory = ((cmode & NC_INMEMORY) == NC_INMEMORY);
diskless = ((cmode & NC_DISKLESS) == NC_DISKLESS);
#ifdef WINPATH
path = NCpathcvt(path0);
#else
@ -2190,7 +2379,7 @@ openmagic(struct MagicFile* file)
int status = NC_NOERR;
if(file->inmemory) {
/* Get its length */
NC_MEM_INFO* meminfo = (NC_MEM_INFO*)file->parameters;
NC_memio* meminfo = (NC_memio*)file->parameters;
file->filelen = (long long)meminfo->size;
goto done;
}
@ -2255,7 +2444,7 @@ readmagic(struct MagicFile* file, long pos, char* magic)
memset(magic,0,MAGIC_NUMBER_LEN);
if(file->inmemory) {
char* mempos;
NC_MEM_INFO* meminfo = (NC_MEM_INFO*)file->parameters;
NC_memio* meminfo = (NC_memio*)file->parameters;
if((pos + MAGIC_NUMBER_LEN) > meminfo->size)
{status = NC_EDISKLESS; goto done;}
mempos = ((char*)meminfo->memory) + pos;

View File

@ -169,9 +169,9 @@ NC_readfile(const char* filename, NCbytes* content)
char part[1024];
#ifdef _MSC_VER
stream = NCfopen(filename,"r");
#else
stream = NCfopen(filename,"rb");
#else
stream = NCfopen(filename,"r");
#endif
if(stream == NULL) {ret=errno; goto done;}
for(;;) {

View File

@ -426,7 +426,7 @@ NC_inq_recvar(int ncid, int varid, int* nrecdimsp, int *is_recdim)
* Find the length of a type. This is how much space is required by
* the in memory to hold one element of this type.
*
* @parm type A netCDF atomic type.
* @param type A netCDF atomic type.
*
* @return Length of the type in bytes, or -1 if type not found.
* @author Ed Hartnett

View File

@ -2,40 +2,35 @@
* Copyright 1996, University Corporation for Atmospheric Research
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*/
#if defined (_WIN32) || defined (_WIN64)
#include <windows.h>
#include <winbase.h>
#include <io.h>
#define lseek64 lseek
#endif
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef _MSC_VER /* Microsoft Compilers */
#include <io.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef _MSC_VER /* Microsoft Compilers */
#include <windows.h>
#include <winbase.h>
#include <io.h>
#define access(path,mode) _access(path,mode)
#endif
#include "ncdispatch.h"
#include "nc3internal.h"
#include "netcdf_mem.h"
#include "ncwinpath.h"
#undef DEBUG
#ifdef DEBUG
#include <stdio.h>
#endif
#ifndef HAVE_SSIZE_T
typedef int ssize_t;
#endif
@ -77,15 +72,33 @@ typedef int ssize_t;
#undef X_ALIGN
#endif
#undef REALLOCBUG
#ifdef REALLOCBUG
/* There is some kind of realloc bug that I cannot solve yet */
#define reallocx(m,new,old) realloc(m,new)
#else
static void*
reallocx(void* mem, size_t newsize, size_t oldsize)
{
void* m = malloc(newsize);
memcpy(m,mem,oldsize);
return m;
}
#endif
/* Private data for memio */
typedef struct NCMEMIO {
int locked; /* => we cannot realloc */
int locked; /* => we cannot realloc or free*/
int modified; /* => we realloc'd memory at least once */
int persist; /* => save to a file; triggered by NC_WRITE */
char* memory;
off_t alloc;
off_t size;
off_t pos;
size_t alloc;
size_t size;
size_t pos;
/* Convenience flags */
int diskless;
int inmemory; /* assert(inmemory iff !diskless */
} NCMEMIO;
/* Forward */
@ -96,25 +109,28 @@ static int memio_sync(ncio *const nciop);
static int memio_filesize(ncio* nciop, off_t* filesizep);
static int memio_pad_length(ncio* nciop, off_t length);
static int memio_close(ncio* nciop, int);
static int readfile(const char* path, NC_memio*);
static int writefile(const char* path, NCMEMIO*);
static int fileiswriteable(const char* path);
/* Mnemonic */
#define DOOPEN 1
static long pagesize = 0;
static size_t pagesize = 0;
/*! Create a new ncio struct to hold info about the file. */
static int memio_new(const char* path, int ioflags, off_t initialsize, void* memory, ncio** nciopp, NCMEMIO** memiop)
static int
memio_new(const char* path, int ioflags, off_t initialsize, ncio** nciopp, NCMEMIO** memiop)
{
int status = NC_NOERR;
ncio* nciop = NULL;
NCMEMIO* memio = NULL;
off_t minsize = initialsize;
int inmemory = (fIsSet(ioflags,NC_INMEMORY));
size_t minsize = (size_t)initialsize;
/* use asserts because this is an internal function */
assert(fIsSet(ioflags,NC_INMEMORY));
assert(memiop != NULL && nciopp != NULL);
assert(path != NULL || (memory != NULL && initialsize > 0));
assert(!inmemory || (memory != NULL && initialsize > 0));
assert(path != NULL);
if(pagesize == 0) {
#if defined (_WIN32) || defined(_WIN64)
@ -122,7 +138,7 @@ static int memio_new(const char* path, int ioflags, off_t initialsize, void* mem
GetSystemInfo (&info);
pagesize = info.dwPageSize;
#elif defined HAVE_SYSCONF
pagesize = sysconf(_SC_PAGE_SIZE);
pagesize = (size_t)sysconf(_SC_PAGE_SIZE);
#elif defined HAVE_GETPAGESIZE
pagesize = getpagesize();
#else
@ -165,24 +181,25 @@ static int memio_new(const char* path, int ioflags, off_t initialsize, void* mem
*((char**)&nciop->path) = strdup(path);
if(nciop->path == NULL) {status = NC_ENOMEM; goto fail;}
memio->alloc = initialsize;
memio->pos = 0;
memio->size = minsize;
memio->memory = NULL;
memio->persist = fIsSet(ioflags,NC_WRITE);
if(memiop && memio) *memiop = memio; else free(memio);
if(nciopp && nciop) *nciopp = nciop;
else {
if(nciop->path != NULL) free((char*)nciop->path);
free(nciop);
}
if(inmemory) {
memio->memory = memory;
} else {
/* malloc memory */
memio->memory = (char*)malloc(memio->alloc);
if(memio->memory == NULL) {status = NC_ENOMEM; goto fail;}
}
memio->alloc = (size_t)initialsize;
memio->pos = 0;
memio->size = minsize;
memio->memory = NULL; /* filled in by caller */
if(fIsSet(ioflags,NC_DISKLESS))
memio->diskless = 1;
if(fIsSet(ioflags,NC_INMEMORY) && !memio->diskless)
memio->inmemory = 1;
if(fIsSet(ioflags,NC_WRITE) && !fIsSet(ioflags,NC_NOCLOBBER) && memio->diskless)
memio->persist = 1;
done:
return status;
@ -214,41 +231,31 @@ int
memio_create(const char* path, int ioflags,
size_t initialsz,
off_t igeto, size_t igetsz, size_t* sizehintp,
void* parameters,
void* parameters /*ignored*/,
ncio* *nciopp, void** const mempp)
{
ncio* nciop;
int fd;
int status;
NCMEMIO* memio = NULL;
int persist = (ioflags & NC_WRITE?1:0);
int oflags;
if(path == NULL ||* path == 0)
return NC_EINVAL;
status = memio_new(path, ioflags, initialsz, NULL, &nciop, &memio);
status = memio_new(path, ioflags, initialsz, &nciop, &memio);
if(status != NC_NOERR)
return status;
if(persist) {
/* Open the file just tomake sure we can write it if needed */
oflags = (persist ? O_RDWR : O_RDONLY);
#ifdef O_BINARY
fSet(oflags, O_BINARY);
#endif
oflags |= (O_CREAT|O_TRUNC);
if(fIsSet(ioflags,NC_NOCLOBBER))
oflags |= O_EXCL;
#ifdef vms
fd = open(path, oflags, 0, "ctx=stm");
#else
fd = open(path, oflags, OPENMODE);
#endif
if(fd < 0) {status = errno; goto unwind_open;}
if(memio->persist) {
/* Verify the file is writeable */
if(!fileiswriteable(path))
{status = EPERM; goto unwind_open;}
}
(void)close(fd); /* will reopen at nc_close */
} /*!persist*/
/* Allocate the memory for this file */
memio->memory = (char*)malloc((size_t)memio->alloc);
if(memio->memory == NULL) {status = NC_ENOMEM; goto unwind_open;}
memio->locked = 0;
#ifdef DEBUG
fprintf(stderr,"memio_create: initial memory: %lu/%lu\n",(unsigned long)memio->memory,(unsigned long)memio->alloc);
@ -270,7 +277,7 @@ fprintf(stderr,"memio_create: initial memory: %lu/%lu\n",(unsigned long)memio->m
}
/* Pick a default sizehint */
if(sizehintp) *sizehintp = pagesize;
if(sizehintp) *sizehintp = (size_t)pagesize;
*nciopp = nciop;
return NC_NOERR;
@ -280,8 +287,7 @@ unwind_open:
return status;
}
/* This function opens the data file.
/* This function opens the data file or inmemory data
path - path of data file.
ioflags - flags passed into nc_open.
igeto - looks like this function can do an initial page get, and
@ -305,85 +311,75 @@ memio_open(const char* path,
ncio* nciop = NULL;
int fd = -1;
int status = NC_NOERR;
int persist = (fIsSet(ioflags,NC_WRITE)?1:0);
int inmemory = (fIsSet(ioflags,NC_INMEMORY));
int oflags = 0;
NCMEMIO* memio = NULL;
size_t sizehint = 0;
off_t filesize = 0;
off_t red = 0;
char* pos = NULL;
NC_MEM_INFO* meminfo = (NC_MEM_INFO*)parameters;
NC_memio meminfo; /* use struct to avoid worrying about free'ing it */
NCMEMIO* memio = NULL;
size_t initialsize;
/* Should be the case that diskless => inmemory but not converse */
int diskless = (fIsSet(ioflags,NC_DISKLESS));
int inmemory = (fIsSet(ioflags,NC_INMEMORY) && !diskless);
int locked = 0;
if(path == NULL || strlen(path) == 0)
return NC_EINVAL;
assert(sizehintp != NULL);
sizehint = *sizehintp;
if(inmemory) {
filesize = meminfo->size;
} else {
/* Open the file,and make sure we can write it if needed */
oflags = (persist ? O_RDWR : O_RDONLY);
#ifdef O_BINARY
fSet(oflags, O_BINARY);
#endif
oflags |= O_EXCL;
#ifdef vms
fd = open(path, oflags, 0, "ctx=stm");
#else
fd = open(path, oflags, OPENMODE);
#endif
#ifdef DEBUG
if(fd < 0) {
fprintf(stderr,"open failed: file=%s err=",path);
perror("");
}
#endif
if(fd < 0) {status = errno; goto unwind_open;}
/* get current filesize = max(|file|,initialize)*/
filesize = lseek(fd,0,SEEK_END);
if(filesize < 0) {status = errno; goto unwind_open;}
/* move pointer back to beginning of file */
(void)lseek(fd,0,SEEK_SET);
if(filesize < (off_t)sizehint)
filesize = (off_t)sizehint;
if(inmemory) { /* parameters provide the memory chunk */
NC_memio* memparams = (NC_memio*)parameters;
meminfo = *memparams;
locked = fIsSet(meminfo.flags,NC_MEMIO_LOCKED);
/* As a safeguard, if !locked and NC_WRITE is set,
then we must take control of the incoming memory */
if(!locked && fIsSet(ioflags,NC_WRITE)) {
memparams->memory = NULL;
}
} else { /* read the file into a chunk of memory*/
assert(diskless);
status = readfile(path,&meminfo);
if(status != NC_NOERR)
{goto unwind_open;}
}
if(inmemory)
status = memio_new(path, ioflags, filesize, meminfo->memory, &nciop, &memio);
else
status = memio_new(path, ioflags, filesize, NULL, &nciop, &memio);
if(status != NC_NOERR) {
if(fd >= 0)
close(fd);
return status;
/* Fix up initial size */
initialsize = meminfo.size;
/* create the NCMEMIO structure */
status = memio_new(path, ioflags, initialsize, &nciop, &memio);
if(status != NC_NOERR)
{goto unwind_open;}
memio->locked = locked;
/* Initialize the memio memory */
memio->memory = meminfo.memory;
/* memio_new may have modified the allocated size, in which case,
reallocate the memory unless the memory is locked. */
if(memio->alloc > meminfo.size) {
if(memio->locked)
memio->alloc = meminfo.size; /* force it back to what it was */
else {
void* oldmem = memio->memory;
memio->memory = reallocx(oldmem,memio->alloc,meminfo.size);
if(memio->memory == NULL)
{status = NC_ENOMEM; goto unwind_open;}
}
}
#ifdef DEBUG
fprintf(stderr,"memio_open: initial memory: %lu/%lu\n",(unsigned long)memio->memory,(unsigned long)memio->alloc);
#endif
if(!inmemory) {
/* Read the file into the memio memory */
/* We need to do multiple reads because there is no
guarantee that the amount read will be the full amount */
red = memio->size;
pos = memio->memory;
while(red > 0) {
ssize_t count = read(fd, pos, red);
if(count < 0) {status = errno; goto unwind_open;}
if(count == 0) {status = NC_ENOTNC; goto unwind_open;}
red -= count;
pos += count;
}
(void)close(fd);
if(memio->persist) {
/* Verify the file is writeable */
if(!fileiswriteable(path))
{status = EPERM; goto unwind_open;}
}
/* Use half the filesize as the blocksize ; why? */
sizehint = filesize/2;
sizehint = (size_t)(memio->alloc/2);
/* sizehint must be multiple of 8 */
sizehint = (sizehint / 8) * 8;
@ -437,26 +433,34 @@ static int
memio_pad_length(ncio* nciop, off_t length)
{
NCMEMIO* memio;
size_t len = (size_t)length;
if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
memio = (NCMEMIO*)nciop->pvt;
if(!fIsSet(nciop->ioflags, NC_WRITE))
if(!memio->persist)
return EPERM; /* attempt to write readonly file*/
if(memio->locked > 0)
if(memio->locked)
return NC_EDISKLESS;
if(length > memio->alloc) {
if(len > memio->alloc) {
/* Realloc the allocated memory to a multiple of the pagesize*/
off_t newsize = length;
size_t newsize = (size_t)len;
void* newmem = NULL;
/* Round to a multiple of pagesize */
if((newsize % pagesize) != 0)
newsize += (pagesize - (newsize % pagesize));
newmem = (char*)realloc(memio->memory,newsize);
newmem = (char*)reallocx(memio->memory,newsize,memio->alloc);
if(newmem == NULL) return NC_ENOMEM;
/* If not copy is set, then fail if the newmem address is different
from old address */
if(newmem != memio->memory) {
memio->modified++;
if(memio->locked) {
free(newmem);
return NC_EINMEMORY;
}
}
/* zero out the extra memory */
memset((void*)((char*)newmem+memio->alloc),0,(newsize - memio->alloc));
@ -465,10 +469,13 @@ fprintf(stderr,"realloc: %lu/%lu -> %lu/%lu\n",
(unsigned long)memio->memory,(unsigned long)memio->alloc,
(unsigned long)newmem,(unsigned long)newsize);
#endif
if(memio->memory != NULL && (!memio->locked || memio->modified))
free(memio->memory);
memio->memory = newmem;
memio->alloc = newsize;
memio->modified = 1;
}
memio->size = length;
memio->size = len;
return NC_NOERR;
}
@ -486,46 +493,23 @@ memio_close(ncio* nciop, int doUnlink)
{
int status = NC_NOERR;
NCMEMIO* memio ;
int fd = -1;
int inmemory = 0;
if(nciop == NULL || nciop->pvt == NULL) return NC_NOERR;
inmemory = (fIsSet(nciop->ioflags,NC_INMEMORY));
memio = (NCMEMIO*)nciop->pvt;
assert(memio != NULL);
/* See if the user wants the contents persisted to a file */
if(!inmemory && memio->persist) {
/* Try to open the file for writing */
int oflags = O_WRONLY|O_CREAT|O_TRUNC;
#ifdef O_BINARY
fSet(oflags, O_BINARY);
#endif
fd = open(nciop->path, oflags, OPENMODE);
if(fd >= 0) {
/* We need to do multiple writes because there is no
guarantee that the amount written will be the full amount */
off_t written = memio->size;
char* pos = memio->memory;
while(written > 0) {
ssize_t count = write(fd, pos, written);
if(count < 0)
{status = errno; goto done;}
if(count == 0)
{status = NC_ENOTNC; goto done;}
written -= count;
pos += count;
}
} else
status = errno;
}
if(memio->persist && memio->memory != NULL) {
status = writefile(nciop->path,memio);
}
done:
if(!inmemory && memio->memory != NULL)
/* We only free the memio memory if file is not locked or has been modified */
if(memio->memory != NULL && (!memio->locked || memio->modified)) {
free(memio->memory);
memio->memory = NULL;
}
/* do cleanup */
if(fd >= 0) (void)close(fd);
if(memio != NULL) free(memio);
if(nciop->path != NULL) free((char*)nciop->path);
free(nciop);
@ -533,9 +517,10 @@ done:
}
static int
guarantee(ncio* nciop, off_t endpoint)
guarantee(ncio* nciop, off_t endpoint0)
{
NCMEMIO* memio = (NCMEMIO*)nciop->pvt;
size_t endpoint = (size_t)endpoint0;
if(endpoint > memio->alloc) {
/* extend the allocated memory and size */
int status = memio_pad_length(nciop,endpoint);
@ -637,3 +622,117 @@ memio_sync(ncio* const nciop)
{
return NC_NOERR; /* do nothing */
}
/* "Hidden" Internal function to extract a copy of
the size and/or contents of the memory
*/
int
memio_extract(ncio* const nciop, size_t* sizep, void** memoryp)
{
int status = NC_NOERR;
NCMEMIO* memio = NULL;
if(nciop == NULL || nciop->pvt == NULL) return NC_NOERR;
memio = (NCMEMIO*)nciop->pvt;
assert(memio != NULL);
if(sizep) *sizep = memio->size;
if(memoryp && memio->memory != NULL) {
*memoryp = memio->memory;
memio->memory = NULL; /* make sure it does not get free'd */
}
return status;
}
static int
fileiswriteable(const char* path)
{
int ok;
ok = access(path,O_RDWR);
if(ok < 0)
return 0;
return 1;
}
/* Read contents of a disk file into a memory chunk */
static int
readfile(const char* path, NC_memio* memio)
{
int status = NC_NOERR;
FILE* f = NULL;
size_t filesize = 0;
size_t count = 0;
char* memory = NULL;
char* p = NULL;
/* Open the file for reading */
#ifdef _MSC_VER
f = NCfopen(path,"rb");
#else
f = NCfopen(path,"r");
#endif
if(f == NULL)
{status = errno; goto done;}
/* get current filesize */
if(fseek(f,0,SEEK_END) < 0)
{status = errno; goto done;}
filesize = (size_t)ftell(f);
/* allocate memory */
memory = malloc((size_t)filesize);
if(memory == NULL)
{status = NC_ENOMEM; goto done;}
/* move pointer back to beginning of file */
rewind(f);
count = filesize;
p = memory;
while(count > 0) {
size_t actual;
actual = fread(p,1,count,f);
if(actual == 0 || ferror(f))
{status = NC_EIO; goto done;}
count -= actual;
p += actual;
}
if(memio) {
memio->size = (size_t)filesize;
memio->memory = memory;
}
done:
if(status != NC_NOERR && memory != NULL)
free(memory);
if(f != NULL) fclose(f);
return status;
}
/* write contents of a memory chunk back into a disk file */
static int
writefile(const char* path, NCMEMIO* memio)
{
int status = NC_NOERR;
FILE* f = NULL;
size_t count = 0;
char* p = NULL;
/* Open the file for writing*/
#ifdef _MSC_VER
f = NCfopen(path,"rwb");
#else
f = NCfopen(path,"rw");
#endif
if(f == NULL)
{status = errno; goto done;}
rewind(f);
count = memio->size;
p = memio->memory;
while(count > 0) {
size_t actual;
actual = fwrite(p,1,count,f);
if(actual == 0 || ferror(f))
{status = NC_EIO; goto done;}
count -= actual;
p += actual;
}
done:
if(f != NULL) fclose(f);
return status;
}

View File

@ -19,6 +19,7 @@
#endif
#include "nc3internal.h"
#include "netcdf_mem.h"
#include "rnd.h"
#include "ncx.h"
@ -35,6 +36,10 @@
/* For cdf5 */
#define NC_NUMRECS_EXTENT5 8
/* Internal function; breaks ncio abstraction */
extern int memio_extract(ncio* const nciop, size_t* sizep, void** memoryp);
static void
free_NC3INFO(NC3_INFO *nc3)
{
@ -1311,7 +1316,7 @@ NC3_abort(int ncid)
}
int
NC3_close(int ncid)
NC3_close(int ncid, void* params)
{
int status = NC_NOERR;
NC *nc;
@ -1359,6 +1364,12 @@ NC3_close(int ncid)
}
}
if(params != NULL && (nc->mode & NC_INMEMORY) != 0) {
NC_memio* memio = (NC_memio*)params;
/* Extract the final memory size &/or contents */
status = memio_extract(nc3->nciop,&memio->size,&memio->memory);
}
(void) ncio_close(nc3->nciop, 0);
nc3->nciop = NULL;

View File

@ -46,9 +46,9 @@ ncio_create(const char *path, int ioflags, size_t initialsz,
ncio** iopp, void** const mempp)
{
#ifdef USE_DISKLESS
if(fIsSet(ioflags,NC_DISKLESS)) {
if(fIsSet(ioflags,NC_INMEMORY)) {
# ifdef USE_MMAP
if(fIsSet(ioflags,NC_MMAP))
if(fIsSet(ioflags,NC_MMAP) && fIsSet(ioflags, NC_DISKLESS))
return mmapio_create(path,ioflags,initialsz,igeto,igetsz,sizehintp,parameters,iopp,mempp);
else
# endif /*USE_MMAP*/
@ -72,12 +72,12 @@ ncio_open(const char *path, int ioflags,
ncio** iopp, void** const mempp)
{
/* Diskless open has the following constraints:
1. file must be classic version 1 or 2
1. file must be classic version 1 or 2 or 5
*/
#ifdef USE_DISKLESS
if(fIsSet(ioflags,NC_DISKLESS)) {
if(fIsSet(ioflags,NC_INMEMORY)) {
# ifdef USE_MMAP
if(fIsSet(ioflags,NC_MMAP))
if(fIsSet(ioflags,NC_MMAP) && fIsSet(ioflags, NC_DISKLESS))
return mmapio_open(path,ioflags,igeto,igetsz,sizehintp,parameters,iopp,mempp);
else
# endif /*USE_MMAP*/

View File

@ -1,6 +1,6 @@
# Process these files with m4.
SET(libsrc4_SOURCES nc4dispatch.c nc4attr.c nc4dim.c nc4file.c nc4grp.c nc4type.c nc4var.c ncfunc.c nc4internal.c nc4hdf.c nc4info.c)
SET(libsrc4_SOURCES nc4dispatch.c nc4attr.c nc4dim.c nc4file.c nc4grp.c nc4type.c nc4var.c ncfunc.c nc4internal.c nc4hdf.c nc4info.c nc4mem.c nc4memcb.c)
IF(LOGGING)
SET(libsrc4_SOURCES ${libsrc4_SOURCES} error4.c)

View File

@ -12,7 +12,7 @@ libnetcdf4_la_CPPFLAGS = ${AM_CPPFLAGS}
noinst_LTLIBRARIES = libnetcdf4.la
libnetcdf4_la_SOURCES = nc4dispatch.c nc4attr.c nc4dim.c nc4file.c \
nc4grp.c nc4hdf.c nc4internal.c nc4type.c nc4var.c ncfunc.c error4.c \
nc4info.c nc4printer.c
nc4info.c nc4printer.c nc4mem.c nc4memcb.c
EXTRA_DIST = CMakeLists.txt

View File

@ -590,7 +590,7 @@ exit:
* @param file_type Type of the attribute data in file.
* @param len Number of elements in attribute array.
* @param data Attribute data.
* @param memtype Type of data in memory.
* @param mem_type Type of data in memory.
*
* @return ::NC_NOERR No error.
* @return ::NC_EINVAL Invalid parameters.

View File

@ -16,10 +16,21 @@
#include "nc.h"
#include "nc4internal.h"
#include "nc4dispatch.h"
#include <H5DSpublic.h> /* must be after nc4internal.h */
#include <H5Fpublic.h>
#include "netcdf_mem.h"
#ifdef USE_HDF4
#include <mfhdf.h>
#endif
#include <hdf5_hl.h>
#define DEFAULT_CREATE_MEMSIZE ((size_t)1<<16)
extern int nc4_vararray_add(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var);
/* From nc4mem.c */
extern int NC4_open_image_file(NC_HDF5_FILE_INFO_T* h5);
extern int NC4_create_image_file(NC_HDF5_FILE_INFO_T* h5);
extern int NC4_extract_file_image(NC_HDF5_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
@ -457,7 +468,7 @@ exit:
static const int ILLEGAL_OPEN_FLAGS = (NC_MMAP|NC_64BIT_OFFSET);
/** @internal These flags may not be set for create. */
static const int ILLEGAL_CREATE_FLAGS = (NC_NOWRITE|NC_MMAP|NC_INMEMORY|NC_64BIT_OFFSET|NC_CDF5);
static const int ILLEGAL_CREATE_FLAGS = (NC_NOWRITE|NC_MMAP|NC_64BIT_OFFSET|NC_CDF5);
extern void reportopenobjects(int log, hid_t);
@ -551,12 +562,13 @@ sync_netcdf4_file(NC_HDF5_FILE_INFO_T *h5)
*
* @param h5 Pointer to HDF5 file info struct.
* @param abort True if this is an abort.
* @param extractmem True if we need to extract and save final inmemory
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett
*/
static int
close_netcdf4_file(NC_HDF5_FILE_INFO_T *h5, int abort)
close_netcdf4_file(NC_HDF5_FILE_INFO_T *h5, int abort, int extractmem)
{
int retval = NC_NOERR;
@ -589,18 +601,28 @@ close_netcdf4_file(NC_HDF5_FILE_INFO_T *h5, int abort)
MPI_Info_free(&h5->info);
}
#endif
if(h5->fileinfo) free(h5->fileinfo);
if (H5Fclose(h5->hdfid) < 0)
{
int nobjs;
nobjs = H5Fget_obj_count(h5->hdfid, H5F_OBJ_ALL);
/* Apparently we can get an error even when nobjs == 0 */
if(nobjs < 0) {
BAIL_QUIET(NC_EHDFERR);
} else if(nobjs > 0) {
if(h5->fileinfo) free(h5->fileinfo);
/* Check to see if this is an in-memory file and we want to get its
final content
*/
if(extractmem) {
/* File must be read/write */
if(!h5->no_write) {
retval = NC4_extract_file_image(h5);
}
}
if (H5Fclose(h5->hdfid) < 0)
{
int nobjs;
nobjs = H5Fget_obj_count(h5->hdfid, H5F_OBJ_ALL);
/* Apparently we can get an error even when nobjs == 0 */
if(nobjs < 0) {
BAIL_QUIET(NC_EHDFERR);
} else if(nobjs > 0) {
#ifdef LOGGING
char msg[1024];
int logit = 1;
@ -826,8 +848,7 @@ nc4typelen(nc_type type)
*
* @param path The file name of the new file.
* @param cmode The creation mode flag.
* @param comm MPI communicator (parallel IO only).
* @param info MPI info (parallel IO only).
* @param parameters extra parameter info (like MPI communicator)
* @param nc Pointer to an instance of NC.
*
* @return ::NC_NOERR No error.
@ -838,25 +859,43 @@ nc4typelen(nc_type type)
* @author Ed Hartnett, Dennis Heimbigner
*/
static int
nc4_create_file(const char *path, int cmode, MPI_Comm comm, MPI_Info info,
NC *nc)
nc4_create_file(const char *path, int cmode, void* parameters, NC *nc)
{
hid_t fcpl_id, fapl_id = -1;
unsigned flags;
FILE *fp;
int retval = NC_NOERR;
NC_HDF5_FILE_INFO_T* nc4_info = 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 */
#else /* !USE_PARALLEL4 */
int persist = 0; /* Should diskless try to persist its data into file?*/
#endif
#endif /* !USE_PARALLEL4 */
assert(nc && path);
LOG((3, "%s: path %s mode 0x%x", __func__, path, cmode));
if(cmode & NC_DISKLESS)
/* 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->mem.inmemory = (cmode & NC_INMEMORY) == NC_INMEMORY;
nc4_info->mem.diskless = (cmode & NC_DISKLESS) == NC_DISKLESS;
if(nc4_info->mem.inmemory && parameters)
nc4_info->mem.memio = *(NC_memio*)parameters;
#ifdef USE_PARALLEL4
else if(parameters) {
mpinfo = (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;
@ -864,23 +903,17 @@ nc4_create_file(const char *path, int cmode, MPI_Comm comm, MPI_Info info,
flags = H5F_ACC_TRUNC;
/* If this file already exists, and NC_NOCLOBBER is specified,
return an error. */
if (cmode & NC_DISKLESS) {
#ifndef USE_PARALLEL4
if(cmode & NC_WRITE)
persist = 1;
#endif
return an error (unless diskless|inmemory) */
if (nc4_info->mem.diskless) {
if((cmode & NC_WRITE) && (cmode & NC_NOCLOBBER) == 0)
nc4_info->mem.persist = 1;
} else if (nc4_info->mem.inmemory) {
/* ok */
} else if ((cmode & NC_NOCLOBBER) && (fp = fopen(path, "r"))) {
fclose(fp);
return NC_EEXIST;
}
/* 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);
/* 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. */
@ -892,8 +925,7 @@ nc4_create_file(const char *path, int cmode, MPI_Comm comm, MPI_Info info,
#ifdef USE_PARALLEL4
/* If this is a parallel file create, set up the file creation
property list. */
if ((cmode & NC_MPIIO) || (cmode & NC_MPIPOSIX))
{
if ((cmode & NC_MPIIO) || (cmode & NC_MPIPOSIX)) {
nc4_info->parallel = NC_TRUE;
if (cmode & NC_MPIIO) /* MPI/IO */
{
@ -935,7 +967,7 @@ nc4_create_file(const char *path, int cmode, MPI_Comm comm, MPI_Info info,
}
#else /* only set cache for non-parallel... */
if(cmode & NC_DISKLESS) {
if (H5Pset_fapl_core(fapl_id, 4096, persist))
if (H5Pset_fapl_core(fapl_id, 4096, nc4_info->mem.persist))
BAIL(NC_EDISKLESS);
}
if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size,
@ -974,7 +1006,21 @@ nc4_create_file(const char *path, int cmode, MPI_Comm comm, MPI_Info info,
H5Pset_coll_metadata_write(fapl_id, 1);
#endif
if ((nc4_info->hdfid = H5Fcreate(path, flags, fcpl_id, fapl_id)) < 0)
if(nc4_info->mem.inmemory) {
#if 0
if(nc4_info->mem.memio.size == 0)
nc4_info->memio.size = DEFAULT_CREATE_MEMSIZE; /* last ditch fix */
if(nc4_info->memio.memory == NULL) { /* last ditch fix */
nc4_info->memio.memory = malloc(nc4_info->memio.size);
if(nc4_info->memio.memory == NULL)
BAIL(NC_ENOMEM);
}
assert(nc4_info->memio.size > 0 && nc4_info->memio.memory != NULL);
#endif
retval = NC4_create_image_file(nc4_info);
if(retval)
BAIL(retval);
} else if ((nc4_info->hdfid = H5Fcreate(path, flags, fcpl_id, fapl_id)) < 0)
/*Change the return error from NC_EFILEMETADATA to
System error EACCES because that is the more likely problem */
BAIL(EACCES);
@ -1003,7 +1049,7 @@ exit: /*failure exit*/
#endif
if (fapl_id != H5P_DEFAULT) H5Pclose(fapl_id);
if(!nc4_info) return retval;
close_netcdf4_file(nc4_info,1); /* treat like abort */
close_netcdf4_file(nc4_info,1,0); /* treat like abort */
return retval;
}
@ -1031,22 +1077,12 @@ NC4_create(const char* path, int cmode, size_t initialsz, int basepe,
size_t *chunksizehintp, int use_parallel, void *parameters,
NC_Dispatch *dispatch, NC* nc_file)
{
MPI_Comm comm = MPI_COMM_WORLD;
MPI_Info info = MPI_INFO_NULL;
int res;
assert(nc_file && path);
LOG((1, "%s: path %s cmode 0x%x comm %d info %d",
__func__, path, cmode, comm, info));
#ifdef USE_PARALLEL4
if (parameters)
{
comm = ((NC_MPI_INFO *)parameters)->comm;
info = ((NC_MPI_INFO *)parameters)->info;
}
#endif /* USE_PARALLEL4 */
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)
@ -1089,7 +1125,7 @@ NC4_create(const char* path, int cmode, size_t initialsz, int basepe,
nc_file->int_ncid = nc_file->ext_ncid;
res = nc4_create_file(path, cmode, comm, info, nc_file);
res = nc4_create_file(path, cmode, parameters, nc_file);
done:
return res;
@ -2476,27 +2512,47 @@ static int
nc4_open_file(const char *path, int mode, void* parameters, NC *nc)
{
hid_t fapl_id = H5P_DEFAULT;
unsigned flags = (mode & NC_WRITE) ?
H5F_ACC_RDWR : H5F_ACC_RDONLY;
int retval;
unsigned flags;
NC_HDF5_FILE_INFO_T* nc4_info = NULL;
int inmemory = ((mode & NC_INMEMORY) == NC_INMEMORY);
NC_MEM_INFO* meminfo = (NC_MEM_INFO*)parameters;
#ifdef USE_PARALLEL4
NC_MPI_INFO* mpiinfo = (NC_MPI_INFO*)parameters;
int comm_duped = 0; /* Whether the MPI Communicator was duplicated */
int info_duped = 0; /* Whether the MPI Info object was duplicated */
#endif /* !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);
nc4_info->mem.inmemory = ((mode & NC_INMEMORY) == NC_INMEMORY);
nc4_info->mem.diskless = ((mode & NC_DISKLESS) == NC_DISKLESS);
if(nc4_info->mem.inmemory) {
NC_memio* memparams = NULL;
if(parameters == NULL)
BAIL(NC_EINMEMORY);
memparams = (NC_memio*)parameters;
nc4_info->mem.memio = *memparams; /* keep local copy */
/* As a safeguard, if !locked and NC_WRITE is set,
then we must take control of the incoming memory */
nc4_info->mem.locked = (nc4_info->mem.memio.flags & NC_MEMIO_LOCKED) == NC_MEMIO_LOCKED;
if(!nc4_info->mem.locked && ((mode & NC_WRITE) == NC_WRITE)) {
memparams->memory = NULL;
}
#ifdef USE_PARALLEL4
} else {
mpiinfo = (NC_MPI_INFO*)parameters;
#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. */
@ -2564,12 +2620,13 @@ nc4_open_file(const char *path, int mode, void* parameters, NC *nc)
#ifdef HDF5_HAS_COLL_METADATA_OPS
H5Pset_all_coll_metadata_ops(fapl_id, 1 );
#endif
if(inmemory) {
if((nc4_info->hdfid = H5LTopen_file_image(meminfo->memory,meminfo->size,
H5LT_FILE_IMAGE_DONT_COPY|H5LT_FILE_IMAGE_DONT_RELEASE
)) < 0)
if(nc4_info->mem.inmemory) {
/* validate */
if(nc4_info->mem.memio.size == 0 || nc4_info->mem.memio.memory == NULL)
BAIL(NC_INMEMORY);
retval = NC4_open_image_file(nc4_info);
if(retval)
BAIL(NC_EHDFERR);
nc4_info->no_write = NC_TRUE;
} else if ((nc4_info->hdfid = H5Fopen(path, flags, fapl_id)) < 0)
BAIL(NC_EHDFERR);
@ -2610,7 +2667,7 @@ exit:
#endif
if (fapl_id != H5P_DEFAULT) H5Pclose(fapl_id);
if (!nc4_info) return retval;
close_netcdf4_file(nc4_info,1); /* treat like abort*/
close_netcdf4_file(nc4_info,1,0); /* treat like abort*/
return retval;
}
@ -2898,7 +2955,7 @@ NC4_abort(int ncid)
/* Free any resources the netcdf-4 library has for this file's
* metadata. */
if ((retval = close_netcdf4_file(nc4_info, 1)))
if ((retval = close_netcdf4_file(nc4_info, 1, 0)))
return retval;
/* Delete the file, if we should. */
@ -2913,12 +2970,57 @@ NC4_abort(int ncid)
* @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)
NC4_close(int ncid, void* params)
{
NC_GRP_INFO_T *grp;
NC *nc;
NC_HDF5_FILE_INFO_T *h5;
int retval;
int inmemory;
LOG((1, "%s: ncid 0x%x", __func__, ncid));
/* Find our metadata for this file. */
if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
return retval;
assert(nc && h5 && grp);
/* This must be the root group. */
if (grp->parent)
return NC_EBADGRPID;
inmemory = ((h5->cmode & NC_INMEMORY) == NC_INMEMORY);
/* Call the nc4 close. */
if ((retval = close_netcdf4_file(grp->nc4_info, 0, (inmemory?1:0))))
return retval;
if(inmemory && params != NULL) {
NC_memio* memio = (NC_memio*)params;
*memio = h5->mem.memio;
}
return NC_NOERR;
}
/**
* @internal Close an in-memory netcdf file, writing any changes first.
*
* @param ncid File and group ID.
* @param sizep ptr into which the final size is stored
* @param memp ptr into which the final memory is stored
*
* @return ::NC_NOERR No error.
* @author Ed Hartnett
*/
int
NC4_close_mem(int ncid, size_t* sizep, void** memp)
{
NC_GRP_INFO_T *grp;
NC *nc;
@ -2937,10 +3039,15 @@ NC4_close(int ncid)
if (grp->parent)
return NC_EBADGRPID;
/* Call the nc4 close. */
if ((retval = close_netcdf4_file(grp->nc4_info, 0)))
/* If the file is not an in-memory file, then treat like normal close */
if((h5->cmode & NC_INMEMORY) == 0)
return NC4_close(ncid,NULL);
/* Call the nc4 close and extract memory */
if ((retval = close_netcdf4_file(grp->nc4_info, 0, 1)))
return retval;
return NC_NOERR;
}
@ -3049,4 +3156,3 @@ nc4_enddef_netcdf4_file(NC_HDF5_FILE_INFO_T *h5)
return sync_netcdf4_file(h5);
}

View File

@ -1707,6 +1707,7 @@ nc4_break_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *coord_var, NC_DIM_INFO_T
*
* @param grp The grp of the dimscale-only dataset to be deleted, or a
* higher group in the heirarchy (ex. root group).
* @param dimid dimension id
* @param dim Pointer to the dim with the dimscale-only dataset to be
* deleted.
*

317
libsrc4/nc4mem.c Normal file
View File

@ -0,0 +1,317 @@
/*********************************************************************
* 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 <stdlib.h>
#include <string.h>
#include <assert.h>
#include <hdf5.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 */
extern int NC4_open_image(NC_HDF5_FILE_INFO_T* h5);
#if 0
/* Forward */
/* not working */
static int NC4_set_realloc_callback(NC_HDF5_FILE_INFO_T*, hid_t);
#endif
int
NC4_open_image_file(NC_HDF5_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 flags */
h5->mem.flags = 0;
if(h5->mem.locked) {
h5->mem.flags |= (H5LT_FILE_IMAGE_DONT_COPY | H5LT_FILE_IMAGE_DONT_RELEASE);
}
if(!h5->no_write)
h5->mem.flags |= H5LT_FILE_IMAGE_OPEN_RW;
/* Create the file but using our version of H5LTopen_file_image */
hdfid = NC4_open_image(h5);
#if 0
hdfid = H5LTopen_file_image(h5->mem.memio.memory,h5->mem.memio.size,h5->mem.flags);
#endif
if(hdfid < 0)
{stat = NC_EHDFERR; goto done;}
#if 0
/* Override the realloc callback if necessary */
if(h5->mem.flags & H5LT_FILE_IMAGE_DONT_COPY) {
if((stat = NC4_set_realloc_callback(h5,hdfid)) != NC_NOERR)
goto done;
}
#endif
/* Return file identifier */
h5->hdfid = hdfid;
done:
return stat;
}
int
NC4_create_image_file(NC_HDF5_FILE_INFO_T* h5)
{
int stat = NC_NOERR;
NC* nc = h5->controller;
hid_t fcpl, fapl, file_id; /* HDF5 identifiers */
size_t alloc_incr; /* Buffer allocation increment */
size_t min_incr = 65536; /* Minimum buffer increment */
#if 0
double buf_prcnt = 0.1f; /* Percentage of buffer size to set
as increment */
#endif
#if 0
see if we can get by without creating an initial memory for the core driver
/* Assume that the default buffer size and memory were set by caller */
if(h5->mem.memio.memory == NULL || h5->memio.size == 0)
{stat = NC_EINVAL; goto done;}
/* set allocation increment to a percentage of the supplied buffer mem, or
* a pre-defined minimum increment value, whichever is larger
*/
if ((buf_prcnt * h5->memio.size) > min_incr)
alloc_incr = (size_t)(buf_prcnt * h5->memio.size);
else
alloc_incr = min_incr;
#else
alloc_incr = min_incr;
#endif
/* Create FAPL to establish core driver */
if ((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0)
{stat = NC_EHDFERR; goto done;}
/* Make fcpl be default for now */
fcpl = H5P_DEFAULT;
/* Configure FAPL to use the core file driver with no backing store */
if (H5Pset_fapl_core(fapl, alloc_incr, 0) < 0)
{stat = NC_EHDFERR; goto done;}
#if 0
/* Assign file image in user buffer to FAPL */
if(H5Pset_file_image(fapl, h5->memio.memory, h5->memio.size) < 0)
{stat = NC_EHDFERR; goto done;}
#endif
/* Create the file */
if((file_id = H5Fcreate(nc->path, H5F_ACC_TRUNC, fcpl, fapl)) < 0)
{stat = NC_EHDFERR; goto done;}
/* Return file identifier */
h5->hdfid = file_id;
h5->mem.fapl = fapl;
done:
if(stat != NC_NOERR) {
/* Close FAPL */
H5Pclose(fapl);
}
return stat;
}
int
NC4_extract_file_image(NC_HDF5_FILE_INFO_T* h5)
{
int stat = NC_NOERR;
hid_t fapl;
herr_t herr;
NC_memio mem;
assert(h5 && h5->hdfid && !h5->no_write);
/* Get the file access property list */
#if 0
fapl = H5Fget_access_plist(h5->hdfid);
#else
fapl = h5->mem.fapl;
#endif
if(fapl < 0)
{stat = NC_EHDFERR; goto done;}
memset(&mem,0,sizeof(mem));
herr = H5Pget_file_image(fapl, &mem.memory, &mem.size);
if(herr < 0)
{stat = NC_EHDFERR; goto done;}
h5->mem.memio = mem;
/* Close FAPL */
if (H5Pclose(fapl) < 0)
{stat = NC_EHDFERR; goto done;}
h5->mem.fapl = 0;
#if 0
/* Pass 1: get the memory chunk size */
size = H5Fget_file_image(hdfid, NULL, 0);
if(size < 0)
{stat = NC_EHDFERR; goto done;}
/* Create the space to hold image */
h5->mem.memio.size = (size_t)size;
h5->mem.memio.memory = malloc(h5->mem.memio.size);
if(h5->mem.memio.memory == NULL)
{stat = NC_ENOMEM; goto done;}
/* Pass 2: get the memory chunk itself */
size = H5Fget_file_image(hdfid, h5->mem.memio.memory, h5->mem.memio.size);
if(size < 0)
{stat = NC_EHDFERR; goto done;}
#endif
done:
return stat;
}
/*-------------------------------------------------------------------------
* 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
*
*-------------------------------------------------------------------------
* Modified from code in H5LT.c.
*/
/**
Type H5LT_file_image_ud_t is taken from H5LT.c.
*/
#if 0
/* Data structure to pass application data to callbacks. */
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 */
} H5LT_file_image_ud_t;
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;
/* callback is only used if the application buffer is not actually copied */
if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
goto out;
/* realloc() is not allowed if the image is open in read-only mode */
if (!(udata->flags & H5LT_FILE_IMAGE_OPEN_RW))
goto out;
if (file_image_op == H5FD_FILE_IMAGE_OP_FILE_RESIZE) {
if (udata->vfd_image_ptr != ptr)
goto out;
if (udata->vfd_ref_count != 1)
goto out;
/* Modified:
If H5LT_FILE_IMAGE_DONT_RELEASE flag is set: two cases
1. If the realloc new size is <= existing size,
then pretend we did a realloc and return success.
2. Otherwise, realloc() is not allowed because reallocation
may change the address of the buffer. The
new address cannot be communicated to the application
to release it.
*/
if (udata->flags & H5LT_FILE_IMAGE_DONT_RELEASE) {
if(size > udata->vfd_image_size)
goto out;
} else {
if (NULL == (udata->vfd_image_ptr = HDrealloc(ptr, size)))
goto out;
udata->vfd_image_size = size;
}
return_value = udata->vfd_image_ptr;
} /* end if */
else
goto out;
return(return_value);
out:
return NULL;
} /* end local_image_realloc() */
static int
NC4_set_realloc_callback(NC_HDF5_FILE_INFO_T* h5, hid_t hdfid)
{
int stat = NC_NOERR;
hid_t fapl;
herr_t hstat;
H5FD_file_image_callbacks_t callbacks;
/* Get the file access property list */
fapl = H5Fget_access_plist(hdfid);
if(fapl < 0)
{stat = NC_EHDFERR; goto done;}
/* Now get the callbacks */
memset(&callbacks,0,sizeof(callbacks));
/* For some reason, this is not returning the callbacks */
hstat = H5Pget_file_image_callbacks(fapl, &callbacks);
if(hstat < 0)
{stat = NC_EHDFERR; goto done;}
/* Replace the realloc callback */
callbacks.image_realloc = local_image_realloc;
/* Now reset the callbacks */
hstat = H5Pset_file_image_callbacks(fapl, &callbacks);
if(hstat < 0)
{stat = NC_EHDFERR; goto done;}
done:
return stat;
}
#endif

712
libsrc4/nc4memcb.c Normal file
View File

@ -0,0 +1,712 @@
/*********************************************************************
* 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
/* 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;
/* 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. */
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 */
} 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;
/* callback is only used if the application buffer is not actually copied */
if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
goto out;
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:
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++;
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->flags & H5LT_FILE_IMAGE_DONT_COPY))
goto out;
/* fake the malloc by returning the original 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 */
return(return_value);
out:
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;
/* callback is only used if the application buffer is not actually copied */
if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
goto out;
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;
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 */
return(dest);
out:
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
*
*-------------------------------------------------------------------------
*/
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;
/* callback is only used if the application buffer is not actually copied */
if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
goto out;
/* realloc() is not allowed if the image is open in read-only mode */
if (!(udata->flags & H5LT_FILE_IMAGE_OPEN_RW))
goto out;
if (file_image_op == H5FD_FILE_IMAGE_OP_FILE_RESIZE) {
if (udata->vfd_image_ptr != ptr)
goto out;
if (udata->vfd_ref_count != 1)
goto out;
/* 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.
*/
if(size <= udata->vfd_image_size) {
/* Ok, pretend we did a realloc */
} else if((udata->flags & H5LT_FILE_IMAGE_DONT_RELEASE)
|| (udata->flags & H5LT_FILE_IMAGE_DONT_COPY)) {
goto out; /* realloc MAY violate these flags */
} else {
if (NULL == (udata->vfd_image_ptr = HDrealloc(ptr, size)))
goto out;
udata->vfd_image_size = size;
}
return_value = udata->vfd_image_ptr;
} /* end if */
else
goto out;
return(return_value);
out:
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;
/* callback is only used if the application buffer is not actually copied */
if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
goto out;
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--;
/* release the shared buffer only if indicated by the respective flag and there are no outstanding references */
if (udata->fapl_ref_count == 0 && udata->vfd_ref_count == 0 &&
!(udata->flags & H5LT_FILE_IMAGE_DONT_RELEASE)) {
free(udata->fapl_image_ptr);
udata->app_image_ptr = NULL;
udata->fapl_image_ptr = NULL;
udata->vfd_image_ptr = NULL;
} /* end if */
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--;
/* release the shared buffer only if indicated by the respective flag and there are no outstanding references */
if (udata->fapl_ref_count == 0 && udata->vfd_ref_count == 0 &&
!(udata->flags & H5LT_FILE_IMAGE_DONT_RELEASE)) {
free(udata->vfd_image_ptr);
udata->app_image_ptr = NULL;
udata->fapl_image_ptr = NULL;
udata->vfd_image_ptr = NULL;
} /* end if */
break;
/* added unused labels to keep the compiler quite */
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 */
return(SUCCEED);
out:
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;
/* callback is only used if the application buffer is not actually copied */
if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
goto out;
if (udata->ref_count == 0)
goto out;
udata->ref_count++;
return(udata);
out:
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;
/* callback is only used if the application buffer is not actually copied */
if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
goto out;
if (udata->ref_count == 0)
goto out;
udata->ref_count--;
/* checks that there are no references outstanding before deallocating udata */
if (udata->ref_count == 0 && udata->fapl_ref_count == 0 &&
udata->vfd_ref_count == 0)
free(udata);
return(SUCCEED);
out:
return(FAIL);
} /* end udata_free */
/* End of callbacks definitions for file image operations */
/*-------------------------------------------------------------------------
*
* Public functions
*
*-------------------------------------------------------------------------
*/
/*-------------------------------------------------------------------------
* Function: H5LTopen_file_image
*
* Purpose: Open a user supplied file image using the core file driver.
*
* Return: File identifier, Failure: -1
*
* Programmer: Christian Chilan
*
* Date: October 3, 2011
*
*-------------------------------------------------------------------------
*/
int
NC4_open_image(NC_HDF5_FILE_INFO_T* h5)
{
hid_t fapl, file_id; /* HDF5 identifiers */
unsigned file_open_flags;/* Flags for image 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 */
size_t buf_size = h5->mem.memio.size;
void* buf_ptr = h5->mem.memio.memory;
unsigned flags = h5->mem.flags;
static long file_name_counter;
/* Note we use our modified image_realloc -- local_image_realloc */
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};
/* check arguments */
if (buf_ptr == NULL)
goto out;
if (buf_size == 0)
goto out;
if (flags & (unsigned)~(H5LT_FILE_IMAGE_ALL))
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 * buf_size) > min_incr)
alloc_incr = (size_t)(buf_prcnt * buf_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 ONLY if the file image is NOT copied */
if (flags & H5LT_FILE_IMAGE_DONT_COPY) {
H5LT_file_image_ud_t *udata; /* Pointer to udata structure */
/* Allocate buffer to communicate user data to callbacks */
if (NULL == (udata = (H5LT_file_image_ud_t *)malloc(sizeof(H5LT_file_image_ud_t))))
goto out;
/* Initialize udata with info about app buffer containing file image and flags */
udata->app_image_ptr = buf_ptr;
udata->app_image_size = buf_size;
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 = flags;
udata->ref_count = 1; /* corresponding to the first FAPL */
/* copy address of udata into callbacks */
callbacks.udata = (void *)udata;
/* Set file image callbacks */
if (H5Pset_file_image_callbacks(fapl, &callbacks) < 0) {
free(udata);
goto out;
} /* end if */
} /* end if */
/* Assign file image in user buffer to FAPL */
if (H5Pset_file_image(fapl, buf_ptr, buf_size) < 0)
goto out;
/* set file open flags */
if (flags & H5LT_FILE_IMAGE_OPEN_RW)
file_open_flags = H5F_ACC_RDWR;
else
file_open_flags = H5F_ACC_RDONLY;
/* define a unique file name */
snprintf(file_name, (sizeof(file_name) - 1), "file_image_%ld", file_name_counter++);
/* Assign file image in FAPL to the core file driver */
if ((file_id = H5Fopen(file_name, file_open_flags, fapl)) < 0)
goto out;
h5->mem.fapl = fapl;
#if 0
/* Close FAPL */
if (H5Pclose(fapl) < 0)
goto out;
#endif
/* Return file identifier */
return file_id;
out:
H5E_BEGIN_TRY {
H5Pclose(fapl);
} H5E_END_TRY;
return -1;
} /* end H5LTopen_file_image() */

View File

@ -236,7 +236,7 @@ done:
static int
NCP_close(int ncid)
NCP_close(int ncid, void* ignored)
{
NC* nc;
NCP_INFO* nc5;

View File

@ -59,7 +59,7 @@ IF(LARGE_FILE_TESTS)
ENDIF()
IF(BUILD_DISKLESS)
SET(TESTFILES ${TESTFILES} tst_diskless tst_diskless3 tst_diskless4 tst_diskless5)
SET(TESTFILES ${TESTFILES} tst_diskless tst_diskless3 tst_diskless4 tst_diskless5 tst_inmemory)
IF(USE_NETCDF4)
SET(TESTFILES ${TESTFILES} tst_diskless2)
ENDIF()
@ -89,6 +89,7 @@ IF(BUILD_UTILITIES)
add_sh_test(nc_test run_diskless2)
ENDIF(LARGE_FILE_TESTS)
add_sh_test(nc_test run_diskless5)
add_sh_test(nc_test run_inmemory)
ENDIF(BUILD_DISKLESS)
ENDIF(BUILD_UTILITIES)

View File

@ -1,14 +1,14 @@
# Test c output
T=tst_addvar
T=tst_inmemory
ARGS=test_pnetcdf.nc
#ARGS=t
#CMD=valgrind --leak-check=full
CMD=gdb --args
PAR=1
#PAR=1
CFLAGS=-Wall -Wno-unused-variable -Wno-unused-function -g -O -I.. -I../include
CFLAGS=-Wall -Wno-unused-variable -Wno-unused-function -g -O0 -I.. -I../include
ifdef PAR
CC=mpicc

View File

@ -58,7 +58,7 @@ check_PROGRAMS = $(TESTPROGRAMS)
# Build Diskless test helpers
if BUILD_DISKLESS
check_PROGRAMS += tst_diskless tst_diskless3 tst_diskless4 tst_diskless5
check_PROGRAMS += tst_diskless tst_diskless3 tst_diskless4 tst_diskless5 tst_inmemory
if USE_NETCDF4
check_PROGRAMS += tst_diskless2
endif
@ -68,7 +68,7 @@ TESTS = $(TESTPROGRAMS)
if BUILD_UTILITIES
if BUILD_DISKLESS
TESTS += run_diskless.sh run_diskless5.sh
TESTS += run_diskless.sh run_diskless5.sh run_inmemory.sh
if BUILD_MMAP
TESTS += run_mmap.sh
run_mmap.log: run_diskless.log
@ -99,7 +99,7 @@ endif # USE_VALGRIND_TESTS
EXTRA_DIST = test_get.m4 test_put.m4 run_valgrind_tests.sh \
run_diskless.sh run_diskless2.sh run_diskless5.sh run_mmap.sh \
run_pnetcdf_test.sh test_read.m4 test_write.m4 ref_tst_diskless2.cdl \
tst_diskless5.cdl CMakeLists.txt
tst_diskless5.cdl run_inmemory.sh CMakeLists.txt
# These files are created by the tests.
CLEANFILES = nc_test_classic.nc nc_test_64bit.nc nc_test_netcdf4.nc \

49
nc_test/run_inmemory.sh Executable file
View File

@ -0,0 +1,49 @@
#!/bin/sh
if test "x$srcdir" = x ; then srcdir=`pwd`; fi
. ../test_common.sh
set -e
# Get the target OS and CPU
CPU=`uname -p`
OS=`uname`
#Constants
FILE3=tst_inmemory3
CREATE3=tst_inmemory3_create
FILE4=tst_inmemory4
CREATE4=tst_inmemory4_create
echo ""
echo "*** Testing in-memory operations"
HASNC4=`${top_builddir}/nc-config --has-nc4`
# Execute the core of the inmemory tests
if ! ${execdir}/tst_inmemory; then
echo "FAIL: tst.inmemory"
fi
echo "**** Test ncdump of the resulting inmemory data"
${NCDUMP} -n "${FILE3}" ${FILE3}.nc > ${FILE3}.cdl
${NCDUMP} -n "${FILE3}" ${CREATE3}.nc > ${CREATE3}.cdl
if ! diff -wb ${FILE3}.cdl ${CREATE3}.cdl ; then
echo "FAIL: ${FILE3}.cdl vs ${CREATE3}.cdl
fi
if test "x$HASNC4" = "xyes" ; then
${NCDUMP} ${FILE4}.nc > ${FILE4}.cdl
${NCDUMP} ${CREATE4}.nc > ${CREATE4}.cdl
if diff -wb ${FILE4}.cdl ${CREATE4}.cdl ; then
echo "FAIL: ${FILE4}.cdl vs ${CREATE4}.cdl
fi
# cleanup
rm -f ${FILE3}.nc ${FILE4}.nc ${CREATE3}.nc ${CREATE4}.nc
rm -f ${FILE3}.cdl ${FILE4}.cdl ${CREATE3}.cdl ${CREATE4}.cdl
echo "PASS: all inmemory tests"
exit 0

View File

@ -10,11 +10,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_LIBGEN_H
#include <libgen.h> /* basename() */
#else
#define basename(X) X
#endif
#include <netcdf.h>
@ -52,7 +47,7 @@ int main(int argc, char** argv) {
else strcpy(filename, "tst_def_var_fill.nc");
char *cmd_str = (char*)malloc(strlen(argv[0]) + 256);
sprintf(cmd_str, "*** TESTING C %s for def_var_fill ", basename(argv[0]));
sprintf(cmd_str, "*** TESTING C %s for def_var_fill ", argv[0]);
printf("%-66s ------ ", cmd_str); fflush(stdout);
free(cmd_str);

View File

@ -17,7 +17,7 @@
#include "netcdf_mem.h"
#undef MEM
#define DISKLESS
#undef DISKLESS
#define USEINT
#undef DUMP

645
nc_test/tst_inmemory.c Normal file
View File

@ -0,0 +1,645 @@
/** \file \internal
Basic NC_INMEMORY API tests both for netcdf-3 and netcdf-4
Copyright 2011, UCAR/Unidata. See COPYRIGHT file for copying and
redistribution conditions.
*/
#undef DDBG
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "netcdf.h"
#include "netcdf_mem.h"
#include "ncbytes.h"
#include "nc_tests.h"
#include "err_macros.h"
#include <hdf5.h>
#ifdef USE_NETCDF4
extern int H5Eprint1(FILE * stream);
#endif
#define FLAGS4 (NC_INMEMORY|NC_NETCDF4|NC_CLOBBER)
#define FLAGS3 (NC_INMEMORY|NCCLOBBER)
#define NC_NETCDF3 0
#define MODIFIED 1
#define LARGE_SPACE (1<<18)
#define FILE3 "tst_inmemory3.nc"
#define FILE4 "tst_inmemory4.nc"
#define CREATE3 "tst_inmemory3_create.nc"
#define CREATE4 "tst_inmemory4_create.nc"
#define XFAIL "tst_xfail.nc"
#define MISC "tst_misc.nc"
#define CREATEFILE3 "tst_memcreate3.nc"
#define CREATEFILE4 "tst_memcreate4.nc"
/* Make no dimension larger than this */
#define MAXDIMLEN 100
#define NDIMS 2
#define UNLIM_LEN 2
#define DIM0_NAME "fun"
#define DIM1_NAME "money"
#define DIM1_LEN 8
#define ATT0_NAME "home"
#define ATT0_TEXT "earthship"
#define NVARS 3
#define VAR0_NAME "nightlife"
#define VAR1_NAME "time"
#define VAR2_NAME "taxi_distance"
#define VAR3_NAME "miles"
#define FLOATVAL ((float)42.22)
/*
CDL for created file:
netcdf tst_inmemory3 {
dimensions:
fun = UNLIMITED ; // (2 currently)
money = 8 ;
variables:
int nightlife(fun, money) ;
float time ;
short taxi_distance(money) ;
// global attributes:
:home = "earthship" ;
data:
nightlife =
0, 100, 200, 300, 400, 500, 600, 700,
800, 900, 1000, 1100, 1200, 1300, 1400, 1500 ;
time = 42.22 ;
taxi_distance = 0, 1, 2, 3, 4, 5, 6, 7 ;
}
*/
#ifdef DDBG
#undef ERR
static void
fail(int line) {
fflush(stdout);
fprintf(stderr,"\nline=%d\n",line);
fflush(stderr);
exit(1);
}
#define ERR fail(__LINE__)
#endif
/* Store any error message */
static char errmsg[4096];
static int
check(int stat, const char* file, int line, int xfail)
{
int pass = ((!xfail && stat == NC_NOERR) || (xfail && stat != NC_NOERR));
fflush(stdout);
if(!xfail) {
if(stat != NC_NOERR) {
fprintf(stderr,"***Fail: line: %d; status=%d %s\n",
line,stat,nc_strerror(stat));
err++;
}
} else { /*xfail*/
if(stat == NC_NOERR) {
fprintf(stderr,"***XFail Fail: line: %d; passed instead of failed\n",
line);
err++;
} else {
fprintf(stderr,"\t***XFail: status=%d %s\n",
stat,nc_strerror(stat));
}
stat = NC_NOERR; /* because xfail */
}
fflush(stderr);
return stat;
}
#define CHECK(expr) {stat = check((expr),__FILE__,__LINE__,0); if(stat) return stat;}
#define XCHECK(expr) {stat = check((expr),__FILE__,__LINE__,1); if(stat) return stat;}
#define REPORT(xfail,expr) {if((xfail)) {XCHECK((expr));} else {CHECK((expr));}}
/**************************************************/
static void
removefile(const char* path)
{
unlink(path);
}
static int
readfile(const char* path, NC_memio* memio)
{
int status = NC_NOERR;
FILE* f = NULL;
size_t filesize = 0;
size_t count = 0;
char* memory = NULL;
char* p = NULL;
/* Open the file for reading */
#ifdef _MSC_VER
f = fopen(path,"rb");
#else
f = fopen(path,"r");
#endif
if(f == NULL)
{status = errno; goto done;}
/* get current filesize */
if(fseek(f,0,SEEK_END) < 0)
{status = errno; goto done;}
filesize = (size_t)ftell(f);
/* allocate memory */
memory = malloc((size_t)filesize);
if(memory == NULL)
{status = NC_ENOMEM; goto done;}
/* move pointer back to beginning of file */
rewind(f);
count = filesize;
p = memory;
while(count > 0) {
size_t actual;
actual = fread(p,1,count,f);
if(actual == 0 || ferror(f))
{status = NC_EIO; goto done;}
count -= actual;
p += actual;
}
if(memio) {
memio->size = (size_t)filesize;
memio->memory = memory;
}
done:
if(status != NC_NOERR && memory != NULL)
free(memory);
if(f != NULL) fclose(f);
return status;
}
static int
writefile(const char* path, NC_memio* memio)
{
int status = NC_NOERR;
FILE* f = NULL;
size_t count = 0;
char* memory = NULL;
char* p = NULL;
/* Open the file for writing */
#ifdef _MSC_VER
f = fopen(path,"wb");
#else
f = fopen(path,"w");
#endif
if(f == NULL)
{status = errno; goto done;}
count = memio->size;
p = memio->memory;
while(count > 0) {
size_t actual;
actual = fwrite(p,1,count,f);
if(actual == 0 || ferror(f))
{status = NC_EIO; goto done;}
count -= actual;
p += actual;
}
done:
if(f != NULL) fclose(f);
return status;
}
/* Duplicate an NC_memio instance; needed to avoid
attempting to use memory that might have been realloc'd
Allow the new memory to be larger than the src memory
*/
int
duplicatememory(NC_memio* src, NC_memio* target, size_t alloc)
{
if(src == NULL || target == NULL || src->size == 0 || src->memory == NULL)
return NC_EINVAL;
*target = *src;
if(alloc == 0) alloc = src->size;
target->memory = malloc(alloc);
if(target->memory == NULL)
return NC_ENOMEM;
memcpy(target->memory,src->memory,src->size);
target->size = alloc;
return NC_NOERR;
}
/*
Given an ncid of a created file, fill in the meta-data
and data as described by the above CDL. Do not close
the file.
*/
static int
define_metadata(int ncid)
{
int stat = NC_NOERR;
int dimid[NDIMS], varid0, varid1, varid2;
short short_data[DIM1_LEN];
size_t start[1] = {0};
size_t count[1] = {DIM1_LEN};
int dimprod = (UNLIM_LEN*DIM1_LEN);
int i;
float float_data;
int nightdata[UNLIM_LEN*DIM1_LEN] ;
/* Create data to write */
float_data = FLOATVAL;
for (i = 0; i < DIM1_LEN; i++)
short_data[i] = i;
for (i = 0; i < dimprod; i++)
nightdata[i] = (100*i);
CHECK(nc_put_att_text(ncid, NC_GLOBAL, ATT0_NAME,
sizeof(ATT0_TEXT), ATT0_TEXT));
CHECK(nc_def_dim(ncid, DIM0_NAME, NC_UNLIMITED, &dimid[0]));
CHECK(nc_def_dim(ncid, DIM1_NAME, DIM1_LEN, &dimid[1]));
CHECK(nc_def_var(ncid, VAR0_NAME, NC_INT, NDIMS, dimid, &varid0));
CHECK(nc_def_var(ncid, VAR1_NAME, NC_FLOAT, 0, NULL, &varid1));
CHECK(nc_def_var(ncid, VAR2_NAME, NC_SHORT, 1, &dimid[1], &varid2));
CHECK(nc_enddef(ncid));
CHECK(nc_put_vara_float(ncid, varid1, NULL, NULL, &float_data));
CHECK(nc_put_vara_short(ncid, varid2, start, count, short_data));
{
size_t start[2] = {0,0};
size_t count[2] = {2,DIM1_LEN};
CHECK(nc_put_vara_int(ncid, varid0, start, count, nightdata));
}
return stat;
}
/*
Create our reference file as a real on-disk file
and read it back into memory
*/
static int
create_reference_file(const char* filename, int mode, NC_memio* filedata)
{
int stat = NC_NOERR;
int ncid;
CHECK(nc_create(filename, mode|NC_CLOBBER, &ncid)); /* overwrite */
CHECK(define_metadata(ncid));
CHECK(nc_close(ncid));
/* Read back the contents of the file into memory */
if(filedata != NULL) {
memset(filedata,0,sizeof(NC_memio));
CHECK(readfile(filename,filedata));
}
return stat;
}
static int
modify_file(int ncid)
{
int stat = NC_NOERR;
size_t i;
int varid3;
int dimid[1];
size_t len;
int data[MAXDIMLEN];
/* Get id of the unlimited dimension */
if((stat=nc_inq_dimid(ncid, DIM0_NAME, dimid))) goto done;
/* get current dim length */
if((stat=nc_inq_dimlen(ncid, dimid[0], &len))) goto done;
/* open file for new meta-data */
if((stat=nc_redef(ncid))) goto done;
/* Define a new variable */
if((stat=nc_def_var(ncid, VAR3_NAME, NC_INT, 1, dimid, &varid3))) goto done;
/* close metadata */
if((stat=nc_enddef(ncid))) goto done;
/* Write data to new variable */
for(i=0;i<len;i++)
data[i] = i;
if((stat=nc_put_var_int(ncid,varid3,data))) goto done;
done:
return stat;
}
/* Verify the content of a file */
static int
verify_file(int ncid, int modified)
{
int stat = NC_NOERR;
int i,tmp;
int dimid_in[NDIMS];
int dimid[NDIMS];
int ndims_in, nvars_in, natts_in, unlimdimid_in;
char name_in[NC_MAX_NAME + 1], att0_in[NC_MAX_NAME + 1];
nc_type type_in;
size_t len_in;
int varid[4];
int nightdata_in[UNLIM_LEN*DIM1_LEN] ;
float float_data_in;
int milesdata_in[MAXDIMLEN];
int dimprod = UNLIM_LEN * DIM1_LEN;
int modcount = (modified?1:0);
CHECK(nc_inq(ncid, &ndims_in, &nvars_in, &natts_in, &unlimdimid_in));
if (ndims_in != 2 || nvars_in != NVARS+modified || natts_in != 1 || unlimdimid_in != 0)
CHECK(NC_EINVAL);
/* Get all the dimids */
tmp = 0;
#ifdef USE_NETCDF4
CHECK((nc_inq_dimids(ncid,&tmp,dimid,1)));
if(tmp != NDIMS) CHECK(NC_EINVAL);
/* Get all the varids */
tmp = 0;
CHECK((nc_inq_varids(ncid,&tmp,varid)));
if(tmp != (NVARS+modified)) CHECK(NC_EINVAL);
#else
{ /* Simulate nc_inq_varids and nc_inq_dimids */
int j;
int dimcnt = 0;
int varcnt = 0;
CHECK(nc_inq(ncid, &dimcnt, &varcnt, NULL, NULL));
for(j=0;j<dimcnt;j++) dimid[j] = j;
for(j=0;j<varcnt;j++) varid[j] = j;
}
#endif
CHECK(nc_get_att_text(ncid, NC_GLOBAL, ATT0_NAME, att0_in));
att0_in[sizeof(ATT0_TEXT)] = '\0';
if (strcmp(att0_in, ATT0_TEXT)) CHECK(NC_EINVAL);
/* CHECK dimensions. */
CHECK(nc_inq_dim(ncid, dimid[0], name_in, &len_in));
if (strcmp(name_in, DIM0_NAME)) CHECK(NC_EINVAL);
CHECK(nc_inq_dim(ncid, dimid[1], name_in, &len_in));
if (strcmp(name_in, DIM1_NAME) || len_in != DIM1_LEN) CHECK(NC_EINVAL);
/* CHECK variables. */
CHECK(nc_inq_var(ncid, varid[0], name_in, &type_in, &ndims_in, dimid_in, &natts_in));
if (strcmp(name_in, VAR0_NAME) || type_in != NC_INT || ndims_in != NDIMS ||
dimid_in[0] != 0 || dimid_in[1] != 1 || natts_in != 0) CHECK(NC_EINVAL);
CHECK(nc_inq_var(ncid, varid[1], name_in, &type_in, &ndims_in, dimid_in, &natts_in));
if (strcmp(name_in, VAR1_NAME) || type_in != NC_FLOAT || ndims_in != 0 ||
natts_in != 0) CHECK(NC_EINVAL);
CHECK(nc_inq_var(ncid, varid[2], name_in, &type_in, &ndims_in, dimid_in, &natts_in));
if (strcmp(name_in, VAR2_NAME) || type_in != NC_SHORT || ndims_in != 1 ||
dimid_in[0] != 1 || natts_in != 0) CHECK(NC_EINVAL);
CHECK(nc_get_var_int(ncid, varid[0], nightdata_in));
for(i=0;i<dimprod;i++) {
if(nightdata_in[i] != (100*i)) CHECK(NC_EINVAL);
}
CHECK(nc_get_vara_float(ncid, varid[1], NULL, NULL, &float_data_in));
if (float_data_in != FLOATVAL) CHECK(NC_EINVAL);
if(modified) {
size_t unlimlen;
CHECK(nc_inq_var(ncid, varid[3], name_in, &type_in, &ndims_in, dimid_in, &natts_in));
if (strcmp(name_in, VAR3_NAME) || type_in != NC_INT || ndims_in != 1 ||
dimid_in[0] != 0 || natts_in != 0) CHECK(NC_EINVAL);
CHECK(nc_inq_dimlen(ncid, dimid_in[0], &unlimlen));
CHECK(nc_get_var_int(ncid, varid[3], milesdata_in));
for(i=0;i<unlimlen;i++) {
if(milesdata_in[i] != i) CHECK(NC_EINVAL);
}
}
return stat;
}
void
memiofree(NC_memio* memio)
{
if(memio != NULL) {
if(memio->memory != NULL)
free(memio->memory);
}
}
static int
test_open(const char* path, NC_memio* filedata, int mode)
{
int stat = NC_NOERR;
NC_memio duplicate;
NC_memio* finaldata = NULL;
int ncid;
int xmode = mode; /* modified mode */
finaldata = calloc(1,sizeof(NC_memio));
fprintf(stderr,"\n\t***Test open 1: nc_open_mem(): read-only\n");
CHECK(duplicatememory(filedata,&duplicate,0));
CHECK(nc_open_mem(path, xmode, duplicate.size, duplicate.memory, &ncid));
CHECK(verify_file(ncid,!MODIFIED));
CHECK(nc_close(ncid));
free(duplicate.memory);
fprintf(stderr,"\n\t***Test open 2: nc_open_memio(): read-only\n");
CHECK(duplicatememory(filedata,&duplicate,0));
duplicate.flags = NC_MEMIO_LOCKED;
CHECK(nc_open_memio(path, xmode, &duplicate, &ncid))
CHECK(verify_file(ncid,!MODIFIED));
CHECK(nc_close_memio(ncid,finaldata));
/* Published returned finaldata */
fprintf(stderr,"\tfinaldata: size=%lld memory=%p\n",(unsigned long long)finaldata->size,finaldata->memory);
/* Verify that finaldata is same */
if(finaldata->size != duplicate.size) CHECK(NC_EINVAL);
if(finaldata->memory != duplicate.memory) CHECK(NC_EINVAL);
free(finaldata->memory);
fprintf(stderr,"\n\t***Test open 3: nc_open_memio(): read-write, copy\n");
xmode |= NC_WRITE; /* allow file to be modified */
CHECK(duplicatememory(filedata,&duplicate,0));
CHECK(nc_open_memio(path, xmode, &duplicate, &ncid))
/* modify file */
CHECK(modify_file(ncid));
CHECK(verify_file(ncid,MODIFIED));
CHECK(nc_close_memio(ncid,finaldata));
/* Published returned finaldata */
fprintf(stderr,"\tfinaldata: size=%lld memory=%p\n",(unsigned long long)finaldata->size,finaldata->memory);
/* Verify that finaldata is same */
if(finaldata->size < filedata->size) CHECK(NC_EINVAL);
/* As a safeguard, the memory in duplicate should have been set to NULL*/
free(finaldata->memory);
fprintf(stderr,"\n\t***Test open 4: nc_open_memio(): read-write, locked, extra space\n");
/* Store the filedata in a memory chunk that leaves room for modification */
CHECK(duplicatememory(filedata,&duplicate,LARGE_SPACE));
/* Lock the duplicate memory */
duplicate.flags |= NC_MEMIO_LOCKED;
xmode |= NC_WRITE; /* allow file to be modified */
CHECK(nc_open_memio(path, xmode, &duplicate, &ncid))
/* modify file */
CHECK(modify_file(ncid));
CHECK(verify_file(ncid,MODIFIED));
CHECK(nc_close_memio(ncid,finaldata));
/* Published returned finaldata */
fprintf(stderr,"\tfinaldata: size=%lld memory=%p\n",(unsigned long long)finaldata->size,finaldata->memory);
/* Check returned finaldata */
if(finaldata->size != duplicate.size) CHECK(NC_EINVAL);
if(finaldata->memory != duplicate.memory) CHECK(NC_EINVAL);
free(finaldata->memory);
return stat;
}
static int
test_create(const char* path, int mode)
{
int stat = NC_NOERR;
NC_memio* finaldata = NULL;
int ncid;
int xmode = mode;
fprintf(stderr,"\n\t***Test create 1: nc_create_memio(): no initialsize\n");
CHECK(nc_create_mem(path, xmode, 0, &ncid))
/* create file metadata */
CHECK(define_metadata(ncid));
CHECK(verify_file(ncid,!MODIFIED));
finaldata = calloc(1,sizeof(NC_memio));
CHECK(nc_close_memio(ncid,finaldata));
/* Published returned finaldata */
fprintf(stderr,"\tfinaldata: size=%lld memory=%p\n",(unsigned long long)finaldata->size,finaldata->memory);
free(finaldata->memory);
fprintf(stderr,"\n\t***Test create 2: nc_create_memio(): initialsize; save file\n");
CHECK(nc_create_mem(path, xmode, LARGE_SPACE, &ncid))
/* create file metadata */
CHECK(define_metadata(ncid));
CHECK(verify_file(ncid,!MODIFIED));
finaldata = calloc(1,sizeof(NC_memio));
CHECK(nc_close_memio(ncid,finaldata));
/* Published returned finaldata */
fprintf(stderr,"\tfinaldata: size=%lld memory=%p\n",(unsigned long long)finaldata->size,finaldata->memory);
/* Write out the final data as a .nc file */
CHECK(writefile(path,finaldata));
free(finaldata->memory);
return stat;
}
static int
test_misc(const char* path, int mode, NC_memio* filedata)
{
int stat = NC_NOERR;
int ncid;
int xmode = mode;
NC_memio duplicate;
fprintf(stderr,"\n\t***Test misc 1: use nc_close on created inmemory file\n");
CHECK(nc_create_mem(MISC, xmode, 0, &ncid))
CHECK(nc_close(ncid));
removefile(MISC);
fprintf(stderr,"\n\t***Test misc 2: use nc_close on opened inmemory file\n");
CHECK(duplicatememory(filedata,&duplicate,0));
CHECK(nc_open_memio(path, xmode, &duplicate, &ncid))
CHECK(verify_file(ncid,!MODIFIED));
CHECK(nc_close(ncid));
removefile(MISC);
return stat;
}
/* Test various edge conditions to ensure they fail correctly */
static int
test_xfail(const char* path, int mode, NC_memio* filedata)
{
int stat = NC_NOERR;
NC_memio duplicate;
int ncid;
int xmode = mode; /* modified mode */
fprintf(stderr,"\n\t***Test xfail 1: nc_open_mem(): write to read-only\n");
CHECK(duplicatememory(filedata,&duplicate,0));
CHECK(nc_open_mem(XFAIL, xmode, duplicate.size, duplicate.memory, &ncid));
XCHECK(nc_redef(ncid));
CHECK(nc_abort(ncid));
free(duplicate.memory);
fprintf(stderr,"\n\t***Test xfail 2: nc_open_memio(): modify without overallocating\n");
if((mode & NC_NETCDF4)) {
fprintf(stderr,"\tSuppressed because of HDF5 library bug\n");
} else {
/* With HDF5 1.8.20, and possibly other versions,
this tests causes a seg fault in the HDF5 Library.
So until it is fixed, just leave well enough alone */
NC_memio* finaldata = NULL;
finaldata = calloc(1,sizeof(NC_memio));
CHECK(duplicatememory(filedata,&duplicate,0));
duplicate.flags = NC_MEMIO_LOCKED;
xmode |= NC_WRITE;
CHECK(nc_open_memio(XFAIL, xmode, &duplicate, &ncid))
XCHECK(modify_file(ncid));
CHECK(nc_abort(ncid));
free(finaldata->memory);
}
return stat;
}
int
main(int argc, char **argv)
{
int stat = NC_NOERR;
int i;
void* filedata = NULL;
NC_memio filedata3;
NC_memio filedata4;
nc_set_log_level(0);
#ifdef USE_NETCDF4
H5Eprint1(stderr);
#endif
fprintf(stderr,"\n*** Testing the inmemory API: netcdf-3.\n");
CHECK(create_reference_file(FILE3,NC_NETCDF3,&filedata3)); /* netcdf-3 */
CHECK(test_open(FILE3,&filedata3,NC_NETCDF3));
CHECK(test_create(CREATE3,NC_NETCDF3));
CHECK(test_misc(FILE3, NC_NETCDF3, &filedata3));
CHECK(test_xfail(FILE3, NC_NETCDF3, &filedata3));
memiofree(&filedata3);
#ifdef USE_NETCDF4
fprintf(stderr,"\n*** Testing the inmemory API: netcdf-4.\n");
CHECK(create_reference_file(FILE4,NC_NETCDF4,&filedata4));
CHECK(test_open(FILE4,&filedata4,NC_NETCDF4));
CHECK(test_create(CREATE4,NC_NETCDF4));
CHECK(test_misc(FILE4,NC_NETCDF4, &filedata4));
CHECK(test_xfail(FILE4, NC_NETCDF4, &filedata4));
memiofree(&filedata4);
#endif
SUMMARIZE_ERR;
FINAL_RESULTS;
return 0;
}