NetCDF  4.9.3
nc4hdf.c
Go to the documentation of this file.
1 /* Copyright 2018, University Corporation for Atmospheric
2  * Research. See the COPYRIGHT file for copying and redistribution
3  * conditions. */
17 #include "config.h"
18 #include "netcdf.h"
19 #include "nc4internal.h"
20 #include "ncdispatch.h"
21 #include "hdf5internal.h"
22 #include "hdf5err.h" /* For BAIL2 */
23 #include "hdf5debug.h"
24 #include <math.h>
25 #include <stddef.h>
26 
27 #ifdef HAVE_INTTYPES_H
28 #define __STDC_FORMAT_MACROS
29 #include <inttypes.h>
30 #endif
31 
32 #define NC_HDF5_MAX_NAME 1024
42 static int
43 flag_atts_dirty(NCindex *attlist) {
44 
45  NC_ATT_INFO_T *att = NULL;
46 
47  if(attlist == NULL) {
48  return NC_NOERR;
49  }
50 
51  for(size_t i=0;i<ncindexsize(attlist);i++) {
52  att = (NC_ATT_INFO_T*)ncindexith(attlist,i);
53  if(att == NULL) continue;
54  att->dirty = NC_TRUE;
55  }
56 
57  return NC_NOERR;
58 }
59 
76 int
77 rec_reattach_scales(NC_GRP_INFO_T *grp, int dimid, hid_t dimscaleid)
78 {
79  NC_VAR_INFO_T *var;
80  NC_GRP_INFO_T *child_grp;
81  size_t i;
82  int retval;
83 
84  assert(grp && grp->hdr.name && dimid >= 0 && dimscaleid >= 0);
85  LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
86 
87  /* If there are any child groups, attach dimscale there, if needed. */
88  for (i = 0; i < ncindexsize(grp->children); i++)
89  {
90  child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children, i);
91  assert(child_grp);
92  if ((retval = rec_reattach_scales(child_grp, dimid, dimscaleid)))
93  return retval;
94  }
95 
96  /* Find any vars that use this dimension id. */
97  for (i = 0; i < ncindexsize(grp->vars); i++)
98  {
99  NC_HDF5_VAR_INFO_T *hdf5_var;
100 
101  var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
102  assert(var && var->format_var_info);
103 
104  hdf5_var = (NC_HDF5_VAR_INFO_T*)var->format_var_info;
105  assert(hdf5_var != NULL);
106  for (unsigned int d = 0; d < var->ndims; d++)
107  {
108  if (var->dimids[d] == dimid && !hdf5_var->dimscale)
109  {
110  LOG((2, "%s: attaching scale for dimid %d to var %s",
111  __func__, var->dimids[d], var->hdr.name));
112  if (var->created)
113  {
114  if (H5DSattach_scale(hdf5_var->hdf_datasetid,
115  dimscaleid, d) < 0)
116  return NC_EDIMSCALE;
117  hdf5_var->dimscale_attached[d] = NC_TRUE;
118  }
119  }
120  }
121  }
122  return NC_NOERR;
123 }
124 
141 int
142 rec_detach_scales(NC_GRP_INFO_T *grp, int dimid, hid_t dimscaleid)
143 {
144  NC_VAR_INFO_T *var;
145  NC_GRP_INFO_T *child_grp;
146  size_t i;
147  int retval;
148 
149  assert(grp && grp->hdr.name && dimid >= 0 && dimscaleid >= 0);
150  LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
151 
152  /* If there are any child groups, detach dimscale there, if needed. */
153  for(i=0;i<ncindexsize(grp->children);i++) {
154  child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children,i);
155  if(child_grp == NULL) continue;
156  if ((retval = rec_detach_scales(child_grp, dimid, dimscaleid)))
157  return retval;
158  }
159 
160  /* Find any vars that use this dimension id. */
161  for (i = 0; i < ncindexsize(grp->vars); i++)
162  {
163  NC_HDF5_VAR_INFO_T *hdf5_var;
164  var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i);
165  assert(var && var->format_var_info);
166  hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
167 
168  for (unsigned int d = 0; d < var->ndims; d++)
169  {
170  if (var->dimids[d] == dimid && !hdf5_var->dimscale)
171  {
172  LOG((2, "%s: detaching scale for dimid %d to var %s",
173  __func__, var->dimids[d], var->hdr.name));
174  if (var->created)
175  {
176  if (hdf5_var->dimscale_attached && hdf5_var->dimscale_attached[d])
177  {
178  if (H5DSdetach_scale(hdf5_var->hdf_datasetid,
179  dimscaleid, d) < 0)
180  return NC_EDIMSCALE;
181  hdf5_var->dimscale_attached[d] = NC_FALSE;
182  }
183  }
184  }
185  }
186  }
187  return NC_NOERR;
188 }
189 
201 int
202 nc4_open_var_grp2(NC_GRP_INFO_T *grp, int varid, hid_t *dataset)
203 {
204  NC_VAR_INFO_T *var;
205  NC_HDF5_VAR_INFO_T *hdf5_var;
206 
207  assert(grp && grp->format_grp_info && dataset);
208 
209  /* Find the requested varid. */
210  if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, (size_t)varid)))
211  return NC_ENOTVAR;
212  assert(var && var->hdr.id == varid && var->format_var_info);
213  hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
214 
215  /* Open this dataset if necessary. */
216  if (!hdf5_var->hdf_datasetid)
217  {
218  NC_HDF5_GRP_INFO_T *hdf5_grp;
219  hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
220 
221  if ((hdf5_var->hdf_datasetid = H5Dopen2(hdf5_grp->hdf_grpid,
222  var->hdr.name, H5P_DEFAULT)) < 0)
223  return NC_ENOTVAR;
224  }
225 
226  *dataset = hdf5_var->hdf_datasetid;
227 
228  return NC_NOERR;
229 }
230 
248 int
249 nc4_get_hdf_typeid(NC_FILE_INFO_T *h5, nc_type xtype,
250  hid_t *hdf_typeid, int endianness)
251 {
252  NC_TYPE_INFO_T *type;
253  hid_t typeid = 0;
254  int retval = NC_NOERR;
255 
256  assert(hdf_typeid && h5);
257 
258  *hdf_typeid = -1;
259 
260  /* Determine an appropriate HDF5 datatype */
261  if (xtype == NC_NAT)
262  return NC_EBADTYPE;
263  else if (xtype == NC_CHAR || xtype == NC_STRING)
264  {
265  /* NC_CHAR & NC_STRING types create a new HDF5 datatype */
266  if (xtype == NC_CHAR)
267  {
268  if ((typeid = H5Tcopy(H5T_C_S1)) < 0)
269  return NC_EHDFERR;
270  if (H5Tset_strpad(typeid, H5T_STR_NULLTERM) < 0)
271  BAIL(NC_EVARMETA);
272  if(H5Tset_cset(typeid, H5T_CSET_ASCII) < 0)
273  BAIL(NC_EVARMETA);
274 
275  /* Take ownership of the newly created HDF5 datatype */
276  *hdf_typeid = typeid;
277  typeid = 0;
278  }
279  else
280  {
281  if ((typeid = H5Tcopy(H5T_C_S1)) < 0)
282  return NC_EHDFERR;
283  if (H5Tset_size(typeid, H5T_VARIABLE) < 0)
284  BAIL(NC_EVARMETA);
285  if(H5Tset_cset(typeid, H5T_CSET_UTF8) < 0)
286  BAIL(NC_EVARMETA);
287 
288  /* Take ownership of the newly created HDF5 datatype */
289  *hdf_typeid = typeid;
290  typeid = 0;
291  }
292  }
293  else
294  {
295  /* All other types use an existing HDF5 datatype */
296  switch (xtype)
297  {
298  case NC_BYTE: /* signed 1 byte integer */
299  if (endianness == NC_ENDIAN_LITTLE)
300  typeid = H5T_STD_I8LE;
301  else if (endianness == NC_ENDIAN_BIG)
302  typeid = H5T_STD_I8BE;
303  else
304  typeid = H5T_NATIVE_SCHAR;
305  break;
306 
307  case NC_SHORT: /* signed 2 byte integer */
308  if (endianness == NC_ENDIAN_LITTLE)
309  typeid = H5T_STD_I16LE;
310  else if (endianness == NC_ENDIAN_BIG)
311  typeid = H5T_STD_I16BE;
312  else
313  typeid = H5T_NATIVE_SHORT;
314  break;
315 
316  case NC_INT:
317  if (endianness == NC_ENDIAN_LITTLE)
318  typeid = H5T_STD_I32LE;
319  else if (endianness == NC_ENDIAN_BIG)
320  typeid = H5T_STD_I32BE;
321  else
322  typeid = H5T_NATIVE_INT;
323  break;
324 
325  case NC_UBYTE:
326  if (endianness == NC_ENDIAN_LITTLE)
327  typeid = H5T_STD_U8LE;
328  else if (endianness == NC_ENDIAN_BIG)
329  typeid = H5T_STD_U8BE;
330  else
331  typeid = H5T_NATIVE_UCHAR;
332  break;
333 
334  case NC_USHORT:
335  if (endianness == NC_ENDIAN_LITTLE)
336  typeid = H5T_STD_U16LE;
337  else if (endianness == NC_ENDIAN_BIG)
338  typeid = H5T_STD_U16BE;
339  else
340  typeid = H5T_NATIVE_USHORT;
341  break;
342 
343  case NC_UINT:
344  if (endianness == NC_ENDIAN_LITTLE)
345  typeid = H5T_STD_U32LE;
346  else if (endianness == NC_ENDIAN_BIG)
347  typeid = H5T_STD_U32BE;
348  else
349  typeid = H5T_NATIVE_UINT;
350  break;
351 
352  case NC_INT64:
353  if (endianness == NC_ENDIAN_LITTLE)
354  typeid = H5T_STD_I64LE;
355  else if (endianness == NC_ENDIAN_BIG)
356  typeid = H5T_STD_I64BE;
357  else
358  typeid = H5T_NATIVE_LLONG;
359  break;
360 
361  case NC_UINT64:
362  if (endianness == NC_ENDIAN_LITTLE)
363  typeid = H5T_STD_U64LE;
364  else if (endianness == NC_ENDIAN_BIG)
365  typeid = H5T_STD_U64BE;
366  else
367  typeid = H5T_NATIVE_ULLONG;
368  break;
369 
370  case NC_FLOAT:
371  if (endianness == NC_ENDIAN_LITTLE)
372  typeid = H5T_IEEE_F32LE;
373  else if (endianness == NC_ENDIAN_BIG)
374  typeid = H5T_IEEE_F32BE;
375  else
376  typeid = H5T_NATIVE_FLOAT;
377  break;
378 
379  case NC_DOUBLE:
380  if (endianness == NC_ENDIAN_LITTLE)
381  typeid = H5T_IEEE_F64LE;
382  else if (endianness == NC_ENDIAN_BIG)
383  typeid = H5T_IEEE_F64BE;
384  else
385  typeid = H5T_NATIVE_DOUBLE;
386  break;
387 
388  default:
389  /* Maybe this is a user defined type? */
390  if (nc4_find_type(h5, xtype, &type))
391  return NC_EBADTYPE;
392  if (!type)
393  return NC_EBADTYPE;
394  typeid = ((NC_HDF5_TYPE_INFO_T *)type->format_type_info)->hdf_typeid;
395  break;
396  }
397  assert(typeid);
398 
399  /* Copy the HDF5 datatype, so the function operates uniformly */
400  if ((*hdf_typeid = H5Tcopy(typeid)) < 0)
401  return NC_EHDFERR;
402  typeid = 0;
403  }
404  assert(*hdf_typeid != -1);
405 
406 exit:
407  if (typeid > 0 && H5Tclose(typeid) < 0)
408  BAIL2(NC_EHDFERR);
409  return retval;
410 }
411 
426 static int
427 put_att_grpa(NC_GRP_INFO_T *grp, int varid, NC_ATT_INFO_T *att)
428 {
429  NC_HDF5_GRP_INFO_T *hdf5_grp;
430  hid_t datasetid = 0, locid;
431  hid_t attid = 0, spaceid = 0, file_typeid = 0;
432  hid_t existing_att_typeid = 0, existing_attid = 0, existing_spaceid = 0;
433  hsize_t dims[1]; /* netcdf attributes always 1-D. */
434  htri_t attr_exists;
435  void *data;
436  int phoney_data = 99;
437  int retval = NC_NOERR;
438 
439  assert(att->hdr.name && grp && grp->format_grp_info);
440  LOG((3, "%s: varid %d att->hdr.id %d att->hdr.name %s att->nc_typeid %d "
441  "att->len %d", __func__, varid, att->hdr.id, att->hdr.name,
442  att->nc_typeid, att->len));
443 
444  /* Get HDF5-specific group info. */
445  hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
446 
447  /* If the file is read-only, return an error. */
448  if (grp->nc4_info->no_write)
449  BAIL(NC_EPERM);
450 
451  /* Get the hid to attach the attribute to, or read it from. */
452  if (varid == NC_GLOBAL)
453  locid = hdf5_grp->hdf_grpid;
454  else
455  {
456  if ((retval = nc4_open_var_grp2(grp, varid, &datasetid)))
457  BAIL(retval);
458  locid = datasetid;
459  }
460 
461  /* Get the length ready, and find the HDF type we'll be
462  * writing. */
463  dims[0] = (hsize_t)att->len;
464  if ((retval = nc4_get_hdf_typeid(grp->nc4_info, att->nc_typeid,
465  &file_typeid, 0)))
466  BAIL(retval);
467 
468  /* Even if the length is zero, HDF5 won't let me write with a
469  * NULL pointer. So if the length of the att is zero, point to
470  * some phoney data (which won't be written anyway.)*/
471  if (!dims[0])
472  data = &phoney_data;
473  else
474  data = att->data;
475 
476  /* NC_CHAR types require some extra work. The space ID is set to
477  * scalar, and the type is told how long the string is. If it's
478  * really zero length, set the size to 1. (The fact that it's
479  * really zero will be marked by the NULL dataspace, but HDF5
480  * doesn't allow me to set the size of the type to zero.)*/
481  if (att->nc_typeid == NC_CHAR)
482  {
483  size_t string_size = dims[0];
484  if (!string_size)
485  {
486  string_size = 1;
487  if ((spaceid = H5Screate(H5S_NULL)) < 0)
488  BAIL(NC_EATTMETA);
489  }
490  else
491  {
492  if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
493  BAIL(NC_EATTMETA);
494  }
495  if (H5Tset_size(file_typeid, string_size) < 0)
496  BAIL(NC_EATTMETA);
497  if (H5Tset_strpad(file_typeid, H5T_STR_NULLTERM) < 0)
498  BAIL(NC_EATTMETA);
499  }
500  else
501  {
502  if (!att->len)
503  {
504  if ((spaceid = H5Screate(H5S_NULL)) < 0)
505  BAIL(NC_EATTMETA);
506  }
507  else
508  {
509  if ((spaceid = H5Screate_simple(1, dims, NULL)) < 0)
510  BAIL(NC_EATTMETA);
511  }
512  }
513 
514  /* Does the att exists already? */
515  if ((attr_exists = H5Aexists(locid, att->hdr.name)) < 0)
516  BAIL(NC_EHDFERR);
517  if (attr_exists)
518  {
519  hssize_t npoints;
520 
521  /* Open the attribute. */
522  if ((existing_attid = H5Aopen(locid, att->hdr.name, H5P_DEFAULT)) < 0)
523  BAIL(NC_EATTMETA);
524 
525  /* Find the type of the existing attribute. */
526  if ((existing_att_typeid = H5Aget_type(existing_attid)) < 0)
527  BAIL(NC_EATTMETA);
528 
529  /* How big is the attribute? */
530  if ((existing_spaceid = H5Aget_space(existing_attid)) < 0)
531  BAIL(NC_EATTMETA);
532  if ((npoints = H5Sget_simple_extent_npoints(existing_spaceid)) < 0)
533  BAIL(NC_EATTMETA);
534 
535  /* For text attributes the size is specified in the datatype
536  and it is enough to compare types using H5Tequal(). */
537  if (!H5Tequal(file_typeid, existing_att_typeid) ||
538  (att->nc_typeid != NC_CHAR && npoints != att->len))
539  {
540  /* The attribute exists but we cannot re-use it. */
541 
542  /* Delete the attribute. */
543  if (H5Adelete(locid, att->hdr.name) < 0)
544  BAIL(NC_EHDFERR);
545 
546  /* Re-create the attribute with the type and length
547  reflecting the new value (or values). */
548  if ((attid = H5Acreate1(locid, att->hdr.name, file_typeid, spaceid,
549  H5P_DEFAULT)) < 0)
550  BAIL(NC_EATTMETA);
551 
552  /* Write the values, (even if length is zero). */
553  if (H5Awrite(attid, file_typeid, data) < 0)
554  BAIL(NC_EATTMETA);
555  }
556  else
557  {
558  /* The attribute exists and we can re-use it. */
559 
560  /* Write the values, re-using the existing attribute. */
561  if (H5Awrite(existing_attid, file_typeid, data) < 0)
562  BAIL(NC_EATTMETA);
563  }
564  }
565  else
566  {
567  /* The attribute does not exist yet. */
568 
569  /* Create the attribute. */
570  if ((attid = H5Acreate1(locid, att->hdr.name, file_typeid, spaceid,
571  H5P_DEFAULT)) < 0)
572  BAIL(NC_EATTMETA);
573 
574  /* Write the values, (even if length is zero). */
575  if (H5Awrite(attid, file_typeid, data) < 0)
576  BAIL(NC_EATTMETA);
577  }
578 
579 exit:
580  if (file_typeid && H5Tclose(file_typeid))
581  BAIL2(NC_EHDFERR);
582  if (attid > 0 && H5Aclose(attid) < 0)
583  BAIL2(NC_EHDFERR);
584  if (existing_att_typeid && H5Tclose(existing_att_typeid))
585  BAIL2(NC_EHDFERR);
586  if (existing_attid > 0 && H5Aclose(existing_attid) < 0)
587  BAIL2(NC_EHDFERR);
588  if (spaceid > 0 && H5Sclose(spaceid) < 0)
589  BAIL2(NC_EHDFERR);
590  if (existing_spaceid > 0 && H5Sclose(existing_spaceid) < 0)
591  BAIL2(NC_EHDFERR);
592  return retval;
593 }
594 
606 static int
607 write_attlist(NCindex *attlist, int varid, NC_GRP_INFO_T *grp)
608 {
609  NC_ATT_INFO_T *att;
610  int retval;
611 
612  for(size_t i = 0; i < ncindexsize(attlist); i++)
613  {
614  att = (NC_ATT_INFO_T *)ncindexith(attlist, i);
615  assert(att);
616  if (att->dirty)
617  {
618  LOG((4, "%s: writing att %s to varid %d", __func__, att->hdr.name, varid));
619  if ((retval = put_att_grpa(grp, varid, att)))
620  return retval;
621  att->dirty = NC_FALSE;
622  att->created = NC_TRUE;
623  }
624  }
625  return NC_NOERR;
626 }
627 
641 static int
642 write_coord_dimids(NC_VAR_INFO_T *var)
643 {
644  NC_HDF5_VAR_INFO_T *hdf5_var;
645  hsize_t coords_len[1];
646  hid_t c_spaceid = -1, c_attid = -1;
647  int retval = NC_NOERR;
648 
649  assert(var && var->format_var_info);
650 
651  /* Get HDF5-specific var info. */
652  hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
653 
654  /* Set up space for attribute. */
655  coords_len[0] = var->ndims;
656  if ((c_spaceid = H5Screate_simple(1, coords_len, coords_len)) < 0)
657  BAIL(NC_EHDFERR);
658 
659  /* Create the attribute. */
660  if ((c_attid = H5Acreate1(hdf5_var->hdf_datasetid, COORDINATES,
661  H5T_NATIVE_INT, c_spaceid, H5P_DEFAULT)) < 0)
662  BAIL(NC_EHDFERR);
663 
664  /* Write our attribute. */
665  if (H5Awrite(c_attid, H5T_NATIVE_INT, var->dimids) < 0)
666  BAIL(NC_EHDFERR);
667 
668 exit:
669  if (c_spaceid >= 0 && H5Sclose(c_spaceid) < 0)
670  BAIL2(NC_EHDFERR);
671  if (c_attid >= 0 && H5Aclose(c_attid) < 0)
672  BAIL2(NC_EHDFERR);
673  return retval;
674 }
675 
686 static int
687 write_quantize_att(NC_VAR_INFO_T *var)
688 {
689  NC_HDF5_VAR_INFO_T *hdf5_var;
690  hsize_t len = 1;
691  hid_t c_spaceid = -1, c_attid = -1;
692  char att_name[NC_MAX_NAME + 1];
693  int retval = NC_NOERR;
694 
695  assert(var && var->format_var_info);
696 
697  /* Get HDF5-specific var info. */
698  hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
699 
700  /* Different quantize algorithms get different attribute names. */
701  switch (var->quantize_mode)
702  {
704  snprintf(att_name, sizeof(att_name), "%s", NC_QUANTIZE_BITGROOM_ATT_NAME);
705  break;
707  snprintf(att_name, sizeof(att_name), "%s", NC_QUANTIZE_GRANULARBR_ATT_NAME);
708  break;
710  snprintf(att_name, sizeof(att_name), "%s", NC_QUANTIZE_BITROUND_ATT_NAME);
711  break;
712  default:
713  return NC_EINVAL;
714  }
715 
716  /* Set up space for attribute. */
717  if ((c_spaceid = H5Screate_simple(1, &len, &len)) < 0)
718  BAIL(NC_EHDFERR);
719 
720  /* Create the attribute. */
721  if ((c_attid = H5Acreate1(hdf5_var->hdf_datasetid, att_name,
722  H5T_NATIVE_INT, c_spaceid, H5P_DEFAULT)) < 0)
723  BAIL(NC_EHDFERR);
724 
725  /* Write our attribute. */
726  if (H5Awrite(c_attid, H5T_NATIVE_INT, &var->nsd) < 0)
727  BAIL(NC_EHDFERR);
728 
729 exit:
730  if (c_spaceid >= 0 && H5Sclose(c_spaceid) < 0)
731  BAIL2(NC_EHDFERR);
732  if (c_attid >= 0 && H5Aclose(c_attid) < 0)
733  BAIL2(NC_EHDFERR);
734  return retval;
735 }
736 
747 static int
748 write_netcdf4_dimid(hid_t datasetid, int dimid)
749 {
750  hid_t dimid_spaceid = -1, dimid_attid = -1;
751  htri_t attr_exists;
752  int retval = NC_NOERR;
753 
754  /* Create the space. */
755  if ((dimid_spaceid = H5Screate(H5S_SCALAR)) < 0)
756  BAIL(NC_EHDFERR);
757 
758  /* Does the attribute already exist? If so, don't try to create it. */
759  if ((attr_exists = H5Aexists(datasetid, NC_DIMID_ATT_NAME)) < 0)
760  BAIL(NC_EHDFERR);
761  if (attr_exists)
762  dimid_attid = H5Aopen_by_name(datasetid, ".", NC_DIMID_ATT_NAME,
763  H5P_DEFAULT, H5P_DEFAULT);
764  else
765  /* Create the attribute if needed. */
766  dimid_attid = H5Acreate1(datasetid, NC_DIMID_ATT_NAME,
767  H5T_NATIVE_INT, dimid_spaceid, H5P_DEFAULT);
768  if (dimid_attid < 0)
769  BAIL(NC_EHDFERR);
770 
771 
772  /* Write it. */
773  LOG((4, "%s: writing secret dimid %d", __func__, dimid));
774  if (H5Awrite(dimid_attid, H5T_NATIVE_INT, &dimid) < 0)
775  BAIL(NC_EHDFERR);
776 
777 exit:
778  /* Close stuff*/
779  if (dimid_spaceid >= 0 && H5Sclose(dimid_spaceid) < 0)
780  BAIL2(NC_EHDFERR);
781  if (dimid_attid >= 0 && H5Aclose(dimid_attid) < 0)
782  BAIL2(NC_EHDFERR);
783 
784  return retval;
785 }
786 
801 static int
802 var_create_dataset(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, nc_bool_t write_dimid)
803 {
804  NC_HDF5_GRP_INFO_T *hdf5_grp;
805  NC_HDF5_VAR_INFO_T *hdf5_var;
806  hid_t plistid = 0, access_plistid = 0, typeid = 0, spaceid = 0;
807  hsize_t chunksize[H5S_MAX_RANK], dimsize[H5S_MAX_RANK], maxdimsize[H5S_MAX_RANK];
808  int d;
809  void *fillp = NULL;
810  NC_DIM_INFO_T *dim = NULL;
811  char *name_to_use;
812  int retval;
813  unsigned int* params = NULL;
814 
815  assert(grp && grp->format_grp_info && var && var->format_var_info);
816 
817  LOG((3, "%s:: name %s", __func__, var->hdr.name));
818 
819  /* Get HDF5-specific group and var info. */
820  hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
821  hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
822 
823  /* Scalar or not, we need a creation property list. */
824  if ((plistid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
825  BAIL(NC_EHDFERR);
826  if ((access_plistid = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
827  BAIL(NC_EHDFERR);
828 
829  /* Turn off object tracking times in HDF5. */
830  if (H5Pset_obj_track_times(plistid, 0) < 0)
831  BAIL(NC_EHDFERR);
832 
833  /* Find the HDF5 type of the dataset. */
834  if ((retval = nc4_get_hdf_typeid(grp->nc4_info, var->type_info->hdr.id, &typeid,
835  var->type_info->endianness)))
836  BAIL(retval);
837 
838  /* Figure out what fill value to set, if any. */
839  if (var->no_fill)
840  {
841  /* Required to truly turn HDF5 fill values off */
842  if (H5Pset_fill_time(plistid, H5D_FILL_TIME_NEVER) < 0)
843  BAIL(NC_EHDFERR);
844  }
845  else
846  {
847  if ((retval = nc4_get_fill_value(grp->nc4_info, var, &fillp)))
848  BAIL(retval);
849 
850  /* If there is a fill value, set it. */
851  if (fillp)
852  {
853  if (var->type_info->nc_type_class == NC_STRING)
854  {
855  if (H5Pset_fill_value(plistid, typeid, fillp) < 0)
856  BAIL(NC_EHDFERR);
857  }
858  else
859  {
860  /* The fill value set in HDF5 must always be presented as
861  * a native type, even if the endianness for this dataset
862  * is non-native. HDF5 will translate the fill value to
863  * the target endiannesss. */
864  hid_t fill_typeid = 0;
865 
866  if ((retval = nc4_get_hdf_typeid(grp->nc4_info, var->type_info->hdr.id, &fill_typeid,
868  BAIL(retval);
869  if (H5Pset_fill_value(plistid, fill_typeid, fillp) < 0)
870  {
871  if (H5Tclose(fill_typeid) < 0)
872  BAIL(NC_EHDFERR);
873  BAIL(NC_EHDFERR);
874  }
875  if (H5Tclose(fill_typeid) < 0)
876  BAIL(NC_EHDFERR);
877  }
878  }
879  }
880 
881  /* If the user wants to compress the data, using either zlib
882  * (a.k.a deflate) or szip, or another filter, set that up now.
883  * Szip and zip can be turned on
884  * either directly with nc_def_var_szip/deflate(), or using
885  * nc_def_var_filter(). If the user
886  * has specified a filter, it will be applied here. */
887  if(var->filters != NULL) {
888  size_t j;
889  NClist* filters = (NClist*)var->filters;
890  for(j=0;j<nclistlength(filters);j++) {
891  struct NC_HDF5_Filter* fi = (struct NC_HDF5_Filter*)nclistget(filters,j);
892  if(fi->filterid == H5Z_FILTER_FLETCHER32) {
893  if(H5Pset_fletcher32(plistid) < 0)
894  BAIL(NC_EHDFERR);
895  } else if(fi->filterid == H5Z_FILTER_SHUFFLE) {
896  if(H5Pset_shuffle(plistid) < 0)
897  BAIL(NC_EHDFERR);
898  } else if(fi->filterid == H5Z_FILTER_DEFLATE) {/* Handle zip case here */
899  if(fi->nparams != 1)
900  BAIL(NC_EFILTER);
901  unsigned int level = fi->params[0];
902  if(H5Pset_deflate(plistid, level) < 0)
903  BAIL(NC_EFILTER);
904  } else if(fi->filterid == H5Z_FILTER_SZIP) {/* Handle szip case here */
905  if(fi->nparams != 2)
906  BAIL(NC_EFILTER);
907  unsigned int options_mask = fi->params[0];
908  unsigned int bits_per_pixel = fi->params[1];
909  if(H5Pset_szip(plistid, options_mask, bits_per_pixel) < 0)
910  BAIL(NC_EFILTER);
911  } else {
912  herr_t code = H5Pset_filter(plistid, fi->filterid,
913  H5Z_FLAG_OPTIONAL, /* always make optional so filters on vlens are ignored */
914  fi->nparams, fi->params);
915  if(code < 0)
916  BAIL(NC_EFILTER);
917  }
918  }
919  }
920 
921  /* If ndims non-zero, get info for all dimensions. We look up the
922  dimids and get the len of each dimension. We need this to create
923  the space for the dataset. In netCDF a dimension length of zero
924  means an unlimited dimension. */
925  if (var->ndims)
926  {
927  int unlimdim = 0;
928 
929  /* Check to see if any unlimited dimensions are used in this var. */
930  for (d = 0; d < var->ndims; d++) {
931  dim = var->dim[d];
932  assert(dim && dim->hdr.id == var->dimids[d]);
933  if (dim->unlimited)
934  unlimdim++;
935  }
936 
937  /* If there are no unlimited dims, and no filters, and the user
938  * has not specified chunksizes, use contiguous variable for
939  * better performance. */
940  if (nclistlength((NClist*)var->filters) == 0 &&
941  (var->chunksizes == NULL || !var->chunksizes[0]) && !unlimdim)
942  var->storage = NC_CONTIGUOUS;
943 
944  /* Gather current & maximum dimension sizes, along with chunk
945  * sizes. */
946  for (d = 0; d < var->ndims; d++)
947  {
948  dim = var->dim[d];
949  assert(dim && dim->hdr.id == var->dimids[d]);
950  dimsize[d] = dim->unlimited ? NC_HDF5_UNLIMITED_DIMSIZE : dim->len;
951  maxdimsize[d] = dim->unlimited ? H5S_UNLIMITED : (hsize_t)dim->len;
952  if (var->storage == NC_CHUNKED)
953  {
954  if (var->chunksizes[d])
955  chunksize[d] = var->chunksizes[d];
956  else
957  {
958  size_t type_size;
959  if (var->type_info->nc_type_class == NC_STRING)
960  type_size = sizeof(char *);
961  else
962  type_size = var->type_info->size;
963 
964  /* Unlimited dim always gets chunksize of 1. */
965  if (dim->unlimited)
966  chunksize[d] = 1;
967  else
968  chunksize[d] = (hsize_t)pow(DEFAULT_CHUNK_SIZE/(double)type_size,
969  1/(double)((int)var->ndims - unlimdim));
970 
971  /* If the chunksize is greater than the dim
972  * length, make it the dim length. */
973  if (!dim->unlimited && chunksize[d] > dim->len)
974  chunksize[d] = dim->len;
975 
976  /* Remember the computed chunksize */
977  var->chunksizes[d] = chunksize[d];
978  }
979  }
980  }
981 
982  /* Create the dataspace. */
983  if ((spaceid = H5Screate_simple((int)var->ndims, dimsize, maxdimsize)) < 0)
984  BAIL(NC_EHDFERR);
985  }
986  else
987  {
988  if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
989  BAIL(NC_EHDFERR);
990  }
991 
992  /* Set the var storage to contiguous, compact, or chunked. Don't
993  * try to set chunking for scalar vars, they will default to
994  * contiguous if not set to compact. */
995  if (var->storage == NC_CONTIGUOUS)
996  {
997  if (H5Pset_layout(plistid, H5D_CONTIGUOUS) < 0)
998  BAIL(NC_EHDFERR);
999  }
1000  else if (var->storage == NC_COMPACT)
1001  {
1002  if (H5Pset_layout(plistid, H5D_COMPACT) < 0)
1003  BAIL(NC_EHDFERR);
1004  }
1005  else if (var->ndims)
1006  {
1007  if (H5Pset_chunk(plistid, (int)var->ndims, chunksize) < 0)
1008  BAIL(NC_EHDFERR);
1009  }
1010 
1011  /* Turn on creation order tracking. */
1012  if (!grp->nc4_info->no_attr_create_order) {
1013  if (H5Pset_attr_creation_order(plistid, H5P_CRT_ORDER_TRACKED|
1014  H5P_CRT_ORDER_INDEXED) < 0)
1015  BAIL(NC_EHDFERR);
1016  }
1017 
1018  /* Set per-var chunk cache, for chunked datasets. */
1019  if (var->storage == NC_CHUNKED && var->chunkcache.size)
1020  if (H5Pset_chunk_cache(access_plistid, var->chunkcache.nelems,
1021  var->chunkcache.size, var->chunkcache.preemption) < 0)
1022  BAIL(NC_EHDFERR);
1023 
1024  /* At long last, create the dataset. */
1025  name_to_use = var->alt_name ? var->alt_name : var->hdr.name;
1026  LOG((4, "%s: about to H5Dcreate2 dataset %s of type 0x%x", __func__,
1027  name_to_use, typeid));
1028  if ((hdf5_var->hdf_datasetid = H5Dcreate2(hdf5_grp->hdf_grpid, name_to_use, typeid,
1029  spaceid, H5P_DEFAULT, plistid, access_plistid)) < 0)
1030  BAIL(NC_EHDFERR);
1031  var->created = NC_TRUE;
1032  var->is_new_var = NC_FALSE;
1033 
1034  /* Always write the hidden coordinates attribute, which lists the
1035  * dimids of this var. When present, this speeds opens. When not
1036  * present, dimscale matching is used. */
1037  if (var->ndims)
1038  if ((retval = write_coord_dimids(var)))
1039  BAIL(retval);
1040 
1041  /* If this is a dimscale, mark it as such in the HDF5 file. Also
1042  * find the dimension info and store the dataset id of the dimscale
1043  * dataset. */
1044  if (hdf5_var->dimscale)
1045  {
1046  if (H5DSset_scale(hdf5_var->hdf_datasetid, var->hdr.name) < 0)
1047  BAIL(NC_EHDFERR);
1048 
1049  /* If this is a multidimensional coordinate variable, write a
1050  * coordinates attribute. */
1051  /* if (var->ndims > 1) */
1052  /* if ((retval = write_coord_dimids(var))) */
1053  /* BAIL(retval); */
1054 
1055  /* If desired, write the netCDF dimid. */
1056  if (write_dimid)
1057  if ((retval = write_netcdf4_dimid(hdf5_var->hdf_datasetid, var->dimids[0])))
1058  BAIL(retval);
1059  }
1060 
1061  /* If quantization is in use, write an attribute indicating it, a
1062  * single integer which is the number of significant digits
1063  * (NSD, for BitGroom and Granular BitRound) or number of significant bits
1064  * (NSB, for BitRound). */
1065  if (var->quantize_mode)
1066  if ((retval = write_quantize_att(var)))
1067  BAIL(retval);
1068 
1069  /* Write attributes for this var. */
1070  if ((retval = write_attlist(var->att, var->hdr.id, grp)))
1071  BAIL(retval);
1072 
1073  /* The file is now up-to-date with all settings for this var. */
1074  var->attr_dirty = NC_FALSE;
1075 
1076 exit:
1077  nullfree(params);
1078  if (typeid > 0 && H5Tclose(typeid) < 0)
1079  BAIL2(NC_EHDFERR);
1080  if (plistid > 0 && H5Pclose(plistid) < 0)
1081  BAIL2(NC_EHDFERR);
1082  if (access_plistid > 0 && H5Pclose(access_plistid) < 0)
1083  BAIL2(NC_EHDFERR);
1084  if (spaceid > 0 && H5Sclose(spaceid) < 0)
1085  BAIL2(NC_EHDFERR);
1086  if (fillp)
1087  {
1088  if (var->type_info->nc_type_class == NC_VLEN)
1089  nc_free_vlen((nc_vlen_t *)fillp);
1090  else if (var->type_info->nc_type_class == NC_STRING && *(char **)fillp)
1091  free(*(char **)fillp);
1092  free(fillp);
1093  }
1094 
1095  return retval;
1096 }
1097 
1111 int
1112 nc4_adjust_var_cache(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
1113 {
1114  size_t chunk_size_bytes = 1;
1115  int d;
1116  int retval;
1117 
1118  /* Nothing to be done for contiguous or compact data. */
1119  if (var->storage != NC_CHUNKED)
1120  return NC_NOERR;
1121 
1122 #ifdef USE_PARALLEL4
1123  /* Don't set cache for files using parallel I/O. */
1124  if (grp->nc4_info->parallel)
1125  return NC_NOERR;
1126 #endif
1127 
1128  /* How many bytes in the chunk? */
1129  for (d = 0; d < var->ndims; d++)
1130  chunk_size_bytes *= var->chunksizes[d];
1131  if (var->type_info->size)
1132  chunk_size_bytes *= var->type_info->size;
1133  else
1134  chunk_size_bytes *= sizeof(char *);
1135 
1136  /* If the chunk cache is too small, and the user has not changed
1137  * the default value of the chunk cache size, then increase the
1138  * size of the cache. */
1139  if (var->chunkcache.size == CHUNK_CACHE_SIZE)
1140  if (chunk_size_bytes > var->chunkcache.size)
1141  {
1142  var->chunkcache.size = chunk_size_bytes * DEFAULT_CHUNKS_IN_CACHE;
1143  if (var->chunkcache.size > DEFAULT_CHUNK_CACHE_SIZE)
1144  var->chunkcache.size = DEFAULT_CHUNK_CACHE_SIZE;
1145  if ((retval = nc4_reopen_dataset(grp, var)))
1146  return retval;
1147  }
1148 
1149  return NC_NOERR;
1150 }
1151 
1167 static int
1168 commit_type(NC_GRP_INFO_T *grp, NC_TYPE_INFO_T *type)
1169 {
1170  NC_HDF5_GRP_INFO_T *hdf5_grp;
1171  NC_HDF5_TYPE_INFO_T *hdf5_type;
1172  hid_t base_hdf_typeid;
1173  int retval;
1174 
1175  assert(grp && grp->format_grp_info && type && type->format_type_info);
1176 
1177  /* Get HDF5-specific group and type info. */
1178  hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
1179  hdf5_type = (NC_HDF5_TYPE_INFO_T *)type->format_type_info;
1180 
1181  /* Did we already record this type? */
1182  if (type->committed)
1183  return NC_NOERR;
1184 
1185  /* Is this a compound type? */
1186  if (type->nc_type_class == NC_COMPOUND)
1187  {
1188  NC_FIELD_INFO_T *field;
1189  hid_t hdf_base_typeid, hdf_typeid;
1190  size_t i;
1191 
1192  if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_COMPOUND, type->size)) < 0)
1193  return NC_EHDFERR;
1194  LOG((4, "creating compound type %s hdf_typeid 0x%x", type->hdr.name,
1195  hdf5_type->hdf_typeid));
1196 
1197  for(i=0;i<nclistlength(type->u.c.field);i++)
1198  {
1199  field = (NC_FIELD_INFO_T *)nclistget(type->u.c.field, i);
1200  assert(field);
1201  if ((retval = nc4_get_hdf_typeid(grp->nc4_info, field->nc_typeid,
1202  &hdf_base_typeid, type->endianness)))
1203  return retval;
1204 
1205  /* If this is an array, create a special array type. */
1206  if (field->ndims)
1207  {
1208  int d;
1209  hsize_t dims[NC_MAX_VAR_DIMS];
1210 
1211  for (d = 0; d < field->ndims; d++)
1212  dims[d] = (hsize_t)field->dim_size[d];
1213  if ((hdf_typeid = H5Tarray_create1(hdf_base_typeid, field->ndims,
1214  dims, NULL)) < 0)
1215  {
1216  if (H5Tclose(hdf_base_typeid) < 0)
1217  return NC_EHDFERR;
1218  return NC_EHDFERR;
1219  }
1220  if (H5Tclose(hdf_base_typeid) < 0)
1221  return NC_EHDFERR;
1222  }
1223  else
1224  hdf_typeid = hdf_base_typeid;
1225  LOG((4, "inserting field %s offset %d hdf_typeid 0x%x", field->hdr.name,
1226  field->offset, hdf_typeid));
1227  if (H5Tinsert(hdf5_type->hdf_typeid, field->hdr.name, field->offset,
1228  hdf_typeid) < 0)
1229  return NC_EHDFERR;
1230  if (H5Tclose(hdf_typeid) < 0)
1231  return NC_EHDFERR;
1232  }
1233  }
1234  else if (type->nc_type_class == NC_VLEN)
1235  {
1236  /* Find the HDF typeid of the base type of this vlen. */
1237  if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.v.base_nc_typeid,
1238  &base_hdf_typeid, type->endianness)))
1239  return retval;
1240 
1241  /* Create a vlen type. */
1242  if ((hdf5_type->hdf_typeid = H5Tvlen_create(base_hdf_typeid)) < 0)
1243  return NC_EHDFERR;
1244  }
1245  else if (type->nc_type_class == NC_OPAQUE)
1246  {
1247  /* Create the opaque type. */
1248  if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_OPAQUE, type->size)) < 0)
1249  return NC_EHDFERR;
1250  }
1251  else if (type->nc_type_class == NC_ENUM)
1252  {
1253  NC_ENUM_MEMBER_INFO_T *enum_m;
1254  size_t i;
1255 
1256  if (nclistlength(type->u.e.enum_member) == 0)
1257  return NC_EINVAL;
1258 
1259  /* Find the HDF typeid of the base type of this enum. */
1260  if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.e.base_nc_typeid,
1261  &base_hdf_typeid, type->endianness)))
1262  return retval;
1263 
1264  /* Create an enum type. */
1265  if ((hdf5_type->hdf_typeid = H5Tenum_create(base_hdf_typeid)) < 0)
1266  return NC_EHDFERR;
1267 
1268  /* Add all the members to the HDF5 type. */
1269  for(i=0;i<nclistlength(type->u.e.enum_member);i++) {
1270  enum_m = (NC_ENUM_MEMBER_INFO_T*)nclistget(type->u.e.enum_member,i);
1271  if (H5Tenum_insert(hdf5_type->hdf_typeid, enum_m->name, enum_m->value) < 0)
1272  return NC_EHDFERR;
1273  }
1274  }
1275  else
1276  {
1277  LOG((0, "Unknown class: %d", type->nc_type_class));
1278  return NC_EBADTYPE;
1279  }
1280 
1281  /* Commit the type. */
1282  if (H5Tcommit1(hdf5_grp->hdf_grpid, type->hdr.name, hdf5_type->hdf_typeid) < 0)
1283  return NC_EHDFERR;
1284  type->committed = NC_TRUE;
1285  LOG((4, "just committed type %s, HDF typeid: 0x%x", type->hdr.name,
1286  hdf5_type->hdf_typeid));
1287 
1288  /* Later we will always use the native typeid. In this case, it is
1289  * a copy of the same type pointed to by hdf_typeid, but it's
1290  * easier to maintain a copy. */
1291  if ((hdf5_type->native_hdf_typeid = H5Tget_native_type(hdf5_type->hdf_typeid,
1292  H5T_DIR_DEFAULT)) < 0)
1293  return NC_EHDFERR;
1294 
1295  return NC_NOERR;
1296 }
1297 
1308 static int
1309 write_nc3_strict_att(hid_t hdf_grpid)
1310 {
1311  hid_t attid = 0, spaceid = 0;
1312  int one = 1;
1313  int retval = NC_NOERR;
1314  htri_t attr_exists;
1315 
1316  /* If the attribute already exists, call that a success and return
1317  * NC_NOERR. */
1318  if ((attr_exists = H5Aexists(hdf_grpid, NC3_STRICT_ATT_NAME)) < 0)
1319  return NC_EHDFERR;
1320  if (attr_exists)
1321  return NC_NOERR;
1322 
1323  /* Create the attribute to mark this as a file that needs to obey
1324  * strict netcdf-3 rules. */
1325  if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
1326  BAIL(NC_EFILEMETA);
1327  if ((attid = H5Acreate1(hdf_grpid, NC3_STRICT_ATT_NAME,
1328  H5T_NATIVE_INT, spaceid, H5P_DEFAULT)) < 0)
1329  BAIL(NC_EFILEMETA);
1330  if (H5Awrite(attid, H5T_NATIVE_INT, &one) < 0)
1331  BAIL(NC_EFILEMETA);
1332 
1333 exit:
1334  if (spaceid > 0 && (H5Sclose(spaceid) < 0))
1335  BAIL2(NC_EFILEMETA);
1336  if (attid > 0 && (H5Aclose(attid) < 0))
1337  BAIL2(NC_EFILEMETA);
1338  return retval;
1339 }
1340 
1353 static int
1354 create_group(NC_GRP_INFO_T *grp)
1355 {
1356  NC_HDF5_GRP_INFO_T *hdf5_grp, *parent_hdf5_grp;
1357  hid_t gcpl_id = -1;
1358  int retval = NC_NOERR;;
1359 
1360  assert(grp && grp->format_grp_info && grp->parent &&
1361  grp->parent->format_grp_info);
1362 
1363  /* Get HDF5 specific group info for group and parent. */
1364  hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
1365  parent_hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->parent->format_grp_info;
1366  assert(parent_hdf5_grp->hdf_grpid);
1367 
1368  /* Create group, with link_creation_order set in the group
1369  * creation property list. */
1370  if ((gcpl_id = H5Pcreate(H5P_GROUP_CREATE)) < 0)
1371  BAIL(NC_EHDFERR);
1372 
1373  /* Set track_times to be FALSE. */
1374  if (H5Pset_obj_track_times(gcpl_id, 0) < 0)
1375  BAIL(NC_EHDFERR);
1376 
1377  /* Tell HDF5 to keep track of objects in creation order. */
1378  if (H5Pset_link_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0)
1379  BAIL(NC_EHDFERR);
1380 
1381  /* Tell HDF5 to keep track of attributes in creation order. */
1382  if (!grp->nc4_info->no_attr_create_order) {
1383  if (H5Pset_attr_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0)
1384  BAIL(NC_EHDFERR);
1385  }
1386 
1387  /* Create the group. */
1388  if ((hdf5_grp->hdf_grpid = H5Gcreate2(parent_hdf5_grp->hdf_grpid, grp->hdr.name,
1389  H5P_DEFAULT, gcpl_id, H5P_DEFAULT)) < 0)
1390  BAIL(NC_EHDFERR);
1391 
1392 exit:
1393  if (gcpl_id > -1 && H5Pclose(gcpl_id) < 0)
1394  BAIL2(NC_EHDFERR);
1395  if (retval)
1396  if (hdf5_grp->hdf_grpid > 0 && H5Gclose(hdf5_grp->hdf_grpid) < 0)
1397  BAIL2(NC_EHDFERR);
1398  return retval;
1399 }
1400 
1413 static int
1414 attach_dimscales(NC_GRP_INFO_T *grp)
1415 {
1416  NC_VAR_INFO_T *var;
1417  NC_HDF5_VAR_INFO_T *hdf5_var;
1418 
1419  /* Attach dimension scales. */
1420  for (size_t v = 0; v < ncindexsize(grp->vars); v++)
1421  {
1422  /* Get pointer to var and HDF5-specific var info. */
1423  var = (NC_VAR_INFO_T *)ncindexith(grp->vars, v);
1424  assert(var && var->format_var_info);
1425  hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
1426 
1427  /* Scales themselves do not attach. But I really wish they
1428  * would. */
1429  if (hdf5_var->dimscale)
1430  continue;
1431 
1432  /* Find the scale for each dimension, if any, and attach it. */
1433  for (unsigned int d = 0; d < var->ndims; d++)
1434  {
1435  /* Is there a dimscale for this dimension? */
1436  if (hdf5_var->dimscale_attached)
1437  {
1438  if (!hdf5_var->dimscale_attached[d])
1439  {
1440  hid_t dsid; /* Dataset ID for dimension */
1441  assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] &&
1442  var->dim[d]->format_dim_info);
1443 
1444  LOG((2, "%s: attaching scale for dimid %d to var %s",
1445  __func__, var->dimids[d], var->hdr.name));
1446 
1447  /* Find dataset ID for dimension */
1448  if (var->dim[d]->coord_var)
1449  dsid = ((NC_HDF5_VAR_INFO_T *)(var->dim[d]->coord_var->format_var_info))->hdf_datasetid;
1450  else
1451  dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid;
1452 
1453  if (dsid <= 0)
1454  return NC_EDIMSCALE;
1455 
1456  /* Attach the scale. */
1457  if (H5DSattach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0)
1458  return NC_EDIMSCALE;
1459  hdf5_var->dimscale_attached[d] = NC_TRUE;
1460  }
1461  }
1462  }
1463  }
1464 
1465  return NC_NOERR;
1466 }
1467 
1478 static int
1479 var_exists(hid_t grpid, char *name, nc_bool_t *exists)
1480 {
1481  htri_t link_exists;
1482 
1483  /* Reset the boolean */
1484  *exists = NC_FALSE;
1485 
1486  /* Check if the object name exists in the group */
1487  if ((link_exists = H5Lexists(grpid, name, H5P_DEFAULT)) < 0)
1488  return NC_EHDFERR;
1489  if (link_exists)
1490  {
1491  H5G_stat_t statbuf;
1492 
1493  /* Get info about the object */
1494  if (H5Gget_objinfo(grpid, name, 1, &statbuf) < 0)
1495  return NC_EHDFERR;
1496 
1497  if (H5G_DATASET == statbuf.type)
1498  *exists = NC_TRUE;
1499  }
1500 
1501  return NC_NOERR;
1502 }
1503 
1519 static int
1520 remove_coord_atts(hid_t hdf_datasetid)
1521 {
1522  htri_t attr_exists;
1523 
1524  /* If the variable dataset has an optional NC_DIMID_ATT_NAME
1525  * attribute, delete it. */
1526  if ((attr_exists = H5Aexists(hdf_datasetid, NC_DIMID_ATT_NAME)) < 0)
1527  return NC_EHDFERR;
1528  if (attr_exists)
1529  {
1530  if (H5Adelete(hdf_datasetid, NC_DIMID_ATT_NAME) < 0)
1531  return NC_EHDFERR;
1532  }
1533 
1534  /* Remove the dimension scale 'CLASS' & 'NAME' attributes. */
1535  if ((attr_exists = H5Aexists(hdf_datasetid,
1536  HDF5_DIMSCALE_CLASS_ATT_NAME)) < 0)
1537  return NC_EHDFERR;
1538  if (attr_exists)
1539  {
1540  if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_CLASS_ATT_NAME) < 0)
1541  return NC_EHDFERR;
1542  }
1543  if ((attr_exists = H5Aexists(hdf_datasetid,
1544  HDF5_DIMSCALE_NAME_ATT_NAME)) < 0)
1545  return NC_EHDFERR;
1546  if (attr_exists)
1547  {
1548  if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_NAME_ATT_NAME) < 0)
1549  return NC_EHDFERR;
1550  }
1551  return NC_NOERR;
1552 }
1553 
1568 static int
1569 write_var(NC_VAR_INFO_T *var, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
1570 {
1571  NC_HDF5_GRP_INFO_T *hdf5_grp;
1572  NC_HDF5_VAR_INFO_T *hdf5_var;
1573  nc_bool_t replace_existing_var = NC_FALSE;
1574  int retval;
1575 
1576  assert(var && var->format_var_info && grp && grp->format_grp_info);
1577 
1578  LOG((4, "%s: writing var %s", __func__, var->hdr.name));
1579 
1580  /* Get HDF5-specific group and var info. */
1581  hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
1582  hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
1583 
1584  /* If the variable has already been created & the fill value changed,
1585  * indicate that the existing variable should be replaced. */
1586  if (var->created && var->fill_val_changed)
1587  {
1588  replace_existing_var = NC_TRUE;
1589  var->fill_val_changed = NC_FALSE;
1590  /* If the variable is going to be replaced, we need to flag any
1591  other attributes associated with the variable as 'dirty', or
1592  else *only* the fill value attribute will be copied over and
1593  the rest will be lost. See
1594  https://github.com/Unidata/netcdf-c/issues/239 */
1595  flag_atts_dirty(var->att);
1596  }
1597 
1598  /* Is this a coordinate var that has already been created in
1599  * the HDF5 file as a dimscale dataset? Check for dims with the
1600  * same name in this group. If there is one, check to see if
1601  * this object exists in the HDF group. */
1602  if (var->became_coord_var)
1603  {
1604  if ((NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name))
1605  {
1606  nc_bool_t exists;
1607 
1608  if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists)))
1609  return retval;
1610  if (exists)
1611  {
1612  /* Indicate that the variable already exists, and should
1613  * be replaced. */
1614  replace_existing_var = NC_TRUE;
1615  flag_atts_dirty(var->att);
1616  }
1617  }
1618  }
1619 
1620  /* Check dims if the variable will be replaced, so that the
1621  * dimensions will be de-attached and re-attached correctly. */
1622  if (replace_existing_var)
1623  {
1624  NC_DIM_INFO_T *d1;
1625 
1626  /* Is there a dim with this var's name? */
1627  if ((d1 = (NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name)))
1628  {
1629  nc_bool_t exists;
1630  assert(d1->format_dim_info && d1->hdr.name);
1631 
1632  if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists)))
1633  return retval;
1634  if (exists)
1635  {
1636  hid_t dsid;
1637 
1638  /* Find dataset ID for dimension */
1639  if (d1->coord_var)
1640  dsid = ((NC_HDF5_VAR_INFO_T *)d1->coord_var->format_var_info)->hdf_datasetid;
1641  else
1642  dsid = ((NC_HDF5_DIM_INFO_T *)d1->format_dim_info)->hdf_dimscaleid;
1643  assert(dsid > 0);
1644 
1645  /* If we're replacing an existing dimscale dataset, go to
1646  * every var in the file and detach this dimension scale,
1647  * because we have to delete it. */
1648  if ((retval = rec_detach_scales(grp->nc4_info->root_grp,
1649  var->dimids[0], dsid)))
1650  return retval;
1651  }
1652  }
1653  }
1654 
1655  /* If this is not a dimension scale, remove any attached scales,
1656  * and delete dimscale attributes from the var. */
1657  if (var->was_coord_var && hdf5_var->dimscale_attached)
1658  {
1659  /* If the variable already exists in the file, Remove any dimension scale
1660  * attributes from it, if they exist. */
1661  if (var->created)
1662  if ((retval = remove_coord_atts(hdf5_var->hdf_datasetid)))
1663  return retval;
1664 
1665  /* If this is a regular var, detach all its dim scales. */
1666  for (unsigned int d = 0; d < var->ndims; d++)
1667  {
1668  if (hdf5_var->dimscale_attached[d])
1669  {
1670  hid_t dsid; /* Dataset ID for dimension */
1671  assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] &&
1672  var->dim[d]->format_dim_info);
1673 
1674  /* Find dataset ID for dimension */
1675  if (var->dim[d]->coord_var)
1676  dsid = ((NC_HDF5_VAR_INFO_T *)var->dim[d]->coord_var->format_var_info)->hdf_datasetid;
1677  else
1678  dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid;
1679  assert(dsid > 0);
1680 
1681  /* Detach this dim scale. */
1682  if (H5DSdetach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0)
1683  return NC_EHDFERR;
1684  hdf5_var->dimscale_attached[d] = NC_FALSE;
1685  }
1686  }
1687  }
1688 
1689  /* Delete the HDF5 dataset that is to be replaced. */
1690  if (replace_existing_var)
1691  {
1692  /* Free the HDF5 dataset id. */
1693  if (hdf5_var->hdf_datasetid && H5Dclose(hdf5_var->hdf_datasetid) < 0)
1694  return NC_EHDFERR;
1695  hdf5_var->hdf_datasetid = 0;
1696 
1697  /* Now delete the variable. */
1698  if (H5Gunlink(hdf5_grp->hdf_grpid, var->hdr.name) < 0)
1699  return NC_EDIMMETA;
1700  }
1701 
1702  /* Create the dataset. */
1703  if (var->is_new_var || replace_existing_var)
1704  {
1705  if ((retval = var_create_dataset(grp, var, write_dimid)))
1706  return retval;
1707  }
1708  else
1709  {
1710  if (write_dimid && var->ndims)
1711  if ((retval = write_netcdf4_dimid(hdf5_var->hdf_datasetid,
1712  var->dimids[0])))
1713  return retval;
1714  }
1715 
1716  if (replace_existing_var)
1717  {
1718  /* If this is a dimension scale, reattach the scale everywhere it
1719  * is used. (Recall that netCDF dimscales are always 1-D). */
1720  if(hdf5_var->dimscale)
1721  {
1722  if ((retval = rec_reattach_scales(grp->nc4_info->root_grp,
1723  var->dimids[0], hdf5_var->hdf_datasetid)))
1724  return retval;
1725  }
1726  /* If it's not a dimension scale, clear the dimscale attached flags,
1727  * so the dimensions are re-attached. */
1728  else
1729  {
1730  if (hdf5_var->dimscale_attached)
1731  memset(hdf5_var->dimscale_attached, 0, sizeof(nc_bool_t) * var->ndims);
1732  }
1733  }
1734 
1735  /* Clear coord. var state transition flags */
1736  var->was_coord_var = NC_FALSE;
1737  var->became_coord_var = NC_FALSE;
1738 
1739  /* Now check the attributes for this var. */
1740  if (var->attr_dirty)
1741  {
1742  /* Write attributes for this var. */
1743  if ((retval = write_attlist(var->att, var->hdr.id, grp)))
1744  return retval;
1745  var->attr_dirty = NC_FALSE;
1746  }
1747 
1748  return NC_NOERR;
1749 }
1750 
1762 int
1763 nc4_create_dim_wo_var(NC_DIM_INFO_T *dim)
1764 {
1765  NC_HDF5_DIM_INFO_T *hdf5_dim;
1766  NC_HDF5_GRP_INFO_T *hdf5_grp;
1767  hid_t spaceid = -1, create_propid = -1;
1768  hsize_t dims[1], max_dims[1], chunk_dims[1] = {1};
1769  char dimscale_wo_var[NC_MAX_NAME];
1770  int retval = NC_NOERR;
1771 
1772  LOG((4, "%s: creating dim %s", __func__, dim->hdr.name));
1773 
1774  /* Sanity check */
1775  assert(!dim->coord_var);
1776 
1777  /* Get HDF5-specific dim and group info. */
1778  hdf5_grp = (NC_HDF5_GRP_INFO_T *)dim->container->format_grp_info;
1779  hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
1780 
1781  /* Create a property list. */
1782  if ((create_propid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
1783  BAIL(NC_EHDFERR);
1784 
1785  /* Turn off recording of times associated with this object. */
1786  if (H5Pset_obj_track_times(create_propid, 0) < 0)
1787  BAIL(NC_EHDFERR);
1788 
1789  /* Set size of dataset to size of dimension. */
1790  dims[0] = dim->len;
1791  max_dims[0] = dim->len;
1792 
1793  /* If this dimension scale is unlimited (i.e. it's an unlimited
1794  * dimension), then set up chunking, with a chunksize of 1. */
1795  if (dim->unlimited)
1796  {
1797  max_dims[0] = H5S_UNLIMITED;
1798  if (H5Pset_chunk(create_propid, 1, chunk_dims) < 0)
1799  BAIL(NC_EHDFERR);
1800  }
1801 
1802  /* Set up space. */
1803  if ((spaceid = H5Screate_simple(1, dims, max_dims)) < 0)
1804  BAIL(NC_EHDFERR);
1805 
1806  /* Turn on creation-order tracking. */
1807  if (!dim->container->nc4_info->no_attr_create_order) {
1808  if (H5Pset_attr_creation_order(create_propid, H5P_CRT_ORDER_TRACKED|
1809  H5P_CRT_ORDER_INDEXED) < 0)
1810  BAIL(NC_EHDFERR);
1811  }
1812  /* Create the dataset that will be the dimension scale. */
1813  LOG((4, "%s: about to H5Dcreate1 a dimscale dataset %s", __func__,
1814  dim->hdr.name));
1815  if ((hdf5_dim->hdf_dimscaleid = H5Dcreate2(hdf5_grp->hdf_grpid, dim->hdr.name,
1816  H5T_IEEE_F32BE, spaceid,
1817  H5P_DEFAULT, create_propid,
1818  H5P_DEFAULT)) < 0)
1819  BAIL(NC_EHDFERR);
1820 
1821  /* Indicate that this is a scale. Also indicate that not
1822  * be shown to the user as a variable. It is hidden. It is
1823  * a DIM WITHOUT A VARIABLE! */
1824  snprintf(dimscale_wo_var, sizeof(dimscale_wo_var), "%s%10d", DIM_WITHOUT_VARIABLE, (int)dim->len);
1825  if (H5DSset_scale(hdf5_dim->hdf_dimscaleid, dimscale_wo_var) < 0)
1826  BAIL(NC_EHDFERR);
1827 
1828  /* Since this dimension was created out of order, we cannot rely on
1829  * it getting the correct dimid on file open. We must assign it
1830  * explicitly. */
1831  if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id)))
1832  BAIL(retval);
1833 
1834 exit:
1835  if (spaceid > 0 && H5Sclose(spaceid) < 0)
1836  BAIL2(NC_EHDFERR);
1837  if (create_propid > 0 && H5Pclose(create_propid) < 0)
1838  BAIL2(NC_EHDFERR);
1839  return retval;
1840 }
1841 
1854 static int
1855 write_dim(NC_DIM_INFO_T *dim, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
1856 {
1857  NC_HDF5_DIM_INFO_T *hdf5_dim;
1858  int retval = NC_NOERR;
1859 
1860  assert(dim && dim->format_dim_info && grp && grp->format_grp_info);
1861 
1862  /* Get HDF5-specific dim and group info. */
1863  hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
1864 
1865  /* If there's no dimscale dataset for this dim, create one,
1866  * and mark that it should be hidden from netCDF as a
1867  * variable. (That is, it should appear as a dimension
1868  * without an associated variable.) */
1869  if (!hdf5_dim->hdf_dimscaleid)
1870  if ((retval = nc4_create_dim_wo_var(dim)))
1871  BAIL(retval);
1872 
1873  /* Did we extend an unlimited dimension? */
1874  if (dim->extended)
1875  {
1876  NC_VAR_INFO_T *v1 = NULL;
1877 
1878  assert(dim->unlimited);
1879 
1880  /* If this is a dimension with an associated coordinate var,
1881  * then update the length of that coord var. */
1882  v1 = dim->coord_var;
1883  if (v1)
1884  {
1885  NC_HDF5_VAR_INFO_T *hdf5_v1;
1886  hsize_t *new_size;
1887  int d1;
1888 
1889  hdf5_v1 = (NC_HDF5_VAR_INFO_T *)v1->format_var_info;
1890 
1891  /* Extend the dimension scale dataset to reflect the new
1892  * length of the dimension. */
1893  if (!(new_size = malloc(v1->ndims * sizeof(hsize_t))))
1894  BAIL(NC_ENOMEM);
1895  for (d1 = 0; d1 < v1->ndims; d1++)
1896  {
1897  assert(v1->dim[d1] && v1->dim[d1]->hdr.id == v1->dimids[d1]);
1898  new_size[d1] = v1->dim[d1]->len;
1899  }
1900  if (H5Dset_extent(hdf5_v1->hdf_datasetid, new_size) < 0)
1901  BAIL(NC_EHDFERR);
1902  free(new_size);
1903  }
1904  }
1905 
1906  /* If desired, write the secret dimid. This will be used instead of
1907  * the dimid that the dimension would otherwise receive based on
1908  * creation order. This can be necessary when dims and their
1909  * coordinate variables were created in different order. */
1910  if (write_dimid && hdf5_dim->hdf_dimscaleid)
1911  if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id)))
1912  BAIL(retval);
1913 
1914 exit:
1915 
1916  return retval;
1917 }
1918 
1931 int
1932 nc4_rec_write_metadata(NC_GRP_INFO_T *grp, nc_bool_t bad_coord_order)
1933 {
1934  NC_DIM_INFO_T *dim = NULL;
1935  NC_VAR_INFO_T *var = NULL;
1936  NC_GRP_INFO_T *child_grp = NULL;
1937  int coord_varid = -1;
1938  size_t var_index = 0;
1939  size_t dim_index = 0;
1940  int retval;
1941 
1942  assert(grp && grp->hdr.name &&
1943  ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid);
1944  LOG((3, "%s: grp->hdr.name %s, bad_coord_order %d", __func__, grp->hdr.name,
1945  bad_coord_order));
1946 
1947  /* Write global attributes for this group. */
1948  if ((retval = write_attlist(grp->att, NC_GLOBAL, grp)))
1949  return retval;
1950 
1951  /* Set the pointers to the beginning of the list of dims & vars in this
1952  * group. */
1953  dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, dim_index);
1954  var = (NC_VAR_INFO_T *)ncindexith(grp->vars, var_index);
1955 
1956  /* Because of HDF5 ordering the dims and vars have to be stored in
1957  * this way to ensure that the dims and coordinate vars come out in
1958  * the correct order. */
1959  while (dim || var)
1960  {
1961  nc_bool_t found_coord, wrote_coord;
1962 
1963  /* Write non-coord dims in order, stopping at the first one that
1964  * has an associated coord var. */
1965  for (found_coord = NC_FALSE; dim && !found_coord; )
1966  {
1967  if (!dim->coord_var)
1968  {
1969  if ((retval = write_dim(dim, grp, bad_coord_order)))
1970  return retval;
1971  }
1972  else
1973  {
1974  coord_varid = dim->coord_var->hdr.id;
1975  found_coord = NC_TRUE;
1976  }
1977  dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, ++dim_index);
1978  }
1979 
1980  /* Write each var. When we get to the coord var we are waiting
1981  * for (if any), then we break after writing it. */
1982  for (wrote_coord = NC_FALSE; var && !wrote_coord; )
1983  {
1984  if ((retval = write_var(var, grp, bad_coord_order)))
1985  return retval;
1986  if (found_coord && var->hdr.id == coord_varid)
1987  wrote_coord = NC_TRUE;
1988  var = (NC_VAR_INFO_T *)ncindexith(grp->vars, ++var_index);
1989  }
1990  } /* end while */
1991 
1992  /* Attach dimscales to vars in this group. Unless directed not to. */
1993  if (!grp->nc4_info->no_dimscale_attach) {
1994  if ((retval = attach_dimscales(grp)))
1995  return retval;
1996  }
1997 
1998  /* If there are any child groups, write their metadata. */
1999  for (size_t i = 0; i < ncindexsize(grp->children); i++)
2000  {
2001  child_grp = (NC_GRP_INFO_T *)ncindexith(grp->children, i);
2002  assert(child_grp);
2003  if ((retval = nc4_rec_write_metadata(child_grp, bad_coord_order)))
2004  return retval;
2005  }
2006  return NC_NOERR;
2007 }
2008 
2018 int
2019 nc4_rec_write_groups_types(NC_GRP_INFO_T *grp)
2020 {
2021  NC_GRP_INFO_T *child_grp;
2022  NC_HDF5_GRP_INFO_T *hdf5_grp;
2023  NC_TYPE_INFO_T *type;
2024  int retval;
2025  size_t i;
2026 
2027  assert(grp && grp->hdr.name && grp->format_grp_info);
2028  LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
2029 
2030  /* Get HDF5-specific group info. */
2031  hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
2032 
2033  /* Create the group in the HDF5 file if it doesn't exist. */
2034  if (!hdf5_grp->hdf_grpid)
2035  if ((retval = create_group(grp)))
2036  return retval;
2037 
2038  /* If this is the root group of a file with strict NC3 rules, write
2039  * an attribute. But don't leave the attribute open. */
2040  if (!grp->parent && (grp->nc4_info->cmode & NC_CLASSIC_MODEL))
2041  if ((retval = write_nc3_strict_att(hdf5_grp->hdf_grpid)))
2042  return retval;
2043 
2044  /* If there are any user-defined types, write them now. */
2045  for(i=0;i<ncindexsize(grp->type);i++) {
2046  type = (NC_TYPE_INFO_T *)ncindexith(grp->type, i);
2047  assert(type);
2048  if ((retval = commit_type(grp, type)))
2049  return retval;
2050  }
2051 
2052  /* If there are any child groups, write their groups and types. */
2053  for(i=0;i<ncindexsize(grp->children);i++) {
2054  if((child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children,i)) == NULL) continue;
2055  if ((retval = nc4_rec_write_groups_types(child_grp)))
2056  return retval;
2057  }
2058  return NC_NOERR;
2059 }
2060 
2074 int
2075 nc4_rec_match_dimscales(NC_GRP_INFO_T *grp)
2076 {
2077  NC_GRP_INFO_T *g;
2078  NC_VAR_INFO_T *var;
2079  NC_DIM_INFO_T *dim;
2080  int retval = NC_NOERR;
2081  size_t i;
2082 
2083  assert(grp && grp->hdr.name);
2084  LOG((4, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
2085 
2086  /* Perform var dimscale match for child groups. */
2087  for (i = 0; i < ncindexsize(grp->children); i++)
2088  {
2089  g = (NC_GRP_INFO_T*)ncindexith(grp->children, i);
2090  assert(g);
2091  if ((retval = nc4_rec_match_dimscales(g)))
2092  return retval;
2093  }
2094 
2095  /* Check all the vars in this group. If they have dimscale info,
2096  * try and find a dimension for them. */
2097  for (i = 0; i < ncindexsize(grp->vars); i++)
2098  {
2099  NC_HDF5_VAR_INFO_T *hdf5_var;
2100  int d;
2101 
2102  /* Get pointer to var and to the HDF5-specific var info. */
2103  var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i);
2104  assert(var && var->format_var_info);
2105  hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
2106 
2107  /* Check all vars and see if dim[i] != NULL if dimids[i] valid. */
2108  /* This loop is very odd. Under normal circumstances, var->dimid[d] is zero
2109  (from the initial calloc) which is a legitimate dimid. The code does not
2110  distinquish this case from the dimscale case where the id might actually
2111  be defined.
2112  The original nc4_find_dim searched up the group tree looking for the given
2113  dimid in one of the dim lists associated with each ancestor group.
2114  I changed nc4_fnd_dim to use the dimid directly using h5->alldims.
2115  However, here that is incorrect because it will find the dimid 0 always
2116  (if any dimensions were defined). Except that when dimscale dimids have
2117  been defined, one or more of the values in var->dimids will have a
2118  legitimate value.
2119  The solution I choose is to modify nc4_var_list_add to initialize dimids to
2120  illegal values (-1). This is another example of the problems with dimscales.
2121  */
2122  const size_t ndims = var->ndims;
2123  for (size_t d = 0; d < ndims; d++)
2124  {
2125  if (var->dim[d] == NULL) {
2126  nc4_find_dim(grp, var->dimids[d], &var->dim[d], NULL);
2127  }
2128  /* assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d]); */
2129  }
2130 
2131  /* Skip dimension scale variables */
2132  if (!hdf5_var->dimscale)
2133  {
2134  /* Are there dimscales for this variable? */
2135  if (hdf5_var->dimscale_hdf5_objids)
2136  {
2137  for (size_t d = 0; d < var->ndims; d++)
2138  {
2139  nc_bool_t finished = NC_FALSE;
2140  LOG((5, "%s: var %s has dimscale info...", __func__, var->hdr.name));
2141 
2142  /* Check this and parent groups. */
2143  for (g = grp; g && !finished; g = g->parent)
2144  {
2145  /* Check all dims in this group. */
2146  for (size_t j = 0; j < ncindexsize(g->dim); j++)
2147  {
2148  /* Get the HDF5 specific dim info. */
2149  NC_HDF5_DIM_INFO_T *hdf5_dim;
2150  dim = (NC_DIM_INFO_T *)ncindexith(g->dim, j);
2151  assert(dim && dim->format_dim_info);
2152  hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
2153 
2154  /* Check for exact match of fileno/objid arrays
2155  * to find identical objects in HDF5 file. */
2156 #if H5_VERSION_GE(1,12,0)
2157  int token_cmp;
2158  if (H5Otoken_cmp(hdf5_var->hdf_datasetid, &hdf5_var->dimscale_hdf5_objids[d].token, &hdf5_dim->hdf5_objid.token, &token_cmp) < 0)
2159  return NC_EHDFERR;
2160 
2161  if (hdf5_var->dimscale_hdf5_objids[d].fileno == hdf5_dim->hdf5_objid.fileno &&
2162  token_cmp == 0)
2163 #else
2164  if (hdf5_var->dimscale_hdf5_objids[d].fileno[0] == hdf5_dim->hdf5_objid.fileno[0] &&
2165  hdf5_var->dimscale_hdf5_objids[d].objno[0] == hdf5_dim->hdf5_objid.objno[0] &&
2166  hdf5_var->dimscale_hdf5_objids[d].fileno[1] == hdf5_dim->hdf5_objid.fileno[1] &&
2167  hdf5_var->dimscale_hdf5_objids[d].objno[1] == hdf5_dim->hdf5_objid.objno[1])
2168 #endif
2169  {
2170  LOG((4, "%s: for dimension %d, found dim %s", __func__,
2171  d, dim->hdr.name));
2172  var->dimids[d] = dim->hdr.id;
2173  var->dim[d] = dim;
2174  finished = NC_TRUE;
2175  break;
2176  }
2177  } /* next dim */
2178  } /* next grp */
2179  LOG((5, "%s: dimid for this dimscale is %d", __func__,
2180  var->type_info->hdr.id));
2181  } /* next var->dim */
2182  }
2183  /* No dimscales for this var! Invent phony dimensions. */
2184  else
2185  {
2186  hid_t spaceid = 0;
2187  hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL;
2188  int dataset_ndims;
2189 
2190  /* Find the space information for this dimension. */
2191  if ((spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0)
2192  return NC_EHDFERR;
2193 
2194  /* Get the len of each dim in the space. */
2195  if (var->ndims)
2196  {
2197  if (!(h5dimlen = malloc(var->ndims * sizeof(hsize_t))))
2198  return NC_ENOMEM;
2199  if (!(h5dimlenmax = malloc(var->ndims * sizeof(hsize_t))))
2200  {
2201  free(h5dimlen);
2202  return NC_ENOMEM;
2203  }
2204  if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, h5dimlen,
2205  h5dimlenmax)) < 0) {
2206  free(h5dimlenmax);
2207  free(h5dimlen);
2208  return NC_EHDFERR;
2209  }
2210  if (dataset_ndims != var->ndims) {
2211  free(h5dimlenmax);
2212  free(h5dimlen);
2213  return NC_EHDFERR;
2214  }
2215  }
2216  else
2217  {
2218  /* Make sure it's scalar. */
2219  if (H5Sget_simple_extent_type(spaceid) != H5S_SCALAR)
2220  return NC_EHDFERR;
2221  }
2222 
2223  /* Release the space object. */
2224  if (H5Sclose(spaceid) < 0) {
2225  free(h5dimlen);
2226  free(h5dimlenmax);
2227  return NC_EHDFERR;
2228  }
2229 
2230  /* Create a phony dimension for each dimension in the
2231  * dataset, unless there already is one the correct
2232  * size. */
2233  for (d = 0; d < var->ndims; d++)
2234  {
2235  nc_bool_t match = NC_FALSE;
2236  /* Is there already a phony dimension of the correct size? */
2237  for(size_t k=0;k<ncindexsize(grp->dim);k++) {
2238  if((dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,k)) == NULL) continue;
2239  if ((dim->len == h5dimlen[d]) &&
2240  ((h5dimlenmax[d] == H5S_UNLIMITED && dim->unlimited) ||
2241  (h5dimlenmax[d] != H5S_UNLIMITED && !dim->unlimited)))
2242  {match = NC_TRUE; break;}
2243  }
2244 
2245  /* Didn't find a phony dim? Then create one. */
2246  if (match == NC_FALSE)
2247  {
2248  char phony_dim_name[NC_MAX_NAME + 1];
2249  snprintf(phony_dim_name, sizeof(phony_dim_name), "phony_dim_%d", grp->nc4_info->next_dimid);
2250  LOG((3, "%s: creating phony dim for var %s", __func__, var->hdr.name));
2251  if ((retval = nc4_dim_list_add(grp, phony_dim_name, h5dimlen[d], -1, &dim)))
2252  {
2253  free(h5dimlenmax);
2254  free(h5dimlen);
2255  return retval;
2256  }
2257  /* Create struct for HDF5-specific dim info. */
2258  if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T))))
2259  return NC_ENOMEM;
2260  if (h5dimlenmax[d] == H5S_UNLIMITED)
2261  dim->unlimited = NC_TRUE;
2262  }
2263 
2264  /* The variable must remember the dimid. */
2265  var->dimids[d] = dim->hdr.id;
2266  var->dim[d] = dim;
2267  } /* next dim */
2268 
2269  /* Free the memory we malloced. */
2270  free(h5dimlen);
2271  free(h5dimlenmax);
2272  }
2273  }
2274  }
2275 
2276  return retval;
2277 }
2278 
2290 void
2291 reportobject(int uselog, hid_t id, unsigned int type)
2292 {
2293  char name[NC_HDF5_MAX_NAME];
2294  ssize_t len;
2295  const char* typename = NULL;
2296  long long printid = (long long)id;
2297 
2298  len = H5Iget_name(id, name, NC_HDF5_MAX_NAME);
2299  if(len < 0) return;
2300  name[len] = '\0';
2301 
2302  switch (type) {
2303  case H5F_OBJ_FILE: typename = "File"; break;
2304  case H5F_OBJ_DATASET: typename = "Dataset"; break;
2305  case H5F_OBJ_GROUP: typename = "Group"; break;
2306  case H5F_OBJ_DATATYPE: typename = "Datatype"; break;
2307  case H5F_OBJ_ATTR:
2308  typename = "Attribute";
2309  len = H5Aget_name(id, NC_HDF5_MAX_NAME, name);
2310  if(len < 0) len = 0;
2311  name[len] = '\0';
2312  break;
2313  default: typename = "<unknown>"; break;
2314  }
2315 #ifdef LOGGING
2316  if(uselog) {
2317  LOG((0,"Type = %s(%lld) name='%s'",typename,printid,name));
2318  } else
2319 #endif
2320  {
2321  fprintf(stderr,"Type = %s(%lld) name='%s'",typename,printid,name);
2322  }
2323 
2324 }
2325 
2336 static void
2337 reportopenobjectsT(int uselog, hid_t fid, int ntypes, unsigned int* otypes)
2338 {
2339  int t,i;
2340  ssize_t ocount;
2341  hid_t* idlist = NULL;
2342 
2343  /* Always report somehow */
2344 #ifdef LOGGING
2345  if(uselog)
2346  LOG((0,"\nReport: open objects on %lld",(long long)fid));
2347  else
2348 #endif
2349  fprintf(stdout,"\nReport: open objects on %lld\n",(long long)fid);
2350  size_t maxobjs = (size_t)H5Fget_obj_count(fid,H5F_OBJ_ALL);
2351  if(idlist != NULL) free(idlist);
2352  idlist = (hid_t*)malloc(sizeof(hid_t)*maxobjs);
2353  for(t=0;t<ntypes;t++) {
2354  unsigned int ot = otypes[t];
2355  ocount = H5Fget_obj_ids(fid,ot,maxobjs,idlist);
2356  for(i=0;i<ocount;i++) {
2357  hid_t o = idlist[i];
2358  reportobject(uselog,o,ot);
2359  }
2360  }
2361  if(idlist != NULL) free(idlist);
2362 }
2363 
2372 void
2373 reportopenobjects(int uselog, hid_t fid)
2374 {
2375  unsigned int OTYPES[5] = {H5F_OBJ_FILE, H5F_OBJ_DATASET, H5F_OBJ_GROUP,
2376  H5F_OBJ_DATATYPE, H5F_OBJ_ATTR};
2377 
2378  reportopenobjectsT(uselog, fid ,5, OTYPES);
2379 }
2380 
2388 void
2389 showopenobjects5(NC_FILE_INFO_T* h5)
2390 {
2391  NC_HDF5_FILE_INFO_T *hdf5_info;
2392 
2393  assert(h5 && h5->format_file_info);
2394  hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
2395 
2396  fprintf(stderr,"===== begin showopenobjects =====\n");
2397  reportopenobjects(0,hdf5_info->hdfid);
2398  fprintf(stderr,"===== end showopenobjects =====\n");
2399  fflush(stderr);
2400 }
2401 
2410 void
2411 showopenobjects(int ncid)
2412 {
2413  NC_FILE_INFO_T* h5 = NULL;
2414 
2415  /* Find our metadata for this file. */
2416  if (nc4_find_nc_grp_h5(ncid, NULL, NULL, &h5) != NC_NOERR)
2417  fprintf(stderr,"failed\n");
2418  else
2419  showopenobjects5(h5);
2420  fflush(stderr);
2421 }
2422 
2434 int
2435 NC4_hdf5get_libversion(unsigned* major,unsigned* minor,unsigned* release)
2436 {
2437  if(H5get_libversion(major,minor,release) < 0)
2438  return NC_EHDFERR;
2439  return NC_NOERR;
2440 }
2441 
2452 int
2453 NC4_hdf5get_superblock(struct NC_FILE_INFO* h5, int* idp)
2454 {
2455  NC_HDF5_FILE_INFO_T *hdf5_info;
2456  int stat = NC_NOERR;
2457  unsigned super;
2458  hid_t plist = -1;
2459 
2460  assert(h5 && h5->format_file_info);
2461  hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
2462 
2463  if((plist = H5Fget_create_plist(hdf5_info->hdfid)) < 0)
2464  {stat = NC_EHDFERR; goto done;}
2465  if(H5Pget_version(plist, &super, NULL, NULL, NULL) < 0)
2466  {stat = NC_EHDFERR; goto done;}
2467  if(idp) *idp = (int)super;
2468 done:
2469  if(plist >= 0) H5Pclose(plist);
2470  return stat;
2471 }
2472 
2473 static int NC4_strict_att_exists(NC_FILE_INFO_T*);
2474 static int NC4_walk(hid_t, int*);
2475 
2501 int
2502 NC4_isnetcdf4(struct NC_FILE_INFO* h5)
2503 {
2504  int stat;
2505  int isnc4 = 0;
2506  int exists;
2507  int count;
2508 
2509  /* Look for NC3_STRICT_ATT_NAME */
2510  exists = NC4_strict_att_exists(h5);
2511  if(exists)
2512  goto done;
2513  /* attribute did not exist */
2514  /* => last resort: walk the HDF5 file looking for markers */
2515  count = 0;
2516  stat = NC4_walk(((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid,
2517  &count);
2518  if(stat != NC_NOERR)
2519  isnc4 = 0;
2520  else /* Threshold is at least two matches */
2521  isnc4 = (count >= 2);
2522 
2523 done:
2524  return isnc4;
2525 }
2526 
2535 static int
2536 NC4_strict_att_exists(NC_FILE_INFO_T *h5)
2537 {
2538  hid_t grpid = -1;
2539  htri_t attr_exists;
2540 
2541  /* Get root group ID. */
2542  grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid;
2543 
2544  /* See if the NC3_STRICT_ATT_NAME attribute exists */
2545  if ((attr_exists = H5Aexists(grpid, NC3_STRICT_ATT_NAME)) < 0)
2546  return 1;
2547  return (attr_exists?1:0);
2548 }
2549 
2559 static int
2560 NC4_walk(hid_t gid, int* countp)
2561 {
2562  int ncstat = NC_NOERR;
2563  int j,na;
2564  ssize_t len;
2565  hsize_t nobj;
2566  herr_t err;
2567  int otype;
2568  hid_t grpid, dsid;
2569  char name[NC_HDF5_MAX_NAME];
2570 
2571  /* walk group members of interest */
2572  err = H5Gget_num_objs(gid, &nobj);
2573  if(err < 0) return err;
2574 
2575  for(hsize_t i = 0; i < nobj; i++) {
2576  /* Get name & kind of object in the group */
2577  len = H5Gget_objname_by_idx(gid,i,name,(size_t)NC_HDF5_MAX_NAME);
2578  if(len < 0) return (int)len;
2579 
2580  otype = H5Gget_objtype_by_idx(gid, i);
2581  switch(otype) {
2582  case H5G_GROUP:
2583  grpid = H5Gopen1(gid,name);
2584  NC4_walk(grpid,countp);
2585  H5Gclose(grpid);
2586  break;
2587  case H5G_DATASET: /* variables */
2588  /* Check for phony_dim */
2589  if(strcmp(name,"phony_dim")==0)
2590  *countp = *countp + 1;
2591  dsid = H5Dopen1(gid,name);
2592  na = H5Aget_num_attrs(dsid);
2593  for(j = 0; j < na; j++) {
2594  hid_t aid = H5Aopen_idx(dsid,(unsigned int) j);
2595  if(aid >= 0) {
2596  const NC_reservedatt* ra;
2597  ssize_t len = H5Aget_name(aid, NC_HDF5_MAX_NAME, name);
2598  if(len < 0) return (int)len;
2599  /* Is this a netcdf-4 marker attribute */
2600  ra = NC_findreserved(name);
2601  if(ra != NULL)
2602  *countp = *countp + 1;
2603  }
2604  H5Aclose(aid);
2605  }
2606  H5Dclose(dsid);
2607  break;
2608  default:/* ignore */
2609  break;
2610  }
2611  }
2612  return ncstat;
2613 }
2614 
#define NC_ENOMEM
Memory allocation (malloc) failure.
Definition: netcdf.h:458
#define NC_CHUNKED
In HDF5 files you can set storage for each variable to be either contiguous or chunked, with nc_def_var_chunking().
Definition: netcdf.h:314
#define NC_CHAR
ISO/ASCII character.
Definition: netcdf.h:36
#define NC_CONTIGUOUS
In HDF5 files you can set storage for each variable to be either contiguous or chunked, with nc_def_var_chunking().
Definition: netcdf.h:315
#define NC_UBYTE
unsigned 1 byte int
Definition: netcdf.h:42
#define NC_EDIMSCALE
Problem with HDF5 dimscales.
Definition: netcdf.h:514
#define NC_CLASSIC_MODEL
Enforce classic model on netCDF-4.
Definition: netcdf.h:149
#define NC_MAX_VAR_DIMS
max per variable dimensions
Definition: netcdf.h:292
#define NC_UINT
unsigned 4-byte int
Definition: netcdf.h:44
#define NC_EHDFERR
Error at HDF5 layer.
Definition: netcdf.h:491
#define NC_OPAQUE
opaque types
Definition: netcdf.h:54
Main header file for the C API.
#define NC_INT64
signed 8-byte int
Definition: netcdf.h:45
#define NC_STRING
string
Definition: netcdf.h:47
#define NC_DOUBLE
double precision floating point number
Definition: netcdf.h:41
int nc_type
The nc_type type is just an int.
Definition: netcdf.h:25
#define NC_BYTE
signed 1 byte integer
Definition: netcdf.h:35
#define NC_QUANTIZE_BITROUND_ATT_NAME
When quantization is used for a variable, an attribute of the appropriate name is added...
Definition: netcdf.h:355
#define NC_ENDIAN_LITTLE
In HDF5 files you can set the endianness of variables with nc_def_var_endian().
Definition: netcdf.h:305
#define NC_EATTMETA
Problem with attribute metadata.
Definition: netcdf.h:497
#define NC_VLEN
vlen (variable-length) types
Definition: netcdf.h:53
#define NC_QUANTIZE_GRANULARBR_ATT_NAME
When quantization is used for a variable, an attribute of the appropriate name is added...
Definition: netcdf.h:354
#define NC_EFILEMETA
Problem with file metadata.
Definition: netcdf.h:495
#define NC_EFILTER
Filter operation failed.
Definition: netcdf.h:523
#define NC_EBADTYPE
Not a netcdf data type.
Definition: netcdf.h:420
#define NC_EDIMMETA
Problem with dimension metadata.
Definition: netcdf.h:496
#define NC_EINVAL
Invalid Argument.
Definition: netcdf.h:388
#define NC_INT
signed 4 byte integer
Definition: netcdf.h:38
#define NC_QUANTIZE_BITGROOM
Use BitGroom quantization.
Definition: netcdf.h:346
#define NC_ENDIAN_BIG
In HDF5 files you can set the endianness of variables with nc_def_var_endian().
Definition: netcdf.h:306
#define NC_MAX_NAME
Maximum for classic library.
Definition: netcdf.h:291
#define NC_NAT
Not A Type.
Definition: netcdf.h:34
EXTERNL int nc_free_vlen(nc_vlen_t *vl)
Free memory in a single VLEN object.
Definition: dvlen.c:61
#define NC_USHORT
unsigned 2-byte int
Definition: netcdf.h:43
#define NC_COMPACT
In HDF5 files you can set storage for each variable to be either contiguous or chunked, with nc_def_var_chunking().
Definition: netcdf.h:316
#define NC_EVARMETA
Problem with variable metadata.
Definition: netcdf.h:498
This is the type of arrays of vlens.
Definition: netcdf.h:761
#define NC_SHORT
signed 2 byte integer
Definition: netcdf.h:37
#define NC_ENDIAN_NATIVE
In HDF5 files you can set the endianness of variables with nc_def_var_endian().
Definition: netcdf.h:304
#define NC_ENOTVAR
Variable not found.
Definition: netcdf.h:432
#define NC_QUANTIZE_BITROUND
Use BitRound quantization.
Definition: netcdf.h:348
#define NC_EPERM
Write to read only.
Definition: netcdf.h:389
#define NC_NOERR
No Error.
Definition: netcdf.h:378
#define NC_QUANTIZE_GRANULARBR
Use Granular BitRound quantization.
Definition: netcdf.h:347
#define NC_ENUM
enum types
Definition: netcdf.h:55
#define NC_COMPOUND
compound types
Definition: netcdf.h:56
#define NC_GLOBAL
Attribute id to put/get a global attribute.
Definition: netcdf.h:264
#define NC_FLOAT
single precision floating point number
Definition: netcdf.h:40
#define NC_QUANTIZE_BITGROOM_ATT_NAME
When quantization is used for a variable, an attribute of the appropriate name is added...
Definition: netcdf.h:353
#define NC_UINT64
unsigned 8-byte int
Definition: netcdf.h:46