1/** \file
2
3\internal
4
5\page nc_dispatch Internal Dispatch Table Architecture
6
7\tableofcontents
8
9This document describes the architecture and details of the netCDF
10internal dispatch mechanism. The idea is that when a user opens or
11creates a netcdf file, a specific dispatch table is chosen.
12A dispatch table is a struct containing an entry for every function
13in the netcdf-c API.
14Subsequent netcdf API calls are then channeled through that
15dispatch table to the appropriate function for implementing that API
16call. The functions in the dispatch table are not quite the same
17as those defined in netcdf.h. For simplicity and compactness,
18some netcdf.h API calls are
19mapped to the same dispatch table function. In addition to
20the functions, the first entry in the table defines the model
21that this dispatch table implements. It will be one of the
22NC_FORMATX_XXX values.
23
24The list of supported dispatch tables will grow over time.
25To date, at least the following dispatch tables are supported.
26- netcdf classic files (netcdf-3)
27- netcdf enhanced files (netcdf-4)
28- DAP2 to netcdf-3
29- DAP4 to netcdf-4
30- PnetCDF (parallel I/O for classic files)
31- HDF4 SD files
32
33The dispatch table represents a distillation of the netcdf API down to
34a minimal set of internal operations. The format of the dispatch table
35is defined in the file libdispatch/ncdispatch.h. Every new dispatch
36table must define this minimal set of operations.
37
38\section adding_dispatch Adding a New Dispatch Table
39
40In order to make this process concrete, let us assume we plan to add
41an in-memory implementation of netcdf-3.
42
43\subsection dispatch_configure_ac Defining configure.ac flags
44
45Define a _–-enable_ flag and an _AM_CONFIGURE_ flag in _configure.ac_.
46For our example, we assume the option "--enable-ncm" and the AM_CONFIGURE
47flag "ENABLE_NCM". If you examine the existing _configure.ac_ and see how,
48for example, _DAP2_ is defined, then it should be clear how to do it for
49your code.
50
51\subsection dispatch_namespace Defining a "name space"
52
53Choose some prefix of characters to identify the new dispatch
54system. In effect we are defining a name-space. For our in-memory
55system, we will choose "NCM" and "ncm". NCM is used for non-static
56procedures to be entered into the dispatch table and ncm for all other
57non-static procedures. Note that the chosen prefix should probably start
58with "nc" in order to avoid name conflicts outside the netcdf-c library.
59
60\subsection dispatch_netcdf_h Extend include/netcdf.h
61
62Modify the file _include/netcdf.h_ to add an NC_FORMATX_XXX flag
63by adding a flag for this dispatch format at the appropriate places.
64\code
65    \c \#define NC_FORMATX_NCM  7
66\endcode
67
68Add any format specific new error codes.
69\code
70    \c \#define NC_ENCM  (?)
71\endcode
72
73\subsection dispatch_ncdispatch Extend include/ncdispatch.h
74
75Modify the file _include/ncdispatch.h_ to
76add format specific data and functions; note the use of our NCM namespace.
77\code
78    #ifdef ENABLE_NCM
79    extern NC_Dispatch* NCM_dispatch_table;
80    extern int NCM_initialize(void);
81    #endif
82\endcode
83
84\subsection dispatch_define_code Define the dispatch table functions
85
86Define the functions necessary to fill in the dispatch table. As a
87rule, we assume that a new directory is defined, _libsrcm_, say. Within
88this directory, we need to define _Makefile.am_ and _CMakeLists.txt_.
89We also need to define the source files
90containing the dispatch table and the functions to be placed in the
91dispatch table -– call them _ncmdispatch.c_ and _ncmdispatch.h_. Look at
92_libsrc/nc3dispatch.[ch]_ or _libdap4/ncd4dispatch.[ch]_ for examples.
93
94Similarly, it is best to take existing _Makefile.am_ and _CMakeLists.txt_
95files (from _libdap4_ for example) and modify them.
96
97\subsection dispatch_lib Adding the dispatch code to libnetcdf
98
99Provide for the inclusion of this library in the final libnetcdf
100library. This is accomplished by modifying _liblib/Makefile.am_ by
101adding something like the following.
102\code
103     if ENABLE_NCM
104        libnetcdf_la_LIBADD += $(top_builddir)/libsrcm/libnetcdfm.la
105     endif
106\endcode
107
108\subsection dispatch_init Extend library initialization
109
110Modify the _NC_initialize_ function in _liblib/nc_initialize.c_ by adding
111appropriate references to the NCM dispatch function.
112\code
113     #ifdef ENABLE_NCM
114     extern int NCM_initialize(void);
115     #endif
116     ...
117     int NC_initialize(void)
118     {
119     ...
120     #ifdef ENABLE_NCM
121         if((stat = NCM_initialize())) return stat;
122     #endif
123     ...
124     }
125\endcode
126
127\section dispatch_tests Testing the new dispatch table
128
129Add a directory of tests: _ncm_test_, say. The file _ncm_test/Makefile.am_
130will look something like this.
131\code
132     # These files are created by the tests.
133     CLEANFILES = ...
134     # These are the tests which are always run.
135     TESTPROGRAMS = test1 test2 ...
136     test1_SOURCES = test1.c ...
137     ...
138     # Set up the tests.
139     check_PROGRAMS = $(TESTPROGRAMS)
140     TESTS = $(TESTPROGRAMS)
141     # Any extra files required by the tests
142     EXTRA_DIST = ...
143\endcode
144
145\section dispatch_toplevel Top-Level build of the dispatch code
146
147Provide for _libnetcdfm_ to be constructed by adding the following to
148the top-level _Makefile.am_.
149
150\code
151     if ENABLE_NCM
152     NCM=libsrcm
153     NCMTESTDIR=ncm_test
154     endif
155     ...
156     SUBDIRS = ... $(DISPATCHDIR)  $(NCM) ... $(NCMTESTDIR)
157\endcode
158
159\section choosing_dispatch_table Choosing a Dispatch Table
160
161The dispatch table is chosen in the NC_create and the NC_open
162procedures in _libdispatch/netcdf.c_.
163This can be, unfortunately, a complex process.
164The choice is made in _NC_create_ and _NC_open_ in _libdispatch/dfile.c_.
165
166In any case, the choice of dispatch table is currently based on the following
167pieces of information.
168
1691. The mode argument – this can be used to detect, for example, what kind
170of file to create: netcdf-3, netcdf-4, 64-bit netcdf-3, etc.
171Using a mode flag is the most common mechanism, in which case
172_netcdf.h_ needs to be modified to define the relevant mode flag.
173
1742. The file path – this can be used to detect, for example, a DAP url
175versus a normal file system file. If the path looks like a URL, then
176the choice is determined using the function _NC_urlmodel_.
177
1783. The file contents - when the contents of a real file are available,
179the contents of the file can be used to determine the dispatch table.
180As a rule, this is likely to be useful only for _nc_open_.
181
1824. Environment variables - this option is currently not used,
183but information such as environment variables could be used to determine
184the choice of dispatch table.
185
186\section special_dispatch Special Dispatch Table Signatures.
187
188The entries in the dispatch table do not necessarily correspond
189to the external API. In many cases, multiple related API functions
190are merged into a single dispatch table entry.
191
192\subsection create_open_dispatch Create/Open
193
194The create table entry and the open table entry in the dispatch table
195have the following signatures respectively.
196\code
197     int (*create)(const char *path, int cmode,
198                size_t initialsz, int basepe, size_t *chunksizehintp,
199                int useparallel, void* parameters,
200                struct NC_Dispatch* table, NC* ncp);
201\endcode
202
203\code
204     int (*open)(const char *path, int mode,
205              int basepe, size_t *chunksizehintp,
206              int use_parallel, void* parameters,
207              struct NC_Dispatch* table, NC* ncp);
208\endcode
209
210The key difference is that these are the union of all the possible
211create/open signatures from the include/netcdfXXX.h files. Note especially the last
212three parameters. The parameters argument is a pointer to arbitrary data
213to provide extra info to the dispatcher.
214The table argument is included in case the create
215function (e.g. _NCM_create_) needs to invoke other dispatch
216functions. The very last argument, ncp, is a pointer to an NC
217instance. The raw NC instance will have been created by _libdispatch/dfile.c_
218and is passed to e.g. open with the expectation that it will be filled in
219by the dispatch open function.
220
221\subsection put_vara_dispatch Accessing Data with put_vara() and get_vara()
222
223\code
224     int (*put_vara)(int ncid, int varid, const size_t *start, const size_t *count,
225                          const void *value, nc_type memtype);
226\endcode
227
228\code
229     int (*get_vara)(int ncid, int varid, const size_t *start, const size_t *count,
230                     void *value, nc_type memtype);
231\endcode
232
233Most of the parameters are similar to the netcdf API parameters. The
234last parameter, however, is the type of the data in
235memory. Additionally, instead of using an "int islong" parameter, the
236memtype will be either ::NC_INT or ::NC_INT64, depending on the value
237of sizeof(long). This means that even netcdf-3 code must be prepared
238to encounter the ::NC_INT64 type.
239
240\subsection put_attr_dispatch Accessing Attributes with put_attr() and get_attr()
241
242\code
243     int (*get_att)(int ncid, int varid, const char *name,
244                         void *value, nc_type memtype);
245\endcode
246
247\code
248     int (*put_att)(int ncid, int varid, const char *name, nc_type datatype, size_t len,
249                    const void *value, nc_type memtype);
250\endcode
251
252Again, the key difference is the memtype parameter. As with
253put/get_vara, it used ::NC_INT64 to encode the long case.
254
255\subsection pre_def_dispatch Pre-defined Dispatch Functions
256
257It is sometimes not necessary to implement all the functions in the
258dispatch table. Some pre-defined functions are available which may be
259used in many cases.
260
261\subsubsection inquiry_functions Inquiry Functions
262
263The netCDF inquiry functions operate from an in-memory model of
264metadata. Once a file is opened, or a file is created, this
265in-memory metadata model is kept up to date. Consequenty the inquiry
266functions do not depend on the dispatch layer code. These functions
267can be used by all dispatch layers which use the internal netCDF
268enhanced data model.
269
270- NC4_inq
271- NC4_inq_type
272- NC4_inq_dimid
273- NC4_inq_dim
274- NC4_inq_unlimdim
275- NC4_inq_att
276- NC4_inq_attid
277- NC4_inq_attname
278- NC4_get_att
279- NC4_inq_varid
280- NC4_inq_var_all
281- NC4_show_metadata
282- NC4_inq_unlimdims
283- NC4_inq_ncid
284- NC4_inq_grps
285- NC4_inq_grpname
286- NC4_inq_grpname_full
287- NC4_inq_grp_parent
288- NC4_inq_grp_full_ncid
289- NC4_inq_varids
290- NC4_inq_dimids
291- NC4_inq_typeids
292- NC4_inq_type_equal
293- NC4_inq_user_type
294- NC4_inq_typeid
295
296\subsubsection ncdefault_functions NCDEFAULT get/put Functions
297
298The mapped (varm) get/put functions have been
299implemented in terms of the array (vara) functions. So dispatch layers
300need only implement the vara functions, and can use the following
301functions to get the and varm functions:
302
303- NCDEFAULT_get_varm
304- NCDEFAULT_put_varm
305
306For the netcdf-3 format, the strided functions (nc_get/put_vars)
307are similarly implemented in terms of the vara functions. So the following
308convenience functions are available.
309
310- NCDEFAULT_get_vars
311- NCDEFAULT_put_vars
312
313For the netcdf-4 format, the vars functions actually exist, so
314the default vars functions are not used.
315
316\subsubsection read_only_functions Read-Only Functions
317
318Some dispatch layers are read-only (ex. HDF4). Any function which
319writes to a file, including nc_create(), needs to return error code
320::NC_EPERM. The following read-only functions are available so that
321these don't have to be re-implemented in each read-only dispatch layer:
322
323- NC_RO_create
324- NC_RO_redef
325- NC_RO__enddef
326- NC_RO_sync
327- NC_RO_set_fill
328- NC_RO_def_dim
329- NC_RO_rename_dim
330- NC_RO_rename_att
331- NC_RO_del_att
332- NC_RO_put_att
333- NC_RO_def_var
334- NC_RO_rename_var
335- NC_RO_put_vara
336- NC_RO_def_var_fill
337
338\subsubsection classic_functions Classic NetCDF Only Functions
339
340There are two functions that are only used in the classic code. All
341other dispatch layers (except PnetCDF) return error ::NC_ENOTNC3 for
342these functions. The following functions are provided for this
343purpose:
344
345- NOTNC3_inq_base_pe
346- NOTNC3_set_base_pe
347
348\section dispatch_layer HDF4 Dispatch Layer as a Simple Example
349
350The HDF4 dispatch layer is about the simplest possible dispatch
351layer. It is read-only, classic model. It will serve as a nice, simple
352example of a dispatch layer.
353
354Note that the HDF4 layer is optional in the netCDF build. Not all
355users will have HDF4 installed, and those users will not build with
356the HDF4 dispatch layer enabled. For this reason HDF4 code is guarded
357as follows.
358\code
359\c \#ifdef USE_HDF4
360...
361\c \#endif /*USE_HDF4*/
362\endcode
363
364Code in libhdf4 is only compiled if HDF4 is
365turned on in the build.
366
367\subsection header_changes Header File Changes in include Directory
368
369\subsubsection netcdf_h_file The netcdf.h File
370
371In the main netcdf.h file, we have the following:
372
373\code
374\c \#define NC_FORMATX_NC_HDF4   (3)
375\c \#define NC_FORMAT_NC_HDF4   NC_FORMATX_NC_HDF4
376\endcode
377
378\subsubsection ncdispatch_h_file The ncdispatch.h File
379
380In ncdispatch.h we have the following:
381
382\code
383#ifdef USE_HDF4
384extern NC_Dispatch* HDF4_dispatch_table;
385extern int HDF4_initialize(void);
386extern int HDF4_finalize(void);
387#endif
388\endcode
389
390\subsubsection netcdf_meta_h_file The netcdf_meta.h File
391
392The netcdf_meta.h file allows for easy determination of what features
393are in use. It contains the following, set by configure:
394
395\code
396\c \#define NC_HAS_HDF4      1 /*!< hdf4 support. */
397\endcode
398
399\subsubsection hdf4dispatch_h_file The hdf4dispatch.h File
400
401The file _hdf4dispatch.h_ contains prototypes and
402macro definitions used within the HDF4 code in libhdf4. This include
403file should not be used anywhere except in libhdf4.
404
405\subsection liblib_init Initialization Code Changes in liblib Directory
406
407The file _nc_initialize.c_ is modified to include the following:
408\code
409#ifdef USE_HDF4
410extern int HDF4_initialize(void);
411extern int HDF4_finalize(void);
412#endif
413\endcode
414
415\subsection libdispatch_changes Dispatch Code Changes in libdispatch Directory
416
417\subsubsection dfile_c_changes Changes to dfile.c
418
419In order for a dispatch layer to be used, it must be correctly
420determined in functions _NC_open()_ or _NC_create()_ in _libdispatch/dfile.c_.
421
422HDF4 has a magic number that is detected in
423_NC_interpret_magic_number()_, which allows _NC_open_ to automatically
424detect an HDF4 file.
425
426Once HDF4 is detected, the _model_ variable is set to _NC_FORMATX_NC_HDF4_,
427and later this is used in a case statement:
428
429\code
430      case NC_FORMATX_NC_HDF4:
431         dispatcher = HDF4_dispatch_table;
432         break;
433\endcode
434
435This sets the dispatcher to the HDF4 dispatcher, which is defined in
436the libhdf4 directory.
437
438\subsection libhdf4_dispatch_code Dispatch Code in libhdf4
439
440\subsubsection hdf4dispatch_c_table Dispatch Table in hdf4dispatch.c
441
442The file _hdf4dispatch.c_ contains the definition of the HDF4 dispatch
443table. It looks like this:
444\code
445/* This is the dispatch object that holds pointers to all the
446 * functions that make up the HDF4 dispatch interface. */
447static NC_Dispatch HDF4_dispatcher = {
448NC_FORMATX_NC_HDF4,
449NC_RO_create,
450NC_HDF4_open,
451NC_RO_redef,
452NC_RO__enddef,
453NC_RO_sync,
454
455...
456
457NC_NOTNC4_set_var_chunk_cache,
458NC_NOTNC4_get_var_chunk_cache,
459};
460\endcode
461
462Note that most functions use some of the predefined dispatch
463functions. Functions that start with NC_RO_ are read-only, they return
464::NC_EPERM. Functions that start with NOTNC4_ return ::NC_ENOTNC4.
465
466Only the functions that start with NC_HDF4_ need to be implemented for
467the HDF4 dispatch layer. There are 6 such functions:
468
469- NC_HDF4_open
470- NC_HDF4_abort
471- NC_HDF4_close
472- NC_HDF4_inq_format
473- NC_HDF4_inq_format_extended
474- NC_HDF4_get_vara
475
476\subsubsection hdf4_reading_code HDF4 Reading Code
477
478The code in _hdf4file.c_ opens the HDF4 SD dataset, and reads the
479metadata. This metadata is stored in the netCDF internal metadata
480model, allowing the inq functions to work.
481
482The code in _hdf4var.c_ does an _nc_get_vara()_ on the HDF4 SD
483dataset. This is all that is needed for all the nc_get_* functions to
484work.
485
486*/
487