1/*
2This file is part of netcdf-4, a netCDF-like interface for HDF5, or a
3HDF5 backend for netCDF, depending on your point of view.
4
5This file handles the nc4 variable functions.
6
7Copyright 2003-2006, University Corporation for Atmospheric
8Research. See COPYRIGHT file for copying and redistribution
9conditions.
10*/
11
12#include <nc4internal.h>
13#include "nc4dispatch.h"
14#include <math.h>
15
16/* Min and max deflate levels tolerated by HDF5. */
17#define MIN_DEFLATE_LEVEL 0
18#define MAX_DEFLATE_LEVEL 9
19
20/* This is to track opened HDF5 objects to make sure they are
21 * closed. */
22#ifdef EXTRA_TESTS
23extern int num_plists;
24#endif /* EXTRA_TESTS */
25
26/* One meg is the minimum buffer size. */
27#define ONE_MEG 1048576
28
29/* Szip options. */
30#define NC_SZIP_EC_OPTION_MASK 4
31#define NC_SZIP_NN_OPTION_MASK 32
32#define NC_SZIP_MAX_PIXELS_PER_BLOCK 32
33
34extern int nc4_get_default_fill_value(const NC_TYPE_INFO_T *type_info, void *fill_value);
35
36
37/* If the HDF5 dataset for this variable is open, then close it and
38 * reopen it, with the perhaps new settings for chunk caching. */
39int
40nc4_reopen_dataset(NC_GRP_INFO_T *grpNC_VAR_INFO_T *var)
41{
42   hid_t access_pid;
43
44   if (var->hdf_datasetid)
45   {
46      if ((access_pid = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
47  return NC_EHDFERR;
48#ifdef EXTRA_TESTS
49      num_plists++;
50#endif
51      if (H5Pset_chunk_cache(access_pidvar->chunk_cache_nelems,
52      var->chunk_cache_size,
53      var->chunk_cache_preemption) < 0)
54  return NC_EHDFERR;
55      if (H5Dclose(var->hdf_datasetid) < 0)
56  return NC_EHDFERR;
57      if ((var->hdf_datasetid = H5Dopen2(grp->hdf_grpidvar->name,
58  access_pid)) < 0)
59  return NC_EHDFERR;
60      if (H5Pclose(access_pid) < 0)
61  return NC_EHDFERR;
62#ifdef EXTRA_TESTS
63      num_plists--;
64#endif
65   }
66
67   return NC_NOERR;
68}
69
70/* Set chunk cache size for a variable. */
71int
72NC4_set_var_chunk_cache(int ncid, int varid, size_t size, size_t nelems,
73 float preemption)
74{
75   NC *nc;
76   NC_GRP_INFO_T *grp;
77   NC_HDF5_FILE_INFO_T *h5;
78   NC_VAR_INFO_T *var;
79   int retval;
80
81   /* Check input for validity. */
82   if (preemption < 0 || preemption > 1)
83      return NC_EINVAL;
84
85   /* Find info for this file and group, and set pointer to each. */
86   if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
87      return retval;
88
89   /* An attempt to do any of these things on a netCDF-3 file is
90    * ignored with no error. */
91   if (!h5)
92      return NC_NOERR;
93
94   assert(nc && grp && h5);
95
96   /* Find the var. */
97   for (var = grp->varvarvar = var->l.next)
98      if (var->varid == varid)
99         break;
100   if (!var)
101      return NC_ENOTVAR;
102
103   /* Set the values. */
104   var->chunk_cache_size = size;
105   var->chunk_cache_nelems = nelems;
106   var->chunk_cache_preemption = preemption;
107
108   if ((retval = nc4_reopen_dataset(grpvar)))
109      return retval;
110
111   return NC_NOERR;
112}
113
114/* Need this version for fortran. Accept negative numbers to leave
115 * settings as they are. */
116int
117nc_set_var_chunk_cache_ints(int ncid, int varid, int size, int nelems,
118     int preemption)
119{
120   size_t real_size = H5D_CHUNK_CACHE_NBYTES_DEFAULT;
121   size_t real_nelems = H5D_CHUNK_CACHE_NSLOTS_DEFAULT;
122   float real_preemption = H5D_CHUNK_CACHE_W0_DEFAULT;
123
124   if (size >= 0)
125       real_size = ((size_t) size) * MEGABYTE;
126
127   if (nelems >= 0)
128      real_nelems = nelems;
129
130   if (preemption >= 0)
131      real_preemption = preemption / 100.;
132
133   return NC4_set_var_chunk_cache(ncidvaridreal_sizereal_nelems,
134  real_preemption);
135}
136
137/* Get chunk cache size for a variable. */
138int
139NC4_get_var_chunk_cache(int ncid, int varid, size_t *sizep,
140 size_t *nelemsp, float *preemptionp)
141{
142   NC *nc;
143   NC_GRP_INFO_T *grp;
144   NC_HDF5_FILE_INFO_T *h5;
145   NC_VAR_INFO_T *var;
146   int retval;
147
148   /* Find info for this file and group, and set pointer to each. */
149   if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
150      return retval;
151
152   /* Attempting to do any of these things on a netCDF-3 file produces
153    * an error. */
154   if (!h5)
155      return NC_ENOTNC4;
156
157   assert(nc && grp && h5);
158
159   /* Find the var. */
160   for (var = grp->varvarvar = var->l.next)
161      if (var->varid == varid)
162         break;
163   if (!var)
164      return NC_ENOTVAR;
165
166   /* Give the user what they want. */
167   if (sizep)
168      *sizep = var->chunk_cache_size;
169   if (nelemsp)
170      *nelemsp = var->chunk_cache_nelems;
171   if (preemptionp)
172      *preemptionp = var->chunk_cache_preemption;
173
174   return NC_NOERR;
175}
176
177/* Get chunk cache size for a variable. */
178int
179nc_get_var_chunk_cache_ints(int ncid, int varid, int *sizep,
180     int *nelemsp, int *preemptionp)
181{
182   size_t real_sizereal_nelems;
183   float real_preemption;
184   int ret;
185
186   if ((ret = NC4_get_var_chunk_cache(ncidvarid, &real_size,
187      &real_nelems, &real_preemption)))
188      return ret;
189
190   if (sizep)
191      *sizep = real_size / MEGABYTE;
192   if (nelemsp)
193      *nelemsp = (int)real_nelems;
194   if(preemptionp)
195      *preemptionp = (int)(real_preemption * 100);
196
197   return NC_NOERR;
198}
199
200/* Check a set of chunksizes to see if they specify a chunk that is too big. */
201static int
202check_chunksizes(NC_GRP_INFO_T *grpNC_VAR_INFO_T *var, const size_t *chunksizes)
203{
204   double dprod;
205   size_t type_len;
206   int d;
207   int retval;
208
209   if ((retval = nc4_get_typelen_mem(grp->nc4_infovar->type_info->nc_typeid, 0, &type_len)))
210      return retval;
211   if (var->type_info->nc_type_class == NC_VLEN)
212       dprod = (double)sizeof(hvl_t);
213   else
214       dprod = (double)type_len;
215   for (d = 0; d < var->ndimsd++)
216   {
217      if (chunksizes[d] < 1)
218  return NC_EINVAL;
219      dprod *= (double) chunksizes[d];
220   }
221
222   if (dprod > (double) NC_MAX_UINT)
223      return NC_EBADCHUNK;
224
225   return NC_NOERR;
226}
227
228/* Find the default chunk nelems (i.e. length of chunk along each
229 * dimension). */
230static int
231nc4_find_default_chunksizes2(NC_GRP_INFO_T *grpNC_VAR_INFO_T *var)
232{
233   int d;
234   size_t type_size;
235   float num_values = 1, num_unlim = 0;
236   int retval;
237   size_t suggested_size;
238#ifdef LOGGING
239   double total_chunk_size;
240#endif
241
242   if (var->type_info->nc_type_class == NC_STRING)
243      type_size = sizeof(char *);
244   else
245      type_size = var->type_info->size;
246
247#ifdef LOGGING
248   /* Later this will become the total number of bytes in the default
249    * chunk. */
250   total_chunk_size = (double) type_size;
251#endif
252
253   /* How many values in the variable (or one record, if there are
254    * unlimited dimensions). */
255   for (d = 0; d < var->ndimsd++)
256   {
257      assert(var->dim[d]);
258      if (! var->dim[d]->unlimited)
259  num_values *= (float)var->dim[d]->len;
260      else {
261   num_unlim++;
262   var->chunksizes[d] = 1; /* overwritten below, if all dims are unlimited */
263      }
264   }
265   /* Special case to avoid 1D vars with unlim dim taking huge amount
266      of space (DEFAULT_CHUNK_SIZE bytes). Instead we limit to about
267      4KB */
268#define DEFAULT_1D_UNLIM_SIZE (4096) /* TODO: make build-time parameter? */
269   if (var->ndims == 1 && num_unlim == 1) {
270       if (DEFAULT_CHUNK_SIZE / type_size <= 0)
271  suggested_size = 1;
272       else if (DEFAULT_CHUNK_SIZE / type_size > DEFAULT_1D_UNLIM_SIZE)
273  suggested_size = DEFAULT_1D_UNLIM_SIZE;
274       else
275  suggested_size = DEFAULT_CHUNK_SIZE / type_size;
276       var->chunksizes[0] = suggested_size / type_size;
277       LOG((4, "%s: name %s dim %d DEFAULT_CHUNK_SIZE %d num_values %f type_size %d "
278     "chunksize %ld", __func__var->namedDEFAULT_CHUNK_SIZEnum_valuestype_sizevar->chunksizes[0]));
279   }
280   if (var->ndims > 1 && var->ndims == num_unlim) { /* all dims unlimited */
281       suggested_size = pow((double)DEFAULT_CHUNK_SIZE/type_size, 1.0/(double)(var->ndims));
282       for (d = 0; d < var->ndimsd++)
283       {
284    var->chunksizes[d] = suggested_size ? suggested_size : 1;
285    LOG((4, "%s: name %s dim %d DEFAULT_CHUNK_SIZE %d num_values %f type_size %d "
286 "chunksize %ld", __func__var->namedDEFAULT_CHUNK_SIZEnum_valuestype_sizevar->chunksizes[d]));
287       }
288   }
289
290   /* Pick a chunk length for each dimension, if one has not already
291    * been picked above. */
292   for (d = 0; d < var->ndimsd++)
293      if (!var->chunksizes[d])
294      {
295  suggested_size = (pow((double)DEFAULT_CHUNK_SIZE/(num_values * type_size),
296        1.0/(double)(var->ndims - num_unlim)) * var->dim[d]->len - .5);
297  if (suggested_size > var->dim[d]->len)
298     suggested_size = var->dim[d]->len;
299  var->chunksizes[d] = suggested_size ? suggested_size : 1;
300  LOG((4, "%s: name %s dim %d DEFAULT_CHUNK_SIZE %d num_values %f type_size %d "
301       "chunksize %ld", __func__var->namedDEFAULT_CHUNK_SIZEnum_valuestype_sizevar->chunksizes[d]));
302      }
303
304#ifdef LOGGING
305   /* Find total chunk size. */
306   for (d = 0; d < var->ndimsd++)
307       total_chunk_size *= (double) var->chunksizes[d];
308   LOG((4, "total_chunk_size %f", total_chunk_size));
309#endif
310
311   /* But did this result in a chunk that is too big? */
312   retval = check_chunksizes(grpvarvar->chunksizes);
313   if (retval)
314   {
315      /* Other error? */
316      if (retval != NC_EBADCHUNK)
317  return retval;
318
319      /* Chunk is too big! Reduce each dimension by half and try again. */
320      for ( ; retval == NC_EBADCHUNKretval = check_chunksizes(grpvarvar->chunksizes))
321      for (d = 0; d < var->ndimsd++)
322     var->chunksizes[d] = var->chunksizes[d]/2 ? var->chunksizes[d]/2 : 1;
323   }
324
325   /* Do we have any big data overhangs? They can be dangerous to
326    * babies, the elderly, or confused campers who have had too much
327    * beer. */
328   for (d = 0; d < var->ndimsd++)
329   {
330       size_t num_chunks;
331       size_t overhang;
332       assert(var->chunksizes[d] > 0);
333       num_chunks = (var->dim[d]->len + var->chunksizes[d] - 1) / var->chunksizes[d];
334       if(num_chunks > 0) {
335    overhang = (num_chunks * var->chunksizes[d]) - var->dim[d]->len;
336    var->chunksizes[d] -= overhang / num_chunks;
337       }
338   }
339
340   return NC_NOERR;
341}
342
343/* This is called when a new netCDF-4 variable is defined. Break it
344 * down! */
345static int
346nc_def_var_nc4(int ncid, const char *namenc_type xtype,
347               int ndims, const int *dimidsp, int *varidp)
348{
349   NC_GRP_INFO_T *grp;
350   NC_VAR_INFO_T *var;
351   NC_DIM_INFO_T *dim;
352   NC_HDF5_FILE_INFO_T *h5;
353   NC_TYPE_INFO_T *type_info = NULL;
354   char norm_name[NC_MAX_NAME + 1];
355   int d;
356   int retval;
357
358   /* Find info for this file and group, and set pointer to each. */
359   if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
360      BAIL(retval);
361   assert(grp && h5);
362
363   /* If it's not in define mode, strict nc3 files error out,
364    * otherwise switch to define mode. */
365   if (!(h5->flags & NC_INDEF))
366   {
367      if (h5->cmode & NC_CLASSIC_MODEL)
368         BAIL(NC_ENOTINDEFINE);
369      if ((retval = NC4_redef(ncid)))
370         BAIL(retval);
371   }
372
373   /* Check and normalize the name. */
374   if ((retval = nc4_check_name(namenorm_name)))
375      BAIL(retval);
376
377   /* Not a Type is, well, not a type.*/
378   if (xtype == NC_NAT)
379      BAIL(NC_EBADTYPE);
380
381   /* For classic files, only classic types are allowed. */
382   if (h5->cmode & NC_CLASSIC_MODEL && xtype > NC_DOUBLE)
383      BAIL(NC_ESTRICTNC3);
384
385   /* cast needed for braindead systems with signed size_t */
386   if((unsigned long) ndims > X_INT_MAX) /* Backward compat */
387      BAIL(NC_EINVAL);
388
389   /* Classic model files have a limit on number of vars. */
390   if(h5->cmode & NC_CLASSIC_MODEL && h5->nvars >= NC_MAX_VARS)
391      BAIL(NC_EMAXVARS);
392
393   /* Check that this name is not in use as a var, grp, or type. */
394   if ((retval = nc4_check_dup_name(grpnorm_name)))
395      BAIL(retval);
396
397   /* If the file is read-only, return an error. */
398   if (h5->no_write)
399     BAIL(NC_EPERM);
400
401   /* Check all the dimids to make sure they exist. */
402   for (d = 0; d < ndimsd++)
403      if ((retval = nc4_find_dim(grpdimidsp[d], &dimNULL)))
404         BAIL(retval);
405
406   /* These degrubbing messages sure are handy! */
407   LOG((3, "%s: name %s type %d ndims %d", __func__norm_namextypendims));
408#ifdef LOGGING
409   {
410      int dd;
411      for (dd = 0; dd < ndimsdd++)
412         LOG((4, "dimid[%d] %d", dddimidsp[dd]));
413   }
414#endif
415
416   /* Add the var to the end of the list. */
417   if ((retval = nc4_var_list_add(&grp->var, &var)))
418      BAIL(retval);
419
420   /* Now fill in the values in the var info structure. */
421   if (!(var->name = malloc((strlen(norm_name) + 1) * sizeof(char))))
422      BAIL(NC_ENOMEM);
423   strcpy(var->namenorm_name);
424   var->hash = hash_fast(norm_name, strlen(norm_name));
425   var->varid = grp->nvars++;
426   var->ndims = ndims;
427   var->is_new_var = NC_TRUE;
428
429   /* If this is a user-defined type, there is a type_info struct with
430    * all the type information. For atomic types, fake up a type_info
431    * struct. */
432   if (xtype <= NC_STRING)
433   {
434      if (!(type_info = calloc(1, sizeof(NC_TYPE_INFO_T))))
435  BAIL(NC_ENOMEM);
436      type_info->nc_typeid = xtype;
437      type_info->endianness = NC_ENDIAN_NATIVE;
438      if ((retval = nc4_get_hdf_typeid(h5xtype, &type_info->hdf_typeid,
439        type_info->endianness)))
440  BAIL(retval);
441      if ((type_info->native_hdf_typeid = H5Tget_native_type(type_info->hdf_typeid,
442       H5T_DIR_DEFAULT)) < 0)
443         BAIL(NC_EHDFERR);
444      if ((retval = nc4_get_typelen_mem(h5type_info->nc_typeid, 0,
445 &type_info->size)))
446  BAIL(retval);
447
448      /* Set the "class" of the type */
449      if (xtype == NC_CHAR)
450         type_info->nc_type_class = NC_CHAR;
451      else
452      {
453         H5T_class_t class;
454
455         if ((class = H5Tget_class(type_info->hdf_typeid)) < 0)
456            BAIL(NC_EHDFERR);
457         switch(class)
458         {
459            case H5T_STRING:
460               type_info->nc_type_class = NC_STRING;
461               break;
462
463            case H5T_INTEGER:
464               type_info->nc_type_class = NC_INT;
465               break;
466
467            case H5T_FLOAT:
468               type_info->nc_type_class = NC_FLOAT;
469               break;
470
471            default:
472               BAIL(NC_EBADTYPID);
473         }
474      }
475   }
476   /* If this is a user defined type, find it. */
477   else
478   {
479      if (nc4_find_type(grp->nc4_infoxtype, &type_info))
480         BAIL(NC_EBADTYPE);
481   }
482
483  /* Point to the type, and increment its ref. count */
484  var->type_info = type_info;
485  var->type_info->rc++;
486  type_info = NULL;
487
488   /* Allocate space for dimension information. */
489   if (ndims)
490   {
491      if (!(var->dim = calloc(ndims, sizeof(NC_DIM_INFO_T *))))
492  BAIL(NC_ENOMEM);
493      if (!(var->dimids = calloc(ndims, sizeof(int))))
494  BAIL(NC_ENOMEM);
495   }
496
497   /* Assign dimensions to the variable */
498   /* At the same time, check to see if this is a coordinate
499    * variable. If so, it will have the same name as one of its
500    * dimensions. If it is a coordinate var, is it a coordinate var in
501    * the same group as the dim? */
502   /* Also, check whether we should use contiguous or chunked storage */
503   var->contiguous = NC_TRUE;
504   for (d = 0; d < ndimsd++)
505   {
506      NC_GRP_INFO_T *dim_grp;
507
508      /* Look up each dimension */
509      if ((retval = nc4_find_dim(grpdimidsp[d], &dim, &dim_grp)))
510         BAIL(retval);
511
512      /* Check for dim index 0 having the same name, in the same group */
513      if (d == 0 && dim_grp == grp && dim->hash == var->hash && strcmp(dim->namenorm_name) == 0)
514      {
515         var->dimscale = NC_TRUE;
516         dim->coord_var = var;
517
518         /* Use variable's dataset ID for the dimscale ID */
519         if (dim->hdf_dimscaleid)
520         {
521            /* Detach dimscale from any variables using it */
522            if ((retval = rec_detach_scales(grpdimidsp[d], dim->hdf_dimscaleid)) < 0)
523               BAIL(retval);
524
525            if (H5Dclose(dim->hdf_dimscaleid) < 0)
526                BAIL(NC_EHDFERR);
527            dim->hdf_dimscaleid = 0;
528
529            /* Now delete the dataset (it will be recreated later, if necessary) */
530            if (H5Gunlink(grp->hdf_grpiddim->name) < 0)
531               BAIL(NC_EDIMMETA);
532         }
533      }
534
535      /* Check for unlimited dimension and turn off contiguous storage */
536      /* (unless HDF4 file) */
537#ifdef USE_HDF4
538      if (dim->unlimited && !h5->hdf4)
539#else
540      if (dim->unlimited)
541#endif
542  var->contiguous = NC_FALSE;
543
544      /* Track dimensions for variable */
545      var->dimids[d] = dimidsp[d];
546      var->dim[d] = dim;
547   }
548
549   /* Determine default chunksizes for this variable. (Even for
550    * variables which may be contiguous. */
551   LOG((4, "allocating array of %d size_t to hold chunksizes for var %s",
552 var->ndimsvar->name));
553   if (var->ndims)
554      if (!(var->chunksizes = calloc(var->ndims, sizeof(size_t))))
555  BAIL(NC_ENOMEM);
556
557   if ((retval = nc4_find_default_chunksizes2(grpvar)))
558      BAIL(retval);
559
560   /* Is this a variable with a chunksize greater than the current
561    * cache size? */
562   if ((retval = nc4_adjust_var_cache(grpvar)))
563      BAIL(retval);
564
565   /* If the user names this variable the same as a dimension, but
566    * doesn't use that dimension first in its list of dimension ids,
567    * is not a coordinate variable. I need to change its HDF5 name,
568    * because the dimension will cause a HDF5 dataset to be created,
569    * and this var has the same name. */
570   for (dim = grp->dimdimdim = dim->l.next)
571      if (dim->hash == var->hash && !strcmp(dim->namenorm_name) &&
572   (!var->ndims || dimidsp[0] != dim->dimid))
573      {
574  /* Set a different hdf5 name for this variable to avoid name
575   * clash. */
576  if (strlen(norm_name) + strlen(NON_COORD_PREPEND) > NC_MAX_NAME)
577     BAIL(NC_EMAXNAME);
578  if (!(var->hdf5_name = malloc((strlen(NON_COORD_PREPEND) +
579 strlen(norm_name) + 1) * sizeof(char))))
580     BAIL(NC_ENOMEM);
581
582  sprintf(var->hdf5_name, "%s%s", NON_COORD_PREPENDnorm_name);
583      }
584
585   /* If this is a coordinate var, it is marked as a HDF5 dimension
586    * scale. (We found dim above.) Otherwise, allocate space to
587    * remember whether dimension scales have been attached to each
588    * dimension. */
589   if (!var->dimscale && ndims)
590      if (ndims && !(var->dimscale_attached = calloc(ndims, sizeof(nc_bool_t))))
591         BAIL(NC_ENOMEM);
592
593   /* Return the varid. */
594   if (varidp)
595      *varidp = var->varid;
596   LOG((4, "new varid %d", var->varid));
597
598exit:
599   if (type_info)
600      if ((retval = nc4_type_free(type_info)))
601         BAIL2(retval);
602
603   return retval;
604}
605
606/* Create a new variable to hold user data. This is what it's all
607 * about baby! */
608int
609NC4_def_var(int ncid, const char *namenc_type xtype, int ndims,
610           const int *dimidsp, int *varidp)
611{
612   NC *nc;
613   NC_HDF5_FILE_INFO_T *h5;
614
615   LOG((2, "%s: ncid 0x%x name %s xtype %d ndims %d",
616        __func__ncidnamextypendims));
617
618   /* If there are dimensions, I need their ids. */
619   if (ndims && !dimidsp)
620      return NC_EINVAL;
621
622   /* Find metadata for this file. */
623   if (!(nc = nc4_find_nc_file(ncid,&h5)))
624      return NC_EBADID;
625
626   /* Handle netcdf-4 cases. */
627   return nc_def_var_nc4(ncidnamextypendimsdimidspvaridp);
628}
629
630/* Get all the information about a variable. Pass NULL for whatever
631 * you don't care about. This is an internal function, not exposed to
632 * the user. */
633int
634NC4_inq_var_all(int ncid, int varid, char *namenc_type *xtypep,
635               int *ndimsp, int *dimidsp, int *nattsp,
636               int *shufflep, int *deflatep, int *deflate_levelp,
637               int *fletcher32p, int *contiguousp, size_t *chunksizesp,
638               int *no_fill, void *fill_valuep, int *endiannessp,
639        int *options_maskp, int *pixels_per_blockp)
640{
641   NC *nc;
642   NC_GRP_INFO_T *grp;
643   NC_HDF5_FILE_INFO_T *h5;
644   NC_VAR_INFO_T *var;
645   NC_ATT_INFO_T *att;
646   int natts=0;
647   int d;
648   int retval;
649
650   LOG((2, "%s: ncid 0x%x varid %d", __func__ncidvarid));
651
652   /* Find info for this file and group, and set pointer to each. */
653   if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
654      return retval;
655
656   assert(nc);
657   assert(grp && h5);
658
659   /* Walk through the list of vars, and return the info about the one
660      with a matching varid. If the varid is -1, find the global
661      atts and call it a day. */
662   if (varid == NC_GLOBAL)
663   {
664      if (nattsp)
665      {
666         for (att = grp->attattatt = att->l.next)
667            natts++;
668         *nattsp = natts;
669      }
670      return NC_NOERR;
671   }
672
673   /* Find the var. */
674   for (var = grp->varvarvar = var->l.next)
675      if (var->varid == varid)
676         break;
677
678   /* Oh no! Maybe we couldn't find it (*sob*)! */
679   if (!var)
680      return NC_ENOTVAR;
681
682   /* Copy the data to the user's data buffers. */
683   if (name)
684      strcpy(namevar->name);
685   if (xtypep)
686      *xtypep = var->type_info->nc_typeid;
687   if (ndimsp)
688      *ndimsp = var->ndims;
689   if (dimidsp)
690      for (d = 0; d < var->ndimsd++)
691         dimidsp[d] = var->dimids[d];
692   if (nattsp)
693   {
694      for (att = var->attattatt = att->l.next)
695         natts++;
696      *nattsp = natts;
697   }
698
699   /* Chunking stuff. */
700   if (!var->contiguous && chunksizesp)
701      for (d = 0; d < var->ndimsd++)
702      {
703         chunksizesp[d] = var->chunksizes[d];
704         LOG((4, "chunksizesp[%d]=%d", dchunksizesp[d]));
705      }
706
707   if (contiguousp)
708      *contiguousp = var->contiguous ? NC_CONTIGUOUS : NC_CHUNKED;
709
710   /* Filter stuff. */
711   if (deflatep)
712      *deflatep = (int)var->deflate;
713   if (deflate_levelp)
714      *deflate_levelp = var->deflate_level;
715   if (shufflep)
716      *shufflep = (int)var->shuffle;
717   if (fletcher32p)
718      *fletcher32p = (int)var->fletcher32;
719   /* NOTE: No interface for returning szip flag currently (but it should never
720    *   be set).
721    */
722   if (options_maskp)
723      *options_maskp = var->options_mask;
724   if (pixels_per_blockp)
725      *pixels_per_blockp = var->pixels_per_block;
726
727   /* Fill value stuff. */
728   if (no_fill)
729      *no_fill = (int)var->no_fill;
730
731   /* Don't do a thing with fill_valuep if no_fill mode is set for
732    * this var, or if fill_valuep is NULL. */
733   if (!var->no_fill && fill_valuep)
734   {
735      /* Do we have a fill value for this var? */
736     if (var->fill_value)
737       {
738         if (var->type_info->nc_type_class == NC_STRING)
739           {
740             if (*(char **)var->fill_value) {
741
742               if (!(fill_valuep = calloc(1, sizeof(char *))))
743                 return NC_ENOMEM;
744
745               if (!(*(char **)fill_valuep = strdup(*(char **)var->fill_value)))
746                 {
747                   free(fill_valuep);
748                   return NC_ENOMEM;
749                 }
750             }
751           }
752         else {
753             assert(var->type_info->size);
754             memcpy(fill_valuepvar->fill_valuevar->type_info->size);
755         }
756      }
757      else
758      {
759         if (var->type_info->nc_type_class == NC_STRING)
760         {
761            if (!(fill_valuep = calloc(1, sizeof(char *))))
762               return NC_ENOMEM;
763
764            if ((retval = nc4_get_default_fill_value(var->type_info, (char **)fill_valuep)))
765            {
766               free(fill_valuep);
767               return retval;
768            } else {
769       free(fill_valuep);
770     }
771         }
772         else
773         {
774            if ((retval = nc4_get_default_fill_value(var->type_infofill_valuep)))
775               return retval;
776         }
777      }
778   }
779
780   /* Does the user want the endianness of this variable? */
781   if (endiannessp)
782      *endiannessp = var->type_info->endianness;
783
784   return NC_NOERR;
785}
786
787/* This functions sets extra stuff about a netCDF-4 variable which
788   must be set before the enddef but after the def_var. This is an
789   internal function, deliberately hidden from the user so that we can
790   change the prototype of this functions without changing the API. */
791static int
792nc_def_var_extra(int ncid, int varid, int *shuffle, int *deflate,
793  int *deflate_level, int *fletcher32, int *contiguous,
794  const size_t *chunksizes, int *no_fill,
795                 const void *fill_value, int *endianness)
796{
797   NC *nc;
798   NC_GRP_INFO_T *grp;
799   NC_HDF5_FILE_INFO_T *h5;
800   NC_VAR_INFO_T *var;
801   NC_DIM_INFO_T *dim;
802   int d;
803   int retval;
804   nc_bool_t ishdf4 = NC_FALSE; /* Use this to avoid so many ifdefs */
805
806   LOG((2, "%s: ncid 0x%x varid %d", __func__ncidvarid));
807
808   /* Find info for this file and group, and set pointer to each. */
809   if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
810      return retval;
811
812#ifdef USE_HDF4
813   ishdf4 = h5->hdf4;
814#endif
815
816   /* Attempting to do any of these things on a netCDF-3 file produces
817    * an error. */
818   if (!h5)
819      return NC_ENOTNC4;
820
821   assert(nc && grp && h5);
822
823   /* Find the var. */
824   for (var = grp->varvarvar = var->l.next)
825      if (var->varid == varid)
826         break;
827
828   /* Oh no! Maybe we couldn't find it (*sob*)! */
829   if (!var)
830      return NC_ENOTVAR;
831
832   /* Can't turn on contiguous and deflate/fletcher32/szip. */
833   if (contiguous)
834      if ((*contiguous != NC_CHUNKED && deflate) ||
835   (*contiguous != NC_CHUNKED && fletcher32))
836  return NC_EINVAL;
837
838   /* Can't turn on parallel and deflate/fletcher32/szip/shuffle. */
839   if (nc->mode & (NC_MPIIO | NC_MPIPOSIX)) {
840      if (deflate || fletcher32 || shuffle)
841  return NC_EINVAL;
842   }
843
844   /* If the HDF5 dataset has already been created, then it is too
845    * late to set all the extra stuff. */
846   if (var->created)
847      return NC_ELATEDEF;
848
849   /* Check compression options. */
850   if (deflate && !deflate_level)
851      return NC_EINVAL;
852
853   /* Valid deflate level? */
854   if (deflate && deflate_level)
855   {
856      if (*deflate)
857         if (*deflate_level < MIN_DEFLATE_LEVEL ||
858             *deflate_level > MAX_DEFLATE_LEVEL)
859            return NC_EINVAL;
860
861      /* For scalars, just ignore attempt to deflate. */
862      if (!var->ndims)
863            return NC_NOERR;
864
865      /* Well, if we couldn't find any errors, I guess we have to take
866       * the users settings. Darn! */
867      var->contiguous = NC_FALSE;
868      var->deflate = *deflate;
869      if (*deflate)
870         var->deflate_level = *deflate_level;
871      LOG((3, "%s: *deflate_level %d", __func__, *deflate_level));
872   }
873
874   /* Shuffle filter? */
875   if (shuffle)
876   {
877      var->shuffle = *shuffle;
878      var->contiguous = NC_FALSE;
879   }
880
881   /* Fletcher32 checksum error protection? */
882   if (fletcher32)
883   {
884      var->fletcher32 = *fletcher32;
885      var->contiguous = NC_FALSE;
886   }
887
888   /* Does the user want a contiguous dataset? Not so fast! Make sure
889    * that there are no unlimited dimensions, and no filters in use
890    * for this data. */
891   if (contiguous && *contiguous)
892   {
893      if (var->deflate || var->fletcher32 || var->shuffle)
894  return NC_EINVAL;
895
896     if (!ishdf4) {
897      for (d = 0; d < var->ndimsd++)
898      {
899  dim = var->dim[d];
900  if (dim->unlimited)
901     return NC_EINVAL;
902      }
903      var->contiguous = NC_TRUE;
904    }
905   }
906
907   /* Chunksizes anyone? */
908   if (!ishdf4 && contiguous && *contiguous == NC_CHUNKED)
909   {
910      var->contiguous = NC_FALSE;
911
912      /* If the user provided chunksizes, check that they are not too
913       * big, and that their total size of chunk is less than 4 GB. */
914      if (chunksizes)
915      {
916
917  if ((retval = check_chunksizes(grpvarchunksizes)))
918     return retval;
919  for (d = 0; d < var->ndimsd++) {
920     if(var->dim[d]->len > 0 && chunksizes[d] > var->dim[d]->len)
921        return NC_EBADCHUNK;
922  }
923
924  /* Set the chunksizes for this variable. */
925  for (d = 0; d < var->ndimsd++)
926     var->chunksizes[d] = chunksizes[d];
927      }
928   }
929
930   /* Is this a variable with a chunksize greater than the current
931    * cache size? */
932   if (!var->contiguous && (chunksizes || deflate || contiguous))
933   {
934      /* Determine default chunksizes for this variable. */
935      if (!var->chunksizes[0])
936  if ((retval = nc4_find_default_chunksizes2(grpvar)))
937     return retval;
938
939      /* Adjust the cache. */
940      if ((retval = nc4_adjust_var_cache(grpvar)))
941  return retval;
942   }
943
944   /* Are we setting a fill modes? */
945   if (no_fill)
946   {
947      if (*no_fill)
948         var->no_fill = NC_TRUE;
949      else
950         var->no_fill = NC_FALSE;
951   }
952
953   /* Are we setting a fill value? */
954   if (fill_value && !var->no_fill)
955   {
956      /* Copy the fill_value. */
957      LOG((4, "Copying fill value into metadata for variable %s",
958           var->name));
959
960      /* If there's a _FillValue attribute, delete it. */
961      retval = NC4_del_att(ncidvarid_FillValue);
962      if (retval && retval != NC_ENOTATT)
963         return retval;
964
965      /* Create a _FillValue attribute. */
966      if ((retval = nc_put_att(ncidvarid_FillValuevar->type_info->nc_typeid, 1, fill_value)))
967         return retval;
968   }
969
970   /* Is the user setting the endianness? */
971   if (endianness)
972      var->type_info->endianness = *endianness;
973
974   return NC_NOERR;
975}
976
977/* Set the deflate level for a var, lower is faster, higher is
978 * better. Must be called after nc_def_var and before nc_enddef or any
979 * functions which writes data to the file. */
980int
981NC4_def_var_deflate(int ncid, int varid, int shuffle, int deflate,
982                   int deflate_level)
983{
984   return nc_def_var_extra(ncidvarid, &shuffle, &deflate,
985                           &deflate_levelNULLNULLNULLNULLNULLNULL);
986}
987
988/* Set checksum for a var. This must be called after the nc_def_var
989 * but before the nc_enddef. */
990int
991NC4_def_var_fletcher32(int ncid, int varid, int fletcher32)
992{
993   return nc_def_var_extra(ncidvaridNULLNULLNULL, &fletcher32,
994                           NULLNULLNULLNULLNULL);
995}
996
997/* Define chunking stuff for a var. This must be done after nc_def_var
998   and before nc_enddef.
999
1000   Chunking is required in any dataset with one or more unlimited
1001   dimensions in HDF5, or any dataset using a filter.
1002
1003   Where chunksize is a pointer to an array of size ndims, with the
1004   chunksize in each dimension.
1005*/
1006int
1007NC4_def_var_chunking(int ncid, int varid, int contiguous, const size_t *chunksizesp)
1008{
1009   return nc_def_var_extra(ncidvaridNULLNULLNULLNULL,
1010                           &contiguouschunksizespNULLNULLNULL);
1011}
1012
1013/* Inquire about chunking stuff for a var. This is a private,
1014 * undocumented function, used by the f77 API to avoid size_t
1015 * problems. */
1016int
1017nc_inq_var_chunking_ints(int ncid, int varid, int *contiguousp, int *chunksizesp)
1018{
1019   NC *nc;
1020   NC_GRP_INFO_T *grp;
1021   NC_VAR_INFO_T *var;
1022   NC_HDF5_FILE_INFO_T *h5;
1023
1024   size_t *cs = NULL;
1025   int iretval;
1026
1027   /* Find this ncid's file info. */
1028   if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
1029      return retval;
1030   assert(nc);
1031
1032   /* Find var cause I need the number of dims. */
1033   if ((retval = nc4_find_g_var_nc(ncncidvarid, &grp, &var)))
1034      return retval;
1035
1036   /* Allocate space for the size_t copy of the chunksizes array. */
1037   if (var->ndims)
1038      if (!(cs = malloc(var->ndims * sizeof(size_t))))
1039  return NC_ENOMEM;
1040
1041   retval = NC4_inq_var_all(ncidvaridNULLNULLNULLNULLNULL,
1042                           NULLNULLNULLNULLcontiguouspcsNULL,
1043                           NULLNULLNULLNULL);
1044
1045   /* Copy from size_t array. */
1046   if (*contiguousp == NC_CHUNKED)
1047      for (i = 0; i < var->ndimsi++)
1048      {
1049  chunksizesp[i] = (int)cs[i];
1050  if (cs[i] > NC_MAX_INT)
1051     retval = NC_ERANGE;
1052      }
1053
1054   if (var->ndims)
1055      free(cs);
1056   return retval;
1057}
1058
1059/* This function defines the chunking with ints, which works better
1060 * with F77 portability. It is a secret function, which has been
1061 * rendered unmappable, and it is impossible to apparate anywhere in
1062 * this function. */
1063int
1064nc_def_var_chunking_ints(int ncid, int varid, int contiguous, int *chunksizesp)
1065{
1066   NC *nc;
1067   NC_GRP_INFO_T *grp;
1068   NC_VAR_INFO_T *var;
1069   NC_HDF5_FILE_INFO_T *h5;
1070   size_t *cs = NULL;
1071   int iretval;
1072
1073   /* Find this ncid's file info. */
1074   if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
1075      return retval;
1076   assert(nc);
1077
1078#ifdef USE_HDF4
1079   if(h5->hdf4)
1080 return NC_NOERR;
1081#endif
1082
1083   /* Find var cause I need the number of dims. */
1084   if ((retval = nc4_find_g_var_nc(ncncidvarid, &grp, &var)))
1085      return retval;
1086
1087   /* Allocate space for the size_t copy of the chunksizes array. */
1088   if (var->ndims)
1089      if (!(cs = malloc(var->ndims * sizeof(size_t))))
1090  return NC_ENOMEM;
1091
1092   /* Copy to size_t array. */
1093   for (i = 0; i < var->ndimsi++)
1094      cs[i] = chunksizesp[i];
1095
1096   retval = nc_def_var_extra(ncidvaridNULLNULLNULLNULL,
1097                             &contiguouscsNULLNULLNULL);
1098
1099   if (var->ndims)
1100      free(cs);
1101   return retval;
1102}
1103
1104/* Define fill value behavior for a variable. This must be done after
1105   nc_def_var and before nc_enddef. */
1106int
1107NC4_def_var_fill(int ncid, int varid, int no_fill, const void *fill_value)
1108{
1109   return nc_def_var_extra(ncidvaridNULLNULLNULLNULLNULL,
1110                           NULL, &no_fillfill_valueNULL);
1111}
1112
1113
1114/* Define the endianness of a variable. */
1115int
1116NC4_def_var_endian(int ncid, int varid, int endianness)
1117{
1118   return nc_def_var_extra(ncidvaridNULLNULLNULLNULLNULL,
1119                           NULLNULLNULL, &endianness);
1120}
1121
1122/* Get var id from name. */
1123int
1124NC4_inq_varid(int ncid, const char *name, int *varidp)
1125{
1126   NC *nc;
1127   NC_GRP_INFO_T *grp;
1128   NC_VAR_INFO_T *var;
1129   char norm_name[NC_MAX_NAME + 1];
1130   int retval;
1131   uint32_t nn_hash;
1132
1133   if (!name)
1134      return NC_EINVAL;
1135   if (!varidp)
1136      return NC_NOERR;
1137
1138   LOG((2, "%s: ncid 0x%x name %s", __func__ncidname));
1139
1140   /* Find info for this file and group, and set pointer to each. */
1141   if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grpNULL)))
1142      return retval;
1143
1144   /* Normalize name. */
1145   if ((retval = nc4_normalize_name(namenorm_name)))
1146      return retval;
1147
1148   nn_hash = hash_fast(norm_name, strlen(norm_name));
1149
1150   /* Find var of this name. */
1151   for (var = grp->varvarvar = var->l.next)
1152      if (nn_hash == var->hash && !(strcmp(var->namenorm_name)))
1153      {
1154         *varidp = var->varid;
1155         return NC_NOERR;
1156      }
1157
1158   return NC_ENOTVAR;
1159}
1160
1161/* Rename a var to "bubba," for example.
1162
1163   According to the netcdf-3.5 docs: If the new name is longer than
1164   the old name, the netCDF dataset must be in define mode.  */
1165int
1166NC4_rename_var(int ncid, int varid, const char *name)
1167{
1168   NC *nc;
1169   NC_GRP_INFO_T *grp;
1170   NC_HDF5_FILE_INFO_T *h5;
1171   NC_VAR_INFO_T *var, *tmp_var;
1172   uint32_t nn_hash;
1173   int retval = NC_NOERR;
1174
1175   LOG((2, "%s: ncid 0x%x varid %d name %s",
1176        __func__ncidvaridname));
1177
1178   /* Find info for this file and group, and set pointer to each. */
1179   if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
1180      return retval;
1181
1182   assert(h5);
1183
1184   /* Is the new name too long? */
1185   if (strlen(name) > NC_MAX_NAME)
1186      return NC_EMAXNAME;
1187
1188   /* Trying to write to a read-only file? No way, Jose! */
1189   if (h5->no_write)
1190      return NC_EPERM;
1191
1192   /* Check name validity, if strict nc3 rules are in effect for this
1193    * file. */
1194   if ((retval = NC_check_name(name)))
1195      return retval;
1196
1197   /* Check if name is in use, and retain a pointer to the correct variable */
1198   nn_hash = hash_fast(name, strlen(name));
1199   tmp_var = NULL;
1200   for (var = grp->varvarvar = var->l.next)
1201   {
1202      if (nn_hash == var->hash && !strncmp(var->namenameNC_MAX_NAME))
1203         return NC_ENAMEINUSE;
1204      if (var->varid == varid)
1205         tmp_var = var;
1206   }
1207   if (!tmp_var)
1208      return NC_ENOTVAR;
1209   var = tmp_var;
1210
1211   /* If we're not in define mode, new name must be of equal or
1212      less size, if strict nc3 rules are in effect for this . */
1213   if (!(h5->flags & NC_INDEF) && strlen(name) > strlen(var->name) &&
1214       (h5->cmode & NC_CLASSIC_MODEL))
1215      return NC_ENOTINDEFINE;
1216
1217   /* Change the HDF5 file, if this var has already been created
1218      there. */
1219   if (var->created)
1220   {
1221      if (H5Gmove(grp->hdf_grpidvar->namename) < 0)
1222         BAIL(NC_EHDFERR);
1223   }
1224
1225   /* Now change the name in our metadata. */
1226   free(var->name);
1227   if (!(var->name = malloc((strlen(name) + 1) * sizeof(char))))
1228      return NC_ENOMEM;
1229   strcpy(var->namename);
1230   var->hash = nn_hash;
1231
1232   /* Check if this was a coordinate variable previously, but names are different now */
1233   if (var->dimscale && strcmp(var->namevar->dim[0]->name))
1234   {
1235      /* Break up the coordinate variable */
1236      if ((retval = nc4_break_coord_var(grpvarvar->dim[0])))
1237         return retval;
1238   }
1239
1240   /* Check if this should become a coordinate variable */
1241   if (!var->dimscale)
1242   {
1243      /* Only variables with >0 dimensions can become coordinate variables */
1244      if (var->ndims)
1245      {
1246         NC_GRP_INFO_T *dim_grp;
1247         NC_DIM_INFO_T *dim;
1248
1249          /* Check to see if this is became a coordinate variable.  If so, it
1250           * will have the same name as dimension index 0. If it is a
1251           * coordinate var, is it a coordinate var in the same group as the dim?
1252           */
1253         if ((retval = nc4_find_dim(grpvar->dimids[0], &dim, &dim_grp)))
1254            return retval;
1255         if (strcmp(dim->namename) == 0 && dim_grp == grp)
1256         {
1257             /* Reform the coordinate variable */
1258             if ((retval = nc4_reform_coord_var(grpvardim)))
1259                return retval;
1260         }
1261      }
1262   }
1263
1264  exit:
1265   return retval;
1266}
1267
1268
1269int
1270NC4_var_par_access(int ncid, int varid, int par_access)
1271{
1272#ifndef USE_PARALLEL4
1273   return NC_ENOPAR;
1274#else
1275   NC *nc;
1276   NC_GRP_INFO_T *grp;
1277   NC_HDF5_FILE_INFO_T *h5;
1278   NC_VAR_INFO_T *var;
1279   int retval;
1280
1281   LOG((1, "%s: ncid 0x%x varid %d par_access %d", __func__ncid,
1282        varidpar_access));
1283
1284   if (par_access != NC_INDEPENDENT && par_access != NC_COLLECTIVE)
1285      return NC_EINVAL;
1286
1287   /* Find info for this file and group, and set pointer to each. */
1288   if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
1289      return retval;
1290
1291   /* This function only for files opened with nc_open_par or nc_create_par. */
1292   if (!h5->parallel)
1293      return NC_ENOPAR;
1294
1295   /* Find the var, and set its preference. */
1296   for (var = grp->varvarvar = var->l.next)
1297      if (var->varid == varid)
1298         break;
1299   if (!var)
1300      return NC_ENOTVAR;
1301
1302   if (par_access)
1303      var->parallel_access = NC_COLLECTIVE;
1304   else
1305      var->parallel_access = NC_INDEPENDENT;
1306   return NC_NOERR;
1307#endif /* USE_PARALLEL4 */
1308}
1309
1310static int
1311nc4_put_vara_tc(int ncid, int varidnc_type mem_type, int mem_type_is_long,
1312                const size_t *startp, const size_t *countp, const void *op)
1313{
1314   NC *nc;
1315
1316   LOG((2, "%s: ncid 0x%x varid %d mem_type %d mem_type_is_long %d",
1317        __func__ncidvaridmem_typemem_type_is_long));
1318
1319   if (!(nc = nc4_find_nc_file(ncid,NULL)))
1320      return NC_EBADID;
1321
1322   return nc4_put_vara(ncncidvaridstartpcountpmem_type,
1323                       mem_type_is_long, (void *)op);
1324}
1325
1326#ifdef USE_HDF4
1327static int
1328nc4_get_hdf4_vara(NC *nc, int ncid, int varid, const size_t *startp,
1329   const size_t *countpnc_type mem_nc_type, int is_long, void *data)
1330{
1331   NC_GRP_INFO_T *grp;
1332   NC_HDF5_FILE_INFO_T *h5;
1333   NC_VAR_INFO_T *var;
1334   int32 start32[NC_MAX_VAR_DIMS], edge32[NC_MAX_VAR_DIMS];
1335   int retvald;
1336
1337   /* Find our metadata for this file, group, and var. */
1338   assert(nc);
1339   if ((retval = nc4_find_g_var_nc(ncncidvarid, &grp, &var)))
1340      return retval;
1341   h5 = NC4_DATA(nc);
1342   assert(grp && h5 && var && var->name);
1343
1344   for (d = 0; d < var->ndimsd++)
1345   {
1346      start32[d] = startp[d];
1347      edge32[d] = countp[d];
1348   }
1349
1350   if (SDreaddata(var->sdsidstart32NULLedge32data))
1351      return NC_EHDFERR;
1352
1353   return NC_NOERR;
1354}
1355#endif /* USE_HDF4 */
1356
1357/* Get an array. */
1358static int
1359nc4_get_vara_tc(int ncid, int varidnc_type mem_type, int mem_type_is_long,
1360                const size_t *startp, const size_t *countp, void *ip)
1361{
1362   NC *nc;
1363   NC_HDF5_FILE_INFO_Th5;
1364
1365   LOG((2, "%s: ncid 0x%x varid %d mem_type %d mem_type_is_long %d",
1366        __func__ncidvaridmem_typemem_type_is_long));
1367
1368   if (!(nc = nc4_find_nc_file(ncid,&h5)))
1369      return NC_EBADID;
1370
1371#ifdef USE_HDF4
1372   /* Handle HDF4 cases. */
1373   if (h5->hdf4)
1374      return nc4_get_hdf4_vara(ncncidvaridstartpcountpmem_type,
1375        mem_type_is_long, (void *)ip);
1376#endif /* USE_HDF4 */
1377
1378   /* Handle HDF5 cases. */
1379   return nc4_get_vara(ncncidvaridstartpcountpmem_type,
1380                       mem_type_is_long, (void *)ip);
1381}
1382
1383int
1384NC4_put_vara(int ncid, int varid, const size_t *startp,
1385            const size_t *countp, const void *op, int memtype)
1386{
1387   return nc4_put_vara_tc(ncidvaridmemtype, 0, startpcountpop);
1388}
1389
1390
1391/* Read an array of values. */
1392int
1393NC4_get_vara(int ncid, int varid, const size_t *startp,
1394            const size_t *countp, void *ip, int memtype)
1395{
1396   return nc4_get_vara_tc(ncidvaridmemtype, 0, startpcountpip);
1397}


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed