2011-08-22 21:53:37 +08:00
|
|
|
|
/** \file
|
|
|
|
|
\internal
|
|
|
|
|
|
|
|
|
|
\page nc_dispatch Internal Dispatch Table Architecture
|
|
|
|
|
|
2011-09-21 01:30:02 +08:00
|
|
|
|
This document describes the architecture and details of the netCDF
|
2011-08-22 21:53:37 +08:00
|
|
|
|
internal dispatch mechanism. The idea is that when a user opens or
|
2011-09-21 01:30:02 +08:00
|
|
|
|
creates a netcdf file, a specific dispatch table is
|
2011-08-22 21:53:37 +08:00
|
|
|
|
chosen. Subsequent netcdf API calls are then channeled through that
|
|
|
|
|
dispatch table to the appropriate function for implementing that API
|
|
|
|
|
call.
|
|
|
|
|
|
2011-09-21 01:30:02 +08:00
|
|
|
|
At least the following dispatch tables are supported.
|
2011-08-22 21:53:37 +08:00
|
|
|
|
- netcdf classic files (netcdf-3)
|
|
|
|
|
- netcdf enhanced files (netcdf-4)
|
|
|
|
|
- OPeNDAP to netcdf-3
|
|
|
|
|
- OPeNDAP to netcdf-4
|
|
|
|
|
|
|
|
|
|
The dispatch table represents a distillation of the netcdf API down to
|
|
|
|
|
a minimal set of internal operations. The format of the dispatch table
|
|
|
|
|
is defined in the file libdispatch/ncdispatch.h. Every new dispatch
|
|
|
|
|
table must define this minimal set of operations.
|
|
|
|
|
|
|
|
|
|
\page adding_dispatch Adding a New Dispatch Table
|
|
|
|
|
|
|
|
|
|
In order to make this process concrete, let us assume we plan to add
|
|
|
|
|
an in-memory implementation of netcdf-3.
|
|
|
|
|
|
|
|
|
|
\section dispatch_step1 Step 1.
|
|
|
|
|
|
|
|
|
|
Define a –enable flag and an AM_CONFIGURE flag in configure.ac. We
|
|
|
|
|
will use the flags –enable-netcdfm and USE_NETCDFM respectively.
|
|
|
|
|
|
|
|
|
|
\section dispatch_step2 Step 2
|
|
|
|
|
|
|
|
|
|
Choose some prefix of characters to identify the new dispatch
|
|
|
|
|
system. In effect we are defining a name-space. For our in-memory
|
|
|
|
|
system, we will choose "NCM" and "ncm". NCM is used for non-static
|
|
|
|
|
procedures to be entered into the dispatch table and ncm for all other
|
|
|
|
|
non-static procedures.
|
|
|
|
|
|
|
|
|
|
\section dispatch_step3 Step 3.
|
|
|
|
|
|
|
|
|
|
Modify file libdispatch/ncdispatch.h as follows.
|
|
|
|
|
|
|
|
|
|
Add a index for this implementation:
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
#define NC_DISPATCH_NCM 5
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
Define an external reference to the in-memory dispatch table.
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
#ifdef USE_NETCDFM
|
|
|
|
|
extern NC_Dispatch* NCM_dispatch_table;
|
|
|
|
|
#endif
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
\section dispatch_step4 Step 4.
|
|
|
|
|
|
|
|
|
|
Modify file libdispatch/netcdf.c as follows.
|
|
|
|
|
|
|
|
|
|
Add a ptr to the in-memory dispatch table.
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
#ifdef USE_NETCDFM
|
|
|
|
|
NC_Dispatch* NCM_dispatch_table = NULL;
|
|
|
|
|
#endif
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
Add includes for any necessary header files as needed.
|
|
|
|
|
|
|
|
|
|
\section dispatch_step5 Step 5.
|
|
|
|
|
|
|
|
|
|
Define the functions necessary to fill in the dispatch table. As a
|
|
|
|
|
rule, we assume that a new directory is defined, libsrcm, say. Within
|
|
|
|
|
this directory, we need to define Makefile.am, the source files
|
|
|
|
|
containing the dispatch table and the functions to be placed in the
|
|
|
|
|
dispatch table – call them ncmdispatch.c and ncmdispatch.h. Look at
|
|
|
|
|
libsrc/nc3dispatch.[ch] for an example.
|
|
|
|
|
|
|
|
|
|
As part of the ncmdispatch.c file, you must define the following.
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
NC_Dispatch NCM_dispatcher = {
|
|
|
|
|
NC_DISPATCH_NCM,
|
|
|
|
|
NCM_create,
|
|
|
|
|
NCM_open,
|
|
|
|
|
...
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
NCM_initialize(void)
|
|
|
|
|
{
|
|
|
|
|
NCM_dispatch_table = &NCM_dispatcher;
|
|
|
|
|
return NC_NOERR;
|
|
|
|
|
}
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
Assuming that the in-memory library does not require any external
|
|
|
|
|
libraries, then the Makefile.am will look something like this.
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
NCM_SOURCES = ncmdispatch.c ncmdispatch.h ...
|
|
|
|
|
AM_CPPFLAGS += -I$(top_srcdir)/libsrc -I$(top_srcdir)/libdispatch
|
|
|
|
|
libnetcdfm_la_SOURCES = $(NCM_SOURCES)
|
|
|
|
|
noinst_LTLIBRARIES = libnetcdfm.la
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
\section dispatch_step6 Step 6.
|
|
|
|
|
|
|
|
|
|
Provide for the inclusion of this library in the final libnetcdf
|
|
|
|
|
library. This is accomplished by modifying liblib/Makefile.am by
|
|
|
|
|
adding something like the following.
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
if USE_NETCDFM
|
|
|
|
|
libnetcdf_la_LIBADD += $(top_builddir)/libsrcm/libnetcdfm.la
|
|
|
|
|
endif
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
\section dispatch_step7 Step 7.
|
|
|
|
|
|
|
|
|
|
Modify the NC_intialize function in liblib/stub.c by adding
|
|
|
|
|
appropriate references to the NCM dispatch function.
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
#ifdef USE_NETCDFM
|
|
|
|
|
extern int NCM_initialize(void);
|
|
|
|
|
#endif
|
|
|
|
|
...
|
|
|
|
|
int NC_initialize(void)
|
|
|
|
|
{
|
|
|
|
|
...
|
|
|
|
|
#ifdef USE_DAP
|
|
|
|
|
if((stat = NCM_initialize())) return stat;
|
|
|
|
|
#endif
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
\section dispatch_step8 Step 8.
|
|
|
|
|
|
|
|
|
|
Add a directory of tests; ncm_test, say. The file ncm_test/Makefile.am
|
|
|
|
|
will look something like this.
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
# These files are created by the tests.
|
|
|
|
|
CLEANFILES = ...
|
|
|
|
|
# These are the tests which are always run.
|
|
|
|
|
TESTPROGRAMS = test1 test2 ...
|
|
|
|
|
test1_SOURCES = test1.c ...
|
|
|
|
|
...
|
|
|
|
|
# Set up the tests.
|
|
|
|
|
check_PROGRAMS = $(TESTPROGRAMS)
|
|
|
|
|
TESTS = $(TESTPROGRAMS)
|
|
|
|
|
# Any extra files required by the tests
|
|
|
|
|
EXTRA_DIST = ...
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
\section dispatch_step9 Step 9.
|
|
|
|
|
|
|
|
|
|
Provide for libnetcdfm to be constructed by adding the following to
|
|
|
|
|
the top-level Makefile.am.
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
if USE_NETCDFM
|
|
|
|
|
NCM=libsrcm
|
|
|
|
|
NCMTESTDIR=ncm_test
|
|
|
|
|
endif
|
|
|
|
|
...
|
|
|
|
|
SUBDIRS = ... $(DISPATCHDIR) $(NCM) ... $(NCMTESTDIR)
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
\section choosing_dispatch_table Choosing a Dispatch Table
|
|
|
|
|
|
|
|
|
|
The dispatch table is chosen in the NC_create and the NC_open
|
|
|
|
|
procedures in libdispatch/netcdf.c. The decision is currently based on
|
|
|
|
|
the following pieces of information.
|
|
|
|
|
|
|
|
|
|
The file path – this can be used to detect, for example, a DAP url
|
|
|
|
|
versus a normal file system file.
|
|
|
|
|
|
|
|
|
|
The mode argument – this can be used to detect, for example, what kind
|
|
|
|
|
of file to create: netcdf-3, netcdf-4, 64-bit netcdf-3, etc. For
|
|
|
|
|
nc_open and when the file path references a real file, the contents of
|
|
|
|
|
the file can also be used to determine the dispatch table. Although
|
|
|
|
|
currently not used, this code could be modified to also use other
|
|
|
|
|
pieces of information such as environment variables.
|
|
|
|
|
|
|
|
|
|
In addition to the above, there is one additional mechanism to force
|
|
|
|
|
the use of a specific dispatch table. The procedure
|
|
|
|
|
"NC_set_dispatch_override()" can be invoked to specify a dispatch
|
|
|
|
|
table.
|
|
|
|
|
|
|
|
|
|
When adding a new dispatcher, it is necessary to modify NC_create and
|
|
|
|
|
NC_open in libdispatch/netcdf.c to detect when it is appropriate to
|
|
|
|
|
use the NCM dispatcher. Some possibilities are as follows.
|
|
|
|
|
- Add a new mode flag: say NC_NETCDFM.
|
|
|
|
|
- Use an environment variable.
|
|
|
|
|
- Define a special file path format that indicates the need to use a
|
|
|
|
|
special dispatch table.
|
|
|
|
|
|
|
|
|
|
\section special_dispatch Special Dispatch Table Signatures.
|
|
|
|
|
|
|
|
|
|
Several of the entries in the dispatch table are significantly
|
|
|
|
|
different than those of the external API.
|
|
|
|
|
|
|
|
|
|
\section create_open_dispatch Create/Open
|
|
|
|
|
|
|
|
|
|
The create table entry and the open table entry have the following
|
|
|
|
|
signatures respectively.
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
int (*create)(const char *path, int cmode,
|
|
|
|
|
size_t initialsz, int basepe, size_t *chunksizehintp,
|
|
|
|
|
int useparallel, MPI_Comm comm, MPI_Info info,
|
|
|
|
|
struct NC_Dispatch*, struct NC** ncp);
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
int (*open)(const char *path, int mode,
|
|
|
|
|
int basepe, size_t *chunksizehintp,
|
|
|
|
|
int use_parallel, MPI_Comm comm, MPI_Info info,
|
|
|
|
|
NC_Dispatch*, NC** ncp);
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
The key difference is that these are the union of all the possible
|
|
|
|
|
create/open signatures from the netcdf.h API. Note especially the last
|
|
|
|
|
two parameters. The dispatch table is included in case the create
|
|
|
|
|
function (e.g. NCM_create) needs to invoke other dispatch
|
|
|
|
|
functions. The very last parameter is a pointer to a pointer to an NC
|
|
|
|
|
instance. It is expected that the create function will allocate and
|
|
|
|
|
fill in an instance of an "NC" object and return a pointer to it in
|
|
|
|
|
the ncp parameter.
|
|
|
|
|
|
|
|
|
|
\page dispatch_notes Dispatch Programming Notes
|
|
|
|
|
|
|
|
|
|
As with the existing code, and when MPI is not being used, the comm
|
|
|
|
|
and info parameters should be passed in as 0. This is taken care of in
|
|
|
|
|
the nc_open() and nc_create() API procedures in libdispatch/netcdf.c.
|
|
|
|
|
|
|
|
|
|
In fact, the object returned in the ncp parameter does not actually
|
|
|
|
|
have to be an instance of struct NC. It only needs to "look like it
|
|
|
|
|
for the first few fields. This is, in effect, a fake version of
|
|
|
|
|
subclassing. Let us suppose that the NCM_create function uses a struct
|
|
|
|
|
NCM object. The initial part of the definition of NCM must match the
|
|
|
|
|
fields at the beginning of struct NC between the comments BEGIN_COMMON
|
|
|
|
|
and END_COMMON. So, we would have the following.
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
typedef struct NCM {
|
|
|
|
|
/*BEGIN COMMON*/
|
|
|
|
|
int ext_ncid; /* uid «« 16 */
|
|
|
|
|
int int_ncid; /* unspecified other id */
|
|
|
|
|
struct NC_Dispatch* dispatch;
|
|
|
|
|
#ifdef USE_DAP
|
|
|
|
|
struct NCDRNO* drno;
|
|
|
|
|
#endif
|
|
|
|
|
/*END COMMON*/
|
|
|
|
|
...
|
|
|
|
|
} NCM;
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
This allows the pointer to the NCM object to be cast as an instance of
|
|
|
|
|
NC* and its pointer returned in the ncp file. Eventually, this will be
|
|
|
|
|
replaced with a separate structure containing the common fields.
|
|
|
|
|
|
|
|
|
|
\page put_vara_dispatch Accessing Data with put_vara() and get_vara()
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
int (*put_vara)(int ncid, int varid, const size_t *start, const size_t *count,
|
|
|
|
|
const void *value, nc_type memtype);
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
int (*get_vara)(int ncid, int varid, const size_t *start, const size_t *count,
|
|
|
|
|
void *value, nc_type memtype);
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
Most of the parameters are similar to the netcdf API parameters. The
|
|
|
|
|
last parameter, however, is the type of the data in
|
|
|
|
|
memory. Additionally, instead of using an "int islong" parameter, the
|
|
|
|
|
memtype will be either ::NC_INT or ::NC_INT64, depending on the value
|
|
|
|
|
of sizeof(long). This means that even netcdf-3 code must be prepared
|
|
|
|
|
to encounter the ::NC_INT64 type.
|
|
|
|
|
|
|
|
|
|
\page put_attr_dispatch Accessing Attributes with put_attr() and get_attr()
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
int (*get_att)(int ncid, int varid, const char *name,
|
|
|
|
|
void *value, nc_type memtype);
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
\code
|
|
|
|
|
int (*put_att)(int ncid, int varid, const char *name, nc_type datatype, size_t len,
|
|
|
|
|
const void *value, nc_type memtype);
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
Again, the key difference is the memtype parameter. As with
|
|
|
|
|
put/get_vara, it used ::NC_INT64 to encode the long case.
|
|
|
|
|
|
2011-09-21 01:30:02 +08:00
|
|
|
|
*/
|