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