NetCDF 4.9.3
Loading...
Searching...
No Matches
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. */
16
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
33
42static int
43flag_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
76int
77rec_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
141int
142rec_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
201int
202nc4_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
248int
249nc4_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
406exit:
407 if (typeid > 0 && H5Tclose(typeid) < 0)
408 BAIL2(NC_EHDFERR);
409 return retval;
410}
411
426static int
427put_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
579exit:
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
606static int
607write_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
641static int
642write_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
668exit:
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
686static int
687write_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
729exit:
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
747static int
748write_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
777exit:
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
801static int
802var_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
1076exit:
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
1111int
1112nc4_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
1167static int
1168commit_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
1308static int
1309write_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
1333exit:
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
1353static int
1354create_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
1392exit:
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
1413static int
1414attach_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
1478static int
1479var_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
1519static int
1520remove_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
1568static int
1569write_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
1762int
1763nc4_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
1834exit:
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
1854static int
1855write_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
1914exit:
1915
1916 return retval;
1917}
1918
1931int
1932nc4_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
2018int
2019nc4_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
2074int
2075nc4_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
2290void
2291reportobject(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
2336static void
2337reportopenobjectsT(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
2372void
2373reportopenobjects(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
2388void
2389showopenobjects5(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
2410void
2411showopenobjects(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
2434int
2435NC4_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
2452int
2453NC4_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;
2468done:
2469 if(plist >= 0) H5Pclose(plist);
2470 return stat;
2471}
2472
2473static int NC4_strict_att_exists(NC_FILE_INFO_T*);
2474static int NC4_walk(hid_t, int*);
2475
2501int
2502NC4_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
2523done:
2524 return isnc4;
2525}
2526
2535static int
2536NC4_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
2559static int
2560NC4_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
EXTERNL int nc_free_vlen(nc_vlen_t *vl)
Free memory in a single VLEN object.
Definition dvlen.c:61
Main header file for the C API.
#define NC_EBADTYPE
Not a netcdf data type.
Definition netcdf.h:420
#define NC_UINT
unsigned 4-byte int
Definition netcdf.h:44
#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_EFILTER
Filter operation failed.
Definition netcdf.h:523
#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_INT
signed 4 byte integer
Definition netcdf.h:38
#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_VAR_DIMS
max per variable dimensions
Definition netcdf.h:292
#define NC_BYTE
signed 1 byte integer
Definition netcdf.h:35
#define NC_EPERM
Write to read only.
Definition netcdf.h:389
#define NC_QUANTIZE_BITROUND
Use BitRound quantization.
Definition netcdf.h:348
#define NC_EDIMSCALE
Problem with HDF5 dimscales.
Definition netcdf.h:514
#define NC_VLEN
vlen (variable-length) types
Definition netcdf.h:53
#define NC_NAT
Not A Type.
Definition netcdf.h:34
#define NC_DOUBLE
double precision floating point number
Definition netcdf.h:41
#define NC_UBYTE
unsigned 1 byte int
Definition netcdf.h:42
#define NC_FLOAT
single precision floating point number
Definition netcdf.h:40
#define NC_ENOMEM
Memory allocation (malloc) failure.
Definition netcdf.h:458
#define NC_COMPOUND
compound types
Definition netcdf.h:56
#define NC_CHUNKED
In HDF5 files you can set storage for each variable to be either contiguous or chunked,...
Definition netcdf.h:314
#define NC_SHORT
signed 2 byte integer
Definition netcdf.h:37
#define NC_QUANTIZE_GRANULARBR
Use Granular BitRound quantization.
Definition netcdf.h:347
#define NC_QUANTIZE_BITGROOM
Use BitGroom quantization.
Definition netcdf.h:346
#define NC_ENUM
enum types
Definition netcdf.h:55
#define NC_INT64
signed 8-byte int
Definition netcdf.h:45
#define NC_EATTMETA
Problem with attribute metadata.
Definition netcdf.h:497
#define NC_EHDFERR
Error at HDF5 layer.
Definition netcdf.h:491
#define NC_CONTIGUOUS
In HDF5 files you can set storage for each variable to be either contiguous or chunked,...
Definition netcdf.h:315
#define NC_GLOBAL
Attribute id to put/get a global attribute.
Definition netcdf.h:264
#define NC_UINT64
unsigned 8-byte int
Definition netcdf.h:46
#define NC_COMPACT
In HDF5 files you can set storage for each variable to be either contiguous or chunked,...
Definition netcdf.h:316
#define NC_EFILEMETA
Problem with file metadata.
Definition netcdf.h:495
#define NC_ENOTVAR
Variable not found.
Definition netcdf.h:432
#define NC_EINVAL
Invalid Argument.
Definition netcdf.h:388
#define NC_CLASSIC_MODEL
Enforce classic model on netCDF-4.
Definition netcdf.h:149
#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_MAX_NAME
Maximum for classic library.
Definition netcdf.h:291
#define NC_NOERR
No Error.
Definition netcdf.h:378
#define NC_EDIMMETA
Problem with dimension metadata.
Definition netcdf.h:496
#define NC_USHORT
unsigned 2-byte int
Definition netcdf.h:43
#define NC_OPAQUE
opaque types
Definition netcdf.h:54
#define NC_STRING
string
Definition netcdf.h:47
#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_CHAR
ISO/ASCII character.
Definition netcdf.h:36
#define NC_EVARMETA
Problem with variable metadata.
Definition netcdf.h:498
#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
int nc_type
The nc_type type is just an int.
Definition netcdf.h:25
This is the type of arrays of vlens.
Definition netcdf.h:761