NetCDF 4.10.1
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 exist already? */
515 if ((attr_exists = H5Aexists(locid, att->hdr.name)) < 0)
516 BAIL(NC_EHDFERR);
517 if (attr_exists)
518 {
519 /* Open the existing attribute. */
520 if ((existing_attid = H5Aopen(locid, att->hdr.name, H5P_DEFAULT)) < 0)
521 BAIL(NC_EATTMETA);
522
523 /* Get the HDF5 datatype of the existing attribute. */
524 if ((existing_att_typeid = H5Aget_type(existing_attid)) < 0)
525 BAIL(NC_EATTMETA);
526
527 /* Get the HDF5 dataspace of the existing attribute. */
528 if ((existing_spaceid = H5Aget_space(existing_attid)) < 0)
529 BAIL(NC_EATTMETA);
530
531 /* Is new attribute the same datatype and size as previous?
532 * For text attributes the size is embedded in the datatype, not the dataspace.
533 * H5Sextent_equal will also do the right thing with NULL dataspace cases. */
534 if (!H5Tequal(file_typeid, existing_att_typeid) ||
535 (!H5Sextent_equal(spaceid, existing_spaceid)))
536 {
537 /* The attribute exists but we cannot reuse it. */
538
539 /* Delete the attribute. */
540 if (H5Adelete(locid, att->hdr.name) < 0)
541 BAIL(NC_EHDFERR);
542
543 /* Re-create the attribute with the type and length
544 reflecting the new value (or values). */
545 if ((attid = H5Acreate1(locid, att->hdr.name, file_typeid, spaceid,
546 H5P_DEFAULT)) < 0)
547 BAIL(NC_EATTMETA);
548
549 /* Write the values, (even if length is zero). */
550 if (H5Awrite(attid, file_typeid, data) < 0)
551 BAIL(NC_EATTMETA);
552 }
553 else
554 {
555 /* The attribute exists and we can reuse it. */
556
557 /* Write the values, reusing the existing attribute. */
558 if (H5Awrite(existing_attid, file_typeid, data) < 0)
559 BAIL(NC_EATTMETA);
560 }
561 }
562 else
563 {
564 /* The attribute does not exist yet. */
565
566 /* Create the attribute. */
567 if ((attid = H5Acreate1(locid, att->hdr.name, file_typeid, spaceid,
568 H5P_DEFAULT)) < 0)
569 BAIL(NC_EATTMETA);
570
571 /* Write the values, (even if length is zero). */
572 if (H5Awrite(attid, file_typeid, data) < 0)
573 BAIL(NC_EATTMETA);
574 }
575
576exit:
577 if (file_typeid && H5Tclose(file_typeid))
578 BAIL2(NC_EHDFERR);
579 if (attid > 0 && H5Aclose(attid) < 0)
580 BAIL2(NC_EHDFERR);
581 if (existing_att_typeid && H5Tclose(existing_att_typeid))
582 BAIL2(NC_EHDFERR);
583 if (existing_attid > 0 && H5Aclose(existing_attid) < 0)
584 BAIL2(NC_EHDFERR);
585 if (spaceid > 0 && H5Sclose(spaceid) < 0)
586 BAIL2(NC_EHDFERR);
587 if (existing_spaceid > 0 && H5Sclose(existing_spaceid) < 0)
588 BAIL2(NC_EHDFERR);
589 return retval;
590}
591
603static int
604write_attlist(NCindex *attlist, int varid, NC_GRP_INFO_T *grp)
605{
606 NC_ATT_INFO_T *att;
607 int retval;
608
609 for(size_t i = 0; i < ncindexsize(attlist); i++)
610 {
611 att = (NC_ATT_INFO_T *)ncindexith(attlist, i);
612 assert(att);
613 if (att->dirty)
614 {
615 LOG((4, "%s: writing att %s to varid %d", __func__, att->hdr.name, varid));
616 if ((retval = put_att_grpa(grp, varid, att)))
617 return retval;
618 att->dirty = NC_FALSE;
619 att->created = NC_TRUE;
620 }
621 }
622 return NC_NOERR;
623}
624
638static int
639write_coord_dimids(NC_VAR_INFO_T *var)
640{
641 NC_HDF5_VAR_INFO_T *hdf5_var;
642 hsize_t coords_len[1];
643 hid_t c_spaceid = -1, c_attid = -1;
644 int retval = NC_NOERR;
645
646 assert(var && var->format_var_info);
647
648 /* Get HDF5-specific var info. */
649 hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
650
651 /* Set up space for attribute. */
652 coords_len[0] = var->ndims;
653 if ((c_spaceid = H5Screate_simple(1, coords_len, coords_len)) < 0)
654 BAIL(NC_EHDFERR);
655
656 /* Create the attribute. */
657 if ((c_attid = H5Acreate1(hdf5_var->hdf_datasetid, COORDINATES,
658 H5T_NATIVE_INT, c_spaceid, H5P_DEFAULT)) < 0)
659 BAIL(NC_EHDFERR);
660
661 /* Write our attribute. */
662 if (H5Awrite(c_attid, H5T_NATIVE_INT, var->dimids) < 0)
663 BAIL(NC_EHDFERR);
664
665exit:
666 if (c_spaceid >= 0 && H5Sclose(c_spaceid) < 0)
667 BAIL2(NC_EHDFERR);
668 if (c_attid >= 0 && H5Aclose(c_attid) < 0)
669 BAIL2(NC_EHDFERR);
670 return retval;
671}
672
683static int
684write_quantize_att(NC_VAR_INFO_T *var)
685{
686 NC_HDF5_VAR_INFO_T *hdf5_var;
687 hsize_t len = 1;
688 hid_t c_spaceid = -1, c_attid = -1;
689 char att_name[NC_MAX_NAME + 1];
690 int retval = NC_NOERR;
691
692 assert(var && var->format_var_info);
693
694 /* Get HDF5-specific var info. */
695 hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
696
697 /* Different quantize algorithms get different attribute names. */
698 switch (var->quantize_mode)
699 {
701 snprintf(att_name, sizeof(att_name), "%s", NC_QUANTIZE_BITGROOM_ATT_NAME);
702 break;
704 snprintf(att_name, sizeof(att_name), "%s", NC_QUANTIZE_GRANULARBR_ATT_NAME);
705 break;
707 snprintf(att_name, sizeof(att_name), "%s", NC_QUANTIZE_BITROUND_ATT_NAME);
708 break;
709 default:
710 return NC_EINVAL;
711 }
712
713 /* Set up space for attribute. */
714 if ((c_spaceid = H5Screate_simple(1, &len, &len)) < 0)
715 BAIL(NC_EHDFERR);
716
717 /* Create the attribute. */
718 if ((c_attid = H5Acreate1(hdf5_var->hdf_datasetid, att_name,
719 H5T_NATIVE_INT, c_spaceid, H5P_DEFAULT)) < 0)
720 BAIL(NC_EHDFERR);
721
722 /* Write our attribute. */
723 if (H5Awrite(c_attid, H5T_NATIVE_INT, &var->nsd) < 0)
724 BAIL(NC_EHDFERR);
725
726exit:
727 if (c_spaceid >= 0 && H5Sclose(c_spaceid) < 0)
728 BAIL2(NC_EHDFERR);
729 if (c_attid >= 0 && H5Aclose(c_attid) < 0)
730 BAIL2(NC_EHDFERR);
731 return retval;
732}
733
744static int
745write_netcdf4_dimid(hid_t datasetid, int dimid)
746{
747 hid_t dimid_spaceid = -1, dimid_attid = -1;
748 htri_t attr_exists;
749 int retval = NC_NOERR;
750
751 /* Create the space. */
752 if ((dimid_spaceid = H5Screate(H5S_SCALAR)) < 0)
753 BAIL(NC_EHDFERR);
754
755 /* Does the attribute already exist? If so, don't try to create it. */
756 if ((attr_exists = H5Aexists(datasetid, NC_DIMID_ATT_NAME)) < 0)
757 BAIL(NC_EHDFERR);
758 if (attr_exists)
759 dimid_attid = H5Aopen_by_name(datasetid, ".", NC_DIMID_ATT_NAME,
760 H5P_DEFAULT, H5P_DEFAULT);
761 else
762 /* Create the attribute if needed. */
763 dimid_attid = H5Acreate1(datasetid, NC_DIMID_ATT_NAME,
764 H5T_NATIVE_INT, dimid_spaceid, H5P_DEFAULT);
765 if (dimid_attid < 0)
766 BAIL(NC_EHDFERR);
767
768
769 /* Write it. */
770 LOG((4, "%s: writing secret dimid %d", __func__, dimid));
771 if (H5Awrite(dimid_attid, H5T_NATIVE_INT, &dimid) < 0)
772 BAIL(NC_EHDFERR);
773
774exit:
775 /* Close stuff*/
776 if (dimid_spaceid >= 0 && H5Sclose(dimid_spaceid) < 0)
777 BAIL2(NC_EHDFERR);
778 if (dimid_attid >= 0 && H5Aclose(dimid_attid) < 0)
779 BAIL2(NC_EHDFERR);
780
781 return retval;
782}
783
798static int
799var_create_dataset(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, nc_bool_t write_dimid)
800{
801 NC_HDF5_GRP_INFO_T *hdf5_grp;
802 NC_HDF5_VAR_INFO_T *hdf5_var;
803 hid_t plistid = 0, access_plistid = 0, typeid = 0, spaceid = 0;
804 hsize_t chunksize[H5S_MAX_RANK], dimsize[H5S_MAX_RANK], maxdimsize[H5S_MAX_RANK];
805 int d;
806 void *fillp = NULL;
807 NC_DIM_INFO_T *dim = NULL;
808 char *name_to_use;
809 int retval;
810 unsigned int* params = NULL;
811
812 assert(grp && grp->format_grp_info && var && var->format_var_info);
813
814 LOG((3, "%s:: name %s", __func__, var->hdr.name));
815
816 /* Get HDF5-specific group and var info. */
817 hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
818 hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
819
820 /* Scalar or not, we need a creation property list. */
821 if ((plistid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
822 BAIL(NC_EHDFERR);
823 if ((access_plistid = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
824 BAIL(NC_EHDFERR);
825
826 /* Turn off object tracking times in HDF5. */
827 if (H5Pset_obj_track_times(plistid, 0) < 0)
828 BAIL(NC_EHDFERR);
829
830 /* Find the HDF5 type of the dataset. */
831 if ((retval = nc4_get_hdf_typeid(grp->nc4_info, var->type_info->hdr.id, &typeid,
832 var->type_info->endianness)))
833 BAIL(retval);
834
835 /* Figure out what fill value to set, if any. */
836 if (var->no_fill)
837 {
838 /* Required to truly turn HDF5 fill values off */
839 if (H5Pset_fill_time(plistid, H5D_FILL_TIME_NEVER) < 0)
840 BAIL(NC_EHDFERR);
841 }
842 else
843 {
844 if ((retval = nc4_get_fill_value(grp->nc4_info, var, &fillp)))
845 BAIL(retval);
846
847 /* If there is a fill value, set it. */
848 if (fillp)
849 {
850 if (var->type_info->nc_type_class == NC_STRING)
851 {
852 if (H5Pset_fill_value(plistid, typeid, fillp) < 0)
853 BAIL(NC_EHDFERR);
854 }
855 else
856 {
857 /* The fill value set in HDF5 must always be presented as
858 * a native type, even if the endianness for this dataset
859 * is non-native. HDF5 will translate the fill value to
860 * the target endiannesss. */
861 hid_t fill_typeid = 0;
862
863 if ((retval = nc4_get_hdf_typeid(grp->nc4_info, var->type_info->hdr.id, &fill_typeid,
865 BAIL(retval);
866 if (H5Pset_fill_value(plistid, fill_typeid, fillp) < 0)
867 {
868 if (H5Tclose(fill_typeid) < 0)
869 BAIL(NC_EHDFERR);
870 BAIL(NC_EHDFERR);
871 }
872 if (H5Tclose(fill_typeid) < 0)
873 BAIL(NC_EHDFERR);
874 }
875 }
876 }
877
878 /* If the user wants to compress the data, using either zlib
879 * (a.k.a deflate) or szip, or another filter, set that up now.
880 * Szip and zip can be turned on
881 * either directly with nc_def_var_szip/deflate(), or using
882 * nc_def_var_filter(). If the user
883 * has specified a filter, it will be applied here. */
884 if(var->filters != NULL) {
885 size_t j;
886 NClist* filters = (NClist*)var->filters;
887 for(j=0;j<nclistlength(filters);j++) {
888 struct NC_HDF5_Filter* fi = (struct NC_HDF5_Filter*)nclistget(filters,j);
889 if(fi->filterid == H5Z_FILTER_FLETCHER32) {
890 if(H5Pset_fletcher32(plistid) < 0)
891 BAIL(NC_EHDFERR);
892 } else if(fi->filterid == H5Z_FILTER_SHUFFLE) {
893 if(H5Pset_shuffle(plistid) < 0)
894 BAIL(NC_EHDFERR);
895 } else if(fi->filterid == H5Z_FILTER_DEFLATE) {/* Handle zip case here */
896 if(fi->nparams != 1)
897 BAIL(NC_EFILTER);
898 unsigned int level = fi->params[0];
899 if(H5Pset_deflate(plistid, level) < 0)
900 BAIL(NC_EFILTER);
901 } else if(fi->filterid == H5Z_FILTER_SZIP) {/* Handle szip case here */
902 if(fi->nparams != 2)
903 BAIL(NC_EFILTER);
904 unsigned int options_mask = fi->params[0];
905 unsigned int bits_per_pixel = fi->params[1];
906 if(H5Pset_szip(plistid, options_mask, bits_per_pixel) < 0)
907 BAIL(NC_EFILTER);
908 } else {
909 herr_t code = H5Pset_filter(plistid, fi->filterid,
910 H5Z_FLAG_OPTIONAL, /* always make optional so filters on vlens are ignored */
911 fi->nparams, fi->params);
912 if(code < 0)
913 BAIL(NC_EFILTER);
914 }
915 }
916 }
917
918 /* If ndims non-zero, get info for all dimensions. We look up the
919 dimids and get the len of each dimension. We need this to create
920 the space for the dataset. In netCDF a dimension length of zero
921 means an unlimited dimension. */
922 if (var->ndims)
923 {
924 int unlimdim = 0;
925
926 /* Check to see if any unlimited dimensions are used in this var. */
927 for (d = 0; d < var->ndims; d++) {
928 dim = var->dim[d];
929 assert(dim && dim->hdr.id == var->dimids[d]);
930 if (dim->unlimited)
931 unlimdim++;
932 }
933
934 /* If there are no unlimited dims, and no filters, and the user
935 * has not specified chunksizes, use contiguous variable for
936 * better performance. */
937 if (nclistlength((NClist*)var->filters) == 0 &&
938 (var->chunksizes == NULL || !var->chunksizes[0]) && !unlimdim)
939 var->storage = NC_CONTIGUOUS;
940
941 /* Gather current & maximum dimension sizes, along with chunk
942 * sizes. */
943 for (d = 0; d < var->ndims; d++)
944 {
945 dim = var->dim[d];
946 assert(dim && dim->hdr.id == var->dimids[d]);
947 dimsize[d] = dim->unlimited ? NC_HDF5_UNLIMITED_DIMSIZE : dim->len;
948 maxdimsize[d] = dim->unlimited ? H5S_UNLIMITED : (hsize_t)dim->len;
949 if (var->storage == NC_CHUNKED)
950 {
951 if (var->chunksizes[d])
952 chunksize[d] = var->chunksizes[d];
953 else
954 {
955 size_t type_size;
956 if (var->type_info->nc_type_class == NC_STRING)
957 type_size = sizeof(char *);
958 else
959 type_size = var->type_info->size;
960
961 /* Unlimited dim always gets chunksize of 1. */
962 if (dim->unlimited)
963 chunksize[d] = 1;
964 else
965 chunksize[d] = (hsize_t)pow(DEFAULT_CHUNK_SIZE/(double)type_size,
966 1/(double)((int)var->ndims - unlimdim));
967
968 /* If the chunksize is greater than the dim
969 * length, make it the dim length. */
970 if (!dim->unlimited && chunksize[d] > dim->len)
971 chunksize[d] = dim->len;
972
973 /* Remember the computed chunksize */
974 var->chunksizes[d] = chunksize[d];
975 }
976 }
977 }
978
979 /* Create the dataspace. */
980 if ((spaceid = H5Screate_simple((int)var->ndims, dimsize, maxdimsize)) < 0)
981 BAIL(NC_EHDFERR);
982 }
983 else
984 {
985 if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
986 BAIL(NC_EHDFERR);
987 }
988
989 /* Set the var storage to contiguous, compact, or chunked. Don't
990 * try to set chunking for scalar vars, they will default to
991 * contiguous if not set to compact. */
992 if (var->storage == NC_CONTIGUOUS)
993 {
994 if (H5Pset_layout(plistid, H5D_CONTIGUOUS) < 0)
995 BAIL(NC_EHDFERR);
996 }
997 else if (var->storage == NC_COMPACT)
998 {
999 if (H5Pset_layout(plistid, H5D_COMPACT) < 0)
1000 BAIL(NC_EHDFERR);
1001 }
1002 else if (var->ndims)
1003 {
1004 if (H5Pset_chunk(plistid, (int)var->ndims, chunksize) < 0)
1005 BAIL(NC_EHDFERR);
1006 }
1007
1008 /* Turn on creation order tracking. */
1009 if (!grp->nc4_info->no_attr_create_order) {
1010 if (H5Pset_attr_creation_order(plistid, H5P_CRT_ORDER_TRACKED|
1011 H5P_CRT_ORDER_INDEXED) < 0)
1012 BAIL(NC_EHDFERR);
1013 }
1014
1015 /* Set per-var chunk cache, for chunked datasets. */
1016 if (var->storage == NC_CHUNKED && var->chunkcache.size)
1017 if (H5Pset_chunk_cache(access_plistid, var->chunkcache.nelems,
1018 var->chunkcache.size, var->chunkcache.preemption) < 0)
1019 BAIL(NC_EHDFERR);
1020
1021 /* At long last, create the dataset. */
1022 name_to_use = var->alt_name ? var->alt_name : var->hdr.name;
1023 LOG((4, "%s: about to H5Dcreate2 dataset %s of type 0x%x", __func__,
1024 name_to_use, typeid));
1025 if ((hdf5_var->hdf_datasetid = H5Dcreate2(hdf5_grp->hdf_grpid, name_to_use, typeid,
1026 spaceid, H5P_DEFAULT, plistid, access_plistid)) < 0)
1027 BAIL(NC_EHDFERR);
1028 var->created = NC_TRUE;
1029 var->is_new_var = NC_FALSE;
1030
1031 /* Always write the hidden coordinates attribute, which lists the
1032 * dimids of this var. When present, this speeds opens. When not
1033 * present, dimscale matching is used. */
1034 if (var->ndims)
1035 if ((retval = write_coord_dimids(var)))
1036 BAIL(retval);
1037
1038 /* If this is a dimscale, mark it as such in the HDF5 file. Also
1039 * find the dimension info and store the dataset id of the dimscale
1040 * dataset. */
1041 if (hdf5_var->dimscale)
1042 {
1043 if (H5DSset_scale(hdf5_var->hdf_datasetid, var->hdr.name) < 0)
1044 BAIL(NC_EHDFERR);
1045
1046 /* If this is a multidimensional coordinate variable, write a
1047 * coordinates attribute. */
1048 /* if (var->ndims > 1) */
1049 /* if ((retval = write_coord_dimids(var))) */
1050 /* BAIL(retval); */
1051
1052 /* If desired, write the netCDF dimid. */
1053 if (write_dimid)
1054 if ((retval = write_netcdf4_dimid(hdf5_var->hdf_datasetid, var->dimids[0])))
1055 BAIL(retval);
1056 }
1057
1058 /* If quantization is in use, write an attribute indicating it, a
1059 * single integer which is the number of significant digits
1060 * (NSD, for BitGroom and Granular BitRound) or number of significant bits
1061 * (NSB, for BitRound). */
1062 if (var->quantize_mode)
1063 if ((retval = write_quantize_att(var)))
1064 BAIL(retval);
1065
1066 /* Write attributes for this var. */
1067 if ((retval = write_attlist(var->att, var->hdr.id, grp)))
1068 BAIL(retval);
1069
1070 /* The file is now up-to-date with all settings for this var. */
1071 var->attr_dirty = NC_FALSE;
1072
1073exit:
1074 nullfree(params);
1075 if (typeid > 0 && H5Tclose(typeid) < 0)
1076 BAIL2(NC_EHDFERR);
1077 if (plistid > 0 && H5Pclose(plistid) < 0)
1078 BAIL2(NC_EHDFERR);
1079 if (access_plistid > 0 && H5Pclose(access_plistid) < 0)
1080 BAIL2(NC_EHDFERR);
1081 if (spaceid > 0 && H5Sclose(spaceid) < 0)
1082 BAIL2(NC_EHDFERR);
1083 if (fillp)
1084 {
1085 if (var->type_info->nc_type_class == NC_VLEN)
1086 nc_free_vlen((nc_vlen_t *)fillp);
1087 else if (var->type_info->nc_type_class == NC_STRING && *(char **)fillp)
1088 free(*(char **)fillp);
1089 free(fillp);
1090 }
1091
1092 return retval;
1093}
1094
1108int
1109nc4_adjust_var_cache(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
1110{
1111 size_t chunk_size_bytes = 1;
1112 int d;
1113 int retval;
1114
1115 /* Nothing to be done for contiguous or compact data. */
1116 if (var->storage != NC_CHUNKED)
1117 return NC_NOERR;
1118
1119#ifdef USE_PARALLEL4
1120 /* Don't set cache for files using parallel I/O. */
1121 if (grp->nc4_info->parallel)
1122 return NC_NOERR;
1123#endif
1124
1125 /* How many bytes in the chunk? */
1126 for (d = 0; d < var->ndims; d++)
1127 chunk_size_bytes *= var->chunksizes[d];
1128 if (var->type_info->size)
1129 chunk_size_bytes *= var->type_info->size;
1130 else
1131 chunk_size_bytes *= sizeof(char *);
1132
1133 /* If the chunk cache is too small, and the user has not changed
1134 * the default value of the chunk cache size, then increase the
1135 * size of the cache to hold DEFAULT_CHUNKS_IN_CACHE chunks.
1136 *
1137 * The original code checked (chunk_size_bytes > CHUNK_CACHE_SIZE)
1138 * which only triggered when a single chunk exceeded the cache.
1139 * This missed the case where the cache was too small to hold
1140 * enough chunks for efficient access patterns, causing the
1141 * 4.7.3->4.7.4 regression (see GitHub issue #1757). We now
1142 * check whether the cache can hold DEFAULT_CHUNKS_IN_CACHE
1143 * chunks. */
1144 if (var->chunkcache.size == CHUNK_CACHE_SIZE)
1145 {
1146 size_t min_cache = 0;
1147 if (chunk_size_bytes > (DEFAULT_CHUNK_CACHE_SIZE / DEFAULT_CHUNKS_IN_CACHE))
1148 min_cache = DEFAULT_CHUNK_CACHE_SIZE;
1149 else
1150 min_cache = chunk_size_bytes * DEFAULT_CHUNKS_IN_CACHE;
1151 if (var->chunkcache.size < min_cache)
1152 {
1153 var->chunkcache.size = min_cache;
1154 if ((retval = nc4_reopen_dataset(grp, var)))
1155 return retval;
1156 }
1157 }
1158
1159 return NC_NOERR;
1160}
1161
1177static int
1178commit_type(NC_GRP_INFO_T *grp, NC_TYPE_INFO_T *type)
1179{
1180 NC_HDF5_GRP_INFO_T *hdf5_grp;
1181 NC_HDF5_TYPE_INFO_T *hdf5_type;
1182 hid_t base_hdf_typeid;
1183 int retval;
1184
1185 assert(grp && grp->format_grp_info && type && type->format_type_info);
1186
1187 /* Get HDF5-specific group and type info. */
1188 hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
1189 hdf5_type = (NC_HDF5_TYPE_INFO_T *)type->format_type_info;
1190
1191 /* Did we already record this type? */
1192 if (type->committed)
1193 return NC_NOERR;
1194
1195 /* Is this a compound type? */
1196 if (type->nc_type_class == NC_COMPOUND)
1197 {
1198 NC_FIELD_INFO_T *field;
1199 hid_t hdf_base_typeid, hdf_typeid;
1200 size_t i;
1201
1202 if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_COMPOUND, type->size)) < 0)
1203 return NC_EHDFERR;
1204 LOG((4, "creating compound type %s hdf_typeid 0x%x", type->hdr.name,
1205 hdf5_type->hdf_typeid));
1206
1207 for(i=0;i<nclistlength(type->u.c.field);i++)
1208 {
1209 field = (NC_FIELD_INFO_T *)nclistget(type->u.c.field, i);
1210 assert(field);
1211 if ((retval = nc4_get_hdf_typeid(grp->nc4_info, field->nc_typeid,
1212 &hdf_base_typeid, type->endianness)))
1213 return retval;
1214
1215 /* If this is an array, create a special array type. */
1216 if (field->ndims)
1217 {
1218 int d;
1219 hsize_t dims[NC_MAX_VAR_DIMS];
1220
1221 for (d = 0; d < field->ndims; d++)
1222 dims[d] = (hsize_t)field->dim_size[d];
1223 if ((hdf_typeid = H5Tarray_create1(hdf_base_typeid, field->ndims,
1224 dims, NULL)) < 0)
1225 {
1226 if (H5Tclose(hdf_base_typeid) < 0)
1227 return NC_EHDFERR;
1228 return NC_EHDFERR;
1229 }
1230 if (H5Tclose(hdf_base_typeid) < 0)
1231 return NC_EHDFERR;
1232 }
1233 else
1234 hdf_typeid = hdf_base_typeid;
1235 LOG((4, "inserting field %s offset %d hdf_typeid 0x%x", field->hdr.name,
1236 field->offset, hdf_typeid));
1237 if (H5Tinsert(hdf5_type->hdf_typeid, field->hdr.name, field->offset,
1238 hdf_typeid) < 0)
1239 return NC_EHDFERR;
1240 if (H5Tclose(hdf_typeid) < 0)
1241 return NC_EHDFERR;
1242 }
1243 }
1244 else if (type->nc_type_class == NC_VLEN)
1245 {
1246 /* Find the HDF typeid of the base type of this vlen. */
1247 if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.v.base_nc_typeid,
1248 &base_hdf_typeid, type->endianness)))
1249 return retval;
1250
1251 /* Create a vlen type. */
1252 if ((hdf5_type->hdf_typeid = H5Tvlen_create(base_hdf_typeid)) < 0)
1253 return NC_EHDFERR;
1254 }
1255 else if (type->nc_type_class == NC_OPAQUE)
1256 {
1257 /* Create the opaque type. */
1258 if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_OPAQUE, type->size)) < 0)
1259 return NC_EHDFERR;
1260 }
1261 else if (type->nc_type_class == NC_ENUM)
1262 {
1263 NC_ENUM_MEMBER_INFO_T *enum_m;
1264 size_t i;
1265
1266 if (nclistlength(type->u.e.enum_member) == 0)
1267 return NC_EINVAL;
1268
1269 /* Find the HDF typeid of the base type of this enum. */
1270 if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.e.base_nc_typeid,
1271 &base_hdf_typeid, type->endianness)))
1272 return retval;
1273
1274 /* Create an enum type. */
1275 if ((hdf5_type->hdf_typeid = H5Tenum_create(base_hdf_typeid)) < 0)
1276 return NC_EHDFERR;
1277
1278 /* Add all the members to the HDF5 type. */
1279 for(i=0;i<nclistlength(type->u.e.enum_member);i++) {
1280 enum_m = (NC_ENUM_MEMBER_INFO_T*)nclistget(type->u.e.enum_member,i);
1281 if (H5Tenum_insert(hdf5_type->hdf_typeid, enum_m->name, enum_m->value) < 0)
1282 return NC_EHDFERR;
1283 }
1284 }
1285 else
1286 {
1287 LOG((0, "Unknown class: %d", type->nc_type_class));
1288 return NC_EBADTYPE;
1289 }
1290
1291 /* Commit the type. */
1292 if (H5Tcommit1(hdf5_grp->hdf_grpid, type->hdr.name, hdf5_type->hdf_typeid) < 0)
1293 return NC_EHDFERR;
1294 type->committed = NC_TRUE;
1295 LOG((4, "just committed type %s, HDF typeid: 0x%x", type->hdr.name,
1296 hdf5_type->hdf_typeid));
1297
1298 /* Later we will always use the native typeid. In this case, it is
1299 * a copy of the same type pointed to by hdf_typeid, but it's
1300 * easier to maintain a copy. */
1301 if ((hdf5_type->native_hdf_typeid = H5Tget_native_type(hdf5_type->hdf_typeid,
1302 H5T_DIR_DEFAULT)) < 0)
1303 return NC_EHDFERR;
1304
1305 return NC_NOERR;
1306}
1307
1318static int
1319write_nc3_strict_att(hid_t hdf_grpid)
1320{
1321 hid_t attid = 0, spaceid = 0;
1322 int one = 1;
1323 int retval = NC_NOERR;
1324 htri_t attr_exists;
1325
1326 /* If the attribute already exists, call that a success and return
1327 * NC_NOERR. */
1328 if ((attr_exists = H5Aexists(hdf_grpid, NC3_STRICT_ATT_NAME)) < 0)
1329 return NC_EHDFERR;
1330 if (attr_exists)
1331 return NC_NOERR;
1332
1333 /* Create the attribute to mark this as a file that needs to obey
1334 * strict netcdf-3 rules. */
1335 if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
1336 BAIL(NC_EFILEMETA);
1337 if ((attid = H5Acreate1(hdf_grpid, NC3_STRICT_ATT_NAME,
1338 H5T_NATIVE_INT, spaceid, H5P_DEFAULT)) < 0)
1339 BAIL(NC_EFILEMETA);
1340 if (H5Awrite(attid, H5T_NATIVE_INT, &one) < 0)
1341 BAIL(NC_EFILEMETA);
1342
1343exit:
1344 if (spaceid > 0 && (H5Sclose(spaceid) < 0))
1345 BAIL2(NC_EFILEMETA);
1346 if (attid > 0 && (H5Aclose(attid) < 0))
1347 BAIL2(NC_EFILEMETA);
1348 return retval;
1349}
1350
1363static int
1364create_group(NC_GRP_INFO_T *grp)
1365{
1366 NC_HDF5_GRP_INFO_T *hdf5_grp, *parent_hdf5_grp;
1367 hid_t gcpl_id = -1;
1368 int retval = NC_NOERR;;
1369
1370 assert(grp && grp->format_grp_info && grp->parent &&
1371 grp->parent->format_grp_info);
1372
1373 /* Get HDF5 specific group info for group and parent. */
1374 hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
1375 parent_hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->parent->format_grp_info;
1376 assert(parent_hdf5_grp->hdf_grpid);
1377
1378 /* Create group, with link_creation_order set in the group
1379 * creation property list. */
1380 if ((gcpl_id = H5Pcreate(H5P_GROUP_CREATE)) < 0)
1381 BAIL(NC_EHDFERR);
1382
1383 /* Set track_times to be FALSE. */
1384 if (H5Pset_obj_track_times(gcpl_id, 0) < 0)
1385 BAIL(NC_EHDFERR);
1386
1387 /* Tell HDF5 to keep track of objects in creation order. */
1388 if (H5Pset_link_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0)
1389 BAIL(NC_EHDFERR);
1390
1391 /* Tell HDF5 to keep track of attributes in creation order. */
1392 if (!grp->nc4_info->no_attr_create_order) {
1393 if (H5Pset_attr_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0)
1394 BAIL(NC_EHDFERR);
1395 }
1396
1397 /* Create the group. */
1398 if ((hdf5_grp->hdf_grpid = H5Gcreate2(parent_hdf5_grp->hdf_grpid, grp->hdr.name,
1399 H5P_DEFAULT, gcpl_id, H5P_DEFAULT)) < 0)
1400 BAIL(NC_EHDFERR);
1401
1402exit:
1403 if (gcpl_id > -1 && H5Pclose(gcpl_id) < 0)
1404 BAIL2(NC_EHDFERR);
1405 if (retval)
1406 if (hdf5_grp->hdf_grpid > 0 && H5Gclose(hdf5_grp->hdf_grpid) < 0)
1407 BAIL2(NC_EHDFERR);
1408 return retval;
1409}
1410
1423static int
1424attach_dimscales(NC_GRP_INFO_T *grp)
1425{
1426 NC_VAR_INFO_T *var;
1427 NC_HDF5_VAR_INFO_T *hdf5_var;
1428
1429 /* Attach dimension scales. */
1430 for (size_t v = 0; v < ncindexsize(grp->vars); v++)
1431 {
1432 /* Get pointer to var and HDF5-specific var info. */
1433 var = (NC_VAR_INFO_T *)ncindexith(grp->vars, v);
1434 assert(var && var->format_var_info);
1435 hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
1436
1437 /* Scales themselves do not attach. But I really wish they
1438 * would. */
1439 if (hdf5_var->dimscale)
1440 continue;
1441
1442 /* Find the scale for each dimension, if any, and attach it. */
1443 for (unsigned int d = 0; d < var->ndims; d++)
1444 {
1445 /* Is there a dimscale for this dimension? */
1446 if (hdf5_var->dimscale_attached)
1447 {
1448 if (!hdf5_var->dimscale_attached[d])
1449 {
1450 hid_t dsid; /* Dataset ID for dimension */
1451 assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] &&
1452 var->dim[d]->format_dim_info);
1453
1454 LOG((2, "%s: attaching scale for dimid %d to var %s",
1455 __func__, var->dimids[d], var->hdr.name));
1456
1457 /* Find dataset ID for dimension */
1458 if (var->dim[d]->coord_var)
1459 dsid = ((NC_HDF5_VAR_INFO_T *)(var->dim[d]->coord_var->format_var_info))->hdf_datasetid;
1460 else
1461 dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid;
1462
1463 if (dsid <= 0)
1464 return NC_EDIMSCALE;
1465
1466 /* Attach the scale. */
1467 if (H5DSattach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0)
1468 return NC_EDIMSCALE;
1469 hdf5_var->dimscale_attached[d] = NC_TRUE;
1470 }
1471 }
1472 }
1473 }
1474
1475 return NC_NOERR;
1476}
1477
1488static int
1489var_exists(hid_t grpid, char *name, nc_bool_t *exists)
1490{
1491 htri_t link_exists;
1492
1493 /* Reset the boolean */
1494 *exists = NC_FALSE;
1495
1496 /* Check if the object name exists in the group */
1497 if ((link_exists = H5Lexists(grpid, name, H5P_DEFAULT)) < 0)
1498 return NC_EHDFERR;
1499 if (link_exists)
1500 {
1501 H5G_stat_t statbuf;
1502
1503 /* Get info about the object */
1504 if (H5Gget_objinfo(grpid, name, 1, &statbuf) < 0)
1505 return NC_EHDFERR;
1506
1507 if (H5G_DATASET == statbuf.type)
1508 *exists = NC_TRUE;
1509 }
1510
1511 return NC_NOERR;
1512}
1513
1529static int
1530remove_coord_atts(hid_t hdf_datasetid)
1531{
1532 htri_t attr_exists;
1533
1534 /* If the variable dataset has an optional NC_DIMID_ATT_NAME
1535 * attribute, delete it. */
1536 if ((attr_exists = H5Aexists(hdf_datasetid, NC_DIMID_ATT_NAME)) < 0)
1537 return NC_EHDFERR;
1538 if (attr_exists)
1539 {
1540 if (H5Adelete(hdf_datasetid, NC_DIMID_ATT_NAME) < 0)
1541 return NC_EHDFERR;
1542 }
1543
1544 /* Remove the dimension scale 'CLASS' & 'NAME' attributes. */
1545 if ((attr_exists = H5Aexists(hdf_datasetid,
1546 HDF5_DIMSCALE_CLASS_ATT_NAME)) < 0)
1547 return NC_EHDFERR;
1548 if (attr_exists)
1549 {
1550 if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_CLASS_ATT_NAME) < 0)
1551 return NC_EHDFERR;
1552 }
1553 if ((attr_exists = H5Aexists(hdf_datasetid,
1554 HDF5_DIMSCALE_NAME_ATT_NAME)) < 0)
1555 return NC_EHDFERR;
1556 if (attr_exists)
1557 {
1558 if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_NAME_ATT_NAME) < 0)
1559 return NC_EHDFERR;
1560 }
1561 return NC_NOERR;
1562}
1563
1578static int
1579write_var(NC_VAR_INFO_T *var, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
1580{
1581 NC_HDF5_GRP_INFO_T *hdf5_grp;
1582 NC_HDF5_VAR_INFO_T *hdf5_var;
1583 nc_bool_t replace_existing_var = NC_FALSE;
1584 int retval;
1585
1586 assert(var && var->format_var_info && grp && grp->format_grp_info);
1587
1588 LOG((4, "%s: writing var %s", __func__, var->hdr.name));
1589
1590 /* Get HDF5-specific group and var info. */
1591 hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
1592 hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
1593
1594 /* If the variable has already been created & the fill value changed,
1595 * indicate that the existing variable should be replaced. */
1596 if (var->created && var->fill_val_changed)
1597 {
1598 replace_existing_var = NC_TRUE;
1599 var->fill_val_changed = NC_FALSE;
1600 /* If the variable is going to be replaced, we need to flag any
1601 other attributes associated with the variable as 'dirty', or
1602 else *only* the fill value attribute will be copied over and
1603 the rest will be lost. See
1604 https://github.com/Unidata/netcdf-c/issues/239 */
1605 flag_atts_dirty(var->att);
1606 }
1607
1608 /* Is this a coordinate var that has already been created in
1609 * the HDF5 file as a dimscale dataset? Check for dims with the
1610 * same name in this group. If there is one, check to see if
1611 * this object exists in the HDF group. */
1612 if (var->became_coord_var)
1613 {
1614 if ((NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name))
1615 {
1616 nc_bool_t exists;
1617
1618 if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists)))
1619 return retval;
1620 if (exists)
1621 {
1622 /* Indicate that the variable already exists, and should
1623 * be replaced. */
1624 replace_existing_var = NC_TRUE;
1625 flag_atts_dirty(var->att);
1626 }
1627 }
1628 }
1629
1630 /* Check dims if the variable will be replaced, so that the
1631 * dimensions will be de-attached and re-attached correctly. */
1632 if (replace_existing_var)
1633 {
1634 NC_DIM_INFO_T *d1;
1635
1636 /* Is there a dim with this var's name? */
1637 if ((d1 = (NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name)))
1638 {
1639 nc_bool_t exists;
1640 assert(d1->format_dim_info && d1->hdr.name);
1641
1642 if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists)))
1643 return retval;
1644 if (exists)
1645 {
1646 hid_t dsid;
1647
1648 /* Find dataset ID for dimension */
1649 if (d1->coord_var)
1650 dsid = ((NC_HDF5_VAR_INFO_T *)d1->coord_var->format_var_info)->hdf_datasetid;
1651 else
1652 dsid = ((NC_HDF5_DIM_INFO_T *)d1->format_dim_info)->hdf_dimscaleid;
1653 assert(dsid > 0);
1654
1655 /* If we're replacing an existing dimscale dataset, go to
1656 * every var in the file and detach this dimension scale,
1657 * because we have to delete it. */
1658 if ((retval = rec_detach_scales(grp->nc4_info->root_grp,
1659 var->dimids[0], dsid)))
1660 return retval;
1661 }
1662 }
1663 }
1664
1665 /* If this is not a dimension scale, remove any attached scales,
1666 * and delete dimscale attributes from the var. */
1667 if (var->was_coord_var && hdf5_var->dimscale_attached)
1668 {
1669 /* If the variable already exists in the file, Remove any dimension scale
1670 * attributes from it, if they exist. */
1671 if (var->created)
1672 if ((retval = remove_coord_atts(hdf5_var->hdf_datasetid)))
1673 return retval;
1674
1675 /* If this is a regular var, detach all its dim scales. */
1676 for (unsigned int d = 0; d < var->ndims; d++)
1677 {
1678 if (hdf5_var->dimscale_attached[d])
1679 {
1680 hid_t dsid; /* Dataset ID for dimension */
1681 assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] &&
1682 var->dim[d]->format_dim_info);
1683
1684 /* Find dataset ID for dimension */
1685 if (var->dim[d]->coord_var)
1686 dsid = ((NC_HDF5_VAR_INFO_T *)var->dim[d]->coord_var->format_var_info)->hdf_datasetid;
1687 else
1688 dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid;
1689 assert(dsid > 0);
1690
1691 /* Detach this dim scale. */
1692 if (H5DSdetach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0)
1693 return NC_EHDFERR;
1694 hdf5_var->dimscale_attached[d] = NC_FALSE;
1695 }
1696 }
1697 }
1698
1699 /* Delete the HDF5 dataset that is to be replaced. */
1700 if (replace_existing_var)
1701 {
1702 /* Free the HDF5 dataset id. */
1703 if (hdf5_var->hdf_datasetid && H5Dclose(hdf5_var->hdf_datasetid) < 0)
1704 return NC_EHDFERR;
1705 hdf5_var->hdf_datasetid = 0;
1706
1707 /* Now delete the variable. */
1708 if (H5Gunlink(hdf5_grp->hdf_grpid, var->hdr.name) < 0)
1709 return NC_EDIMMETA;
1710 }
1711
1712 /* Create the dataset. */
1713 if (var->is_new_var || replace_existing_var)
1714 {
1715 if ((retval = var_create_dataset(grp, var, write_dimid)))
1716 return retval;
1717 }
1718 else
1719 {
1720 if (write_dimid && var->ndims)
1721 if ((retval = write_netcdf4_dimid(hdf5_var->hdf_datasetid,
1722 var->dimids[0])))
1723 return retval;
1724 }
1725
1726 if (replace_existing_var)
1727 {
1728 /* If this is a dimension scale, reattach the scale everywhere it
1729 * is used. (Recall that netCDF dimscales are always 1-D). */
1730 if(hdf5_var->dimscale)
1731 {
1732 if ((retval = rec_reattach_scales(grp->nc4_info->root_grp,
1733 var->dimids[0], hdf5_var->hdf_datasetid)))
1734 return retval;
1735 }
1736 /* If it's not a dimension scale, clear the dimscale attached flags,
1737 * so the dimensions are re-attached. */
1738 else
1739 {
1740 if (hdf5_var->dimscale_attached)
1741 memset(hdf5_var->dimscale_attached, 0, sizeof(nc_bool_t) * var->ndims);
1742 }
1743 }
1744
1745 /* Clear coord. var state transition flags */
1746 var->was_coord_var = NC_FALSE;
1747 var->became_coord_var = NC_FALSE;
1748
1749 /* Now check the attributes for this var. */
1750 if (var->attr_dirty)
1751 {
1752 /* Write attributes for this var. */
1753 if ((retval = write_attlist(var->att, var->hdr.id, grp)))
1754 return retval;
1755 var->attr_dirty = NC_FALSE;
1756 }
1757
1758 return NC_NOERR;
1759}
1760
1772int
1773nc4_create_dim_wo_var(NC_DIM_INFO_T *dim)
1774{
1775 NC_HDF5_DIM_INFO_T *hdf5_dim;
1776 NC_HDF5_GRP_INFO_T *hdf5_grp;
1777 hid_t spaceid = -1, create_propid = -1;
1778 hsize_t dims[1], max_dims[1], chunk_dims[1] = {1};
1779 char dimscale_wo_var[NC_MAX_NAME];
1780 int retval = NC_NOERR;
1781
1782 LOG((4, "%s: creating dim %s", __func__, dim->hdr.name));
1783
1784 /* Sanity check */
1785 assert(!dim->coord_var);
1786
1787 /* Get HDF5-specific dim and group info. */
1788 hdf5_grp = (NC_HDF5_GRP_INFO_T *)dim->container->format_grp_info;
1789 hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
1790
1791 /* Create a property list. */
1792 if ((create_propid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
1793 BAIL(NC_EHDFERR);
1794
1795 /* Turn off recording of times associated with this object. */
1796 if (H5Pset_obj_track_times(create_propid, 0) < 0)
1797 BAIL(NC_EHDFERR);
1798
1799 /* Set size of dataset to size of dimension. */
1800 dims[0] = dim->len;
1801 max_dims[0] = dim->len;
1802
1803 /* If this dimension scale is unlimited (i.e. it's an unlimited
1804 * dimension), then set up chunking, with a chunksize of 1. */
1805 if (dim->unlimited)
1806 {
1807 max_dims[0] = H5S_UNLIMITED;
1808 if (H5Pset_chunk(create_propid, 1, chunk_dims) < 0)
1809 BAIL(NC_EHDFERR);
1810 }
1811
1812 /* Set up space. */
1813 if ((spaceid = H5Screate_simple(1, dims, max_dims)) < 0)
1814 BAIL(NC_EHDFERR);
1815
1816 /* Turn on creation-order tracking. */
1817 if (!dim->container->nc4_info->no_attr_create_order) {
1818 if (H5Pset_attr_creation_order(create_propid, H5P_CRT_ORDER_TRACKED|
1819 H5P_CRT_ORDER_INDEXED) < 0)
1820 BAIL(NC_EHDFERR);
1821 }
1822 /* Create the dataset that will be the dimension scale. */
1823 LOG((4, "%s: about to H5Dcreate1 a dimscale dataset %s", __func__,
1824 dim->hdr.name));
1825 if ((hdf5_dim->hdf_dimscaleid = H5Dcreate2(hdf5_grp->hdf_grpid, dim->hdr.name,
1826 H5T_IEEE_F32BE, spaceid,
1827 H5P_DEFAULT, create_propid,
1828 H5P_DEFAULT)) < 0)
1829 BAIL(NC_EHDFERR);
1830
1831 /* Indicate that this is a scale. Also indicate that not
1832 * be shown to the user as a variable. It is hidden. It is
1833 * a DIM WITHOUT A VARIABLE! */
1834 snprintf(dimscale_wo_var, sizeof(dimscale_wo_var), "%s%10d", DIM_WITHOUT_VARIABLE, (int)dim->len);
1835 if (H5DSset_scale(hdf5_dim->hdf_dimscaleid, dimscale_wo_var) < 0)
1836 BAIL(NC_EHDFERR);
1837
1838 /* Since this dimension was created out of order, we cannot rely on
1839 * it getting the correct dimid on file open. We must assign it
1840 * explicitly. */
1841 if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id)))
1842 BAIL(retval);
1843
1844exit:
1845 if (spaceid > 0 && H5Sclose(spaceid) < 0)
1846 BAIL2(NC_EHDFERR);
1847 if (create_propid > 0 && H5Pclose(create_propid) < 0)
1848 BAIL2(NC_EHDFERR);
1849 return retval;
1850}
1851
1864static int
1865write_dim(NC_DIM_INFO_T *dim, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
1866{
1867 NC_HDF5_DIM_INFO_T *hdf5_dim;
1868 int retval = NC_NOERR;
1869
1870 assert(dim && dim->format_dim_info && grp && grp->format_grp_info);
1871
1872 /* Get HDF5-specific dim and group info. */
1873 hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
1874
1875 /* If there's no dimscale dataset for this dim, create one,
1876 * and mark that it should be hidden from netCDF as a
1877 * variable. (That is, it should appear as a dimension
1878 * without an associated variable.) */
1879 if (!hdf5_dim->hdf_dimscaleid)
1880 if ((retval = nc4_create_dim_wo_var(dim)))
1881 BAIL(retval);
1882
1883 /* Did we extend an unlimited dimension? */
1884 if (dim->extended)
1885 {
1886 NC_VAR_INFO_T *v1 = NULL;
1887
1888 assert(dim->unlimited);
1889
1890 /* If this is a dimension with an associated coordinate var,
1891 * then update the length of that coord var. */
1892 v1 = dim->coord_var;
1893 if (v1)
1894 {
1895 NC_HDF5_VAR_INFO_T *hdf5_v1;
1896 hsize_t *new_size;
1897 int d1;
1898
1899 hdf5_v1 = (NC_HDF5_VAR_INFO_T *)v1->format_var_info;
1900
1901 /* Extend the dimension scale dataset to reflect the new
1902 * length of the dimension. */
1903 if (!(new_size = malloc(v1->ndims * sizeof(hsize_t))))
1904 BAIL(NC_ENOMEM);
1905 for (d1 = 0; d1 < v1->ndims; d1++)
1906 {
1907 assert(v1->dim[d1] && v1->dim[d1]->hdr.id == v1->dimids[d1]);
1908 new_size[d1] = v1->dim[d1]->len;
1909 }
1910 if (H5Dset_extent(hdf5_v1->hdf_datasetid, new_size) < 0)
1911 BAIL(NC_EHDFERR);
1912 free(new_size);
1913 }
1914 }
1915
1916 /* If desired, write the secret dimid. This will be used instead of
1917 * the dimid that the dimension would otherwise receive based on
1918 * creation order. This can be necessary when dims and their
1919 * coordinate variables were created in different order. */
1920 if (write_dimid && hdf5_dim->hdf_dimscaleid)
1921 if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id)))
1922 BAIL(retval);
1923
1924exit:
1925
1926 return retval;
1927}
1928
1941int
1942nc4_rec_write_metadata(NC_GRP_INFO_T *grp, nc_bool_t bad_coord_order)
1943{
1944 NC_DIM_INFO_T *dim = NULL;
1945 NC_VAR_INFO_T *var = NULL;
1946 NC_GRP_INFO_T *child_grp = NULL;
1947 int coord_varid = -1;
1948 size_t var_index = 0;
1949 size_t dim_index = 0;
1950 int retval;
1951
1952 assert(grp && grp->hdr.name &&
1953 ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid);
1954 LOG((3, "%s: grp->hdr.name %s, bad_coord_order %d", __func__, grp->hdr.name,
1955 bad_coord_order));
1956
1957 /* Write global attributes for this group. */
1958 if ((retval = write_attlist(grp->att, NC_GLOBAL, grp)))
1959 return retval;
1960
1961 /* Set the pointers to the beginning of the list of dims & vars in this
1962 * group. */
1963 dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, dim_index);
1964 var = (NC_VAR_INFO_T *)ncindexith(grp->vars, var_index);
1965
1966 /* Because of HDF5 ordering the dims and vars have to be stored in
1967 * this way to ensure that the dims and coordinate vars come out in
1968 * the correct order. */
1969 while (dim || var)
1970 {
1971 nc_bool_t found_coord, wrote_coord;
1972
1973 /* Write non-coord dims in order, stopping at the first one that
1974 * has an associated coord var. */
1975 for (found_coord = NC_FALSE; dim && !found_coord; )
1976 {
1977 if (!dim->coord_var)
1978 {
1979 if ((retval = write_dim(dim, grp, bad_coord_order)))
1980 return retval;
1981 }
1982 else
1983 {
1984 coord_varid = dim->coord_var->hdr.id;
1985 found_coord = NC_TRUE;
1986 }
1987 dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, ++dim_index);
1988 }
1989
1990 /* Write each var. When we get to the coord var we are waiting
1991 * for (if any), then we break after writing it. */
1992 for (wrote_coord = NC_FALSE; var && !wrote_coord; )
1993 {
1994 if ((retval = write_var(var, grp, bad_coord_order)))
1995 return retval;
1996 if (found_coord && var->hdr.id == coord_varid)
1997 wrote_coord = NC_TRUE;
1998 var = (NC_VAR_INFO_T *)ncindexith(grp->vars, ++var_index);
1999 }
2000 } /* end while */
2001
2002 /* Attach dimscales to vars in this group. Unless directed not to. */
2003 if (!grp->nc4_info->no_dimscale_attach) {
2004 if ((retval = attach_dimscales(grp)))
2005 return retval;
2006 }
2007
2008 /* If there are any child groups, write their metadata. */
2009 for (size_t i = 0; i < ncindexsize(grp->children); i++)
2010 {
2011 child_grp = (NC_GRP_INFO_T *)ncindexith(grp->children, i);
2012 assert(child_grp);
2013 if ((retval = nc4_rec_write_metadata(child_grp, bad_coord_order)))
2014 return retval;
2015 }
2016 return NC_NOERR;
2017}
2018
2028int
2029nc4_rec_write_groups_types(NC_GRP_INFO_T *grp)
2030{
2031 NC_GRP_INFO_T *child_grp;
2032 NC_HDF5_GRP_INFO_T *hdf5_grp;
2033 NC_TYPE_INFO_T *type;
2034 int retval;
2035 size_t i;
2036
2037 assert(grp && grp->hdr.name && grp->format_grp_info);
2038 LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
2039
2040 /* Get HDF5-specific group info. */
2041 hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
2042
2043 /* Create the group in the HDF5 file if it doesn't exist. */
2044 if (!hdf5_grp->hdf_grpid)
2045 if ((retval = create_group(grp)))
2046 return retval;
2047
2048 /* If this is the root group of a file with strict NC3 rules, write
2049 * an attribute. But don't leave the attribute open. */
2050 if (!grp->parent && (grp->nc4_info->cmode & NC_CLASSIC_MODEL))
2051 if ((retval = write_nc3_strict_att(hdf5_grp->hdf_grpid)))
2052 return retval;
2053
2054 /* If there are any user-defined types, write them now. */
2055 for(i=0;i<ncindexsize(grp->type);i++) {
2056 type = (NC_TYPE_INFO_T *)ncindexith(grp->type, i);
2057 assert(type);
2058 if ((retval = commit_type(grp, type)))
2059 return retval;
2060 }
2061
2062 /* If there are any child groups, write their groups and types. */
2063 for(i=0;i<ncindexsize(grp->children);i++) {
2064 if((child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children,i)) == NULL) continue;
2065 if ((retval = nc4_rec_write_groups_types(child_grp)))
2066 return retval;
2067 }
2068 return NC_NOERR;
2069}
2070
2084int
2085nc4_rec_match_dimscales(NC_GRP_INFO_T *grp)
2086{
2087 NC_GRP_INFO_T *g;
2088 NC_VAR_INFO_T *var;
2089 NC_DIM_INFO_T *dim;
2090 int retval = NC_NOERR;
2091 size_t i;
2092
2093 assert(grp && grp->hdr.name);
2094 LOG((4, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
2095
2096 /* Perform var dimscale match for child groups. */
2097 for (i = 0; i < ncindexsize(grp->children); i++)
2098 {
2099 g = (NC_GRP_INFO_T*)ncindexith(grp->children, i);
2100 assert(g);
2101 if ((retval = nc4_rec_match_dimscales(g)))
2102 return retval;
2103 }
2104
2105 /* Check all the vars in this group. If they have dimscale info,
2106 * try and find a dimension for them. */
2107 for (i = 0; i < ncindexsize(grp->vars); i++)
2108 {
2109 NC_HDF5_VAR_INFO_T *hdf5_var;
2110 int d;
2111
2112 /* Get pointer to var and to the HDF5-specific var info. */
2113 var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i);
2114 assert(var && var->format_var_info);
2115 hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
2116
2117 /* Check all vars and see if dim[i] != NULL if dimids[i] valid. */
2118 /* This loop is very odd. Under normal circumstances, var->dimid[d] is zero
2119 (from the initial calloc) which is a legitimate dimid. The code does not
2120 distinguish this case from the dimscale case where the id might actually
2121 be defined.
2122 The original nc4_find_dim searched up the group tree looking for the given
2123 dimid in one of the dim lists associated with each ancestor group.
2124 I changed nc4_fnd_dim to use the dimid directly using h5->alldims.
2125 However, here that is incorrect because it will find the dimid 0 always
2126 (if any dimensions were defined). Except that when dimscale dimids have
2127 been defined, one or more of the values in var->dimids will have a
2128 legitimate value.
2129 The solution I choose is to modify nc4_var_list_add to initialize dimids to
2130 illegal values (-1). This is another example of the problems with dimscales.
2131 */
2132 const size_t ndims = var->ndims;
2133 for (size_t d = 0; d < ndims; d++)
2134 {
2135 if (var->dim[d] == NULL) {
2136 nc4_find_dim(grp, var->dimids[d], &var->dim[d], NULL);
2137 }
2138 /* assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d]); */
2139 }
2140
2141 /* Skip dimension scale variables */
2142 if (!hdf5_var->dimscale)
2143 {
2144 /* Are there dimscales for this variable? */
2145 if (hdf5_var->dimscale_hdf5_objids)
2146 {
2147 for (size_t d = 0; d < var->ndims; d++)
2148 {
2149 nc_bool_t finished = NC_FALSE;
2150 LOG((5, "%s: var %s has dimscale info...", __func__, var->hdr.name));
2151
2152 /* Check this and parent groups. */
2153 for (g = grp; g && !finished; g = g->parent)
2154 {
2155 /* Check all dims in this group. */
2156 for (size_t j = 0; j < ncindexsize(g->dim); j++)
2157 {
2158 /* Get the HDF5 specific dim info. */
2159 NC_HDF5_DIM_INFO_T *hdf5_dim;
2160 dim = (NC_DIM_INFO_T *)ncindexith(g->dim, j);
2161 assert(dim && dim->format_dim_info);
2162 hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
2163
2164 /* Check for exact match of fileno/objid arrays
2165 * to find identical objects in HDF5 file. */
2166#if H5_VERSION_GE(1,12,0)
2167 int token_cmp;
2168 if (H5Otoken_cmp(hdf5_var->hdf_datasetid, &hdf5_var->dimscale_hdf5_objids[d].token, &hdf5_dim->hdf5_objid.token, &token_cmp) < 0)
2169 return NC_EHDFERR;
2170
2171 if (hdf5_var->dimscale_hdf5_objids[d].fileno == hdf5_dim->hdf5_objid.fileno &&
2172 token_cmp == 0)
2173#else
2174 if (hdf5_var->dimscale_hdf5_objids[d].fileno[0] == hdf5_dim->hdf5_objid.fileno[0] &&
2175 hdf5_var->dimscale_hdf5_objids[d].objno[0] == hdf5_dim->hdf5_objid.objno[0] &&
2176 hdf5_var->dimscale_hdf5_objids[d].fileno[1] == hdf5_dim->hdf5_objid.fileno[1] &&
2177 hdf5_var->dimscale_hdf5_objids[d].objno[1] == hdf5_dim->hdf5_objid.objno[1])
2178#endif
2179 {
2180 LOG((4, "%s: for dimension %d, found dim %s", __func__,
2181 d, dim->hdr.name));
2182 var->dimids[d] = dim->hdr.id;
2183 var->dim[d] = dim;
2184 finished = NC_TRUE;
2185 break;
2186 }
2187 } /* next dim */
2188 } /* next grp */
2189 LOG((5, "%s: dimid for this dimscale is %d", __func__,
2190 var->type_info->hdr.id));
2191 } /* next var->dim */
2192 }
2193 /* No dimscales for this var! Invent phony dimensions. */
2194 else
2195 {
2196 hid_t spaceid = 0;
2197 hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL;
2198 int dataset_ndims;
2199
2200 /* Find the space information for this dimension. */
2201 if ((spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0)
2202 return NC_EHDFERR;
2203
2204 /* Get the len of each dim in the space. */
2205 if (var->ndims)
2206 {
2207 if (!(h5dimlen = malloc(var->ndims * sizeof(hsize_t))))
2208 return NC_ENOMEM;
2209 if (!(h5dimlenmax = malloc(var->ndims * sizeof(hsize_t))))
2210 {
2211 free(h5dimlen);
2212 return NC_ENOMEM;
2213 }
2214 if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, h5dimlen,
2215 h5dimlenmax)) < 0) {
2216 free(h5dimlenmax);
2217 free(h5dimlen);
2218 return NC_EHDFERR;
2219 }
2220 if (dataset_ndims != var->ndims) {
2221 free(h5dimlenmax);
2222 free(h5dimlen);
2223 return NC_EHDFERR;
2224 }
2225 }
2226 else
2227 {
2228 /* Make sure it's scalar. */
2229 if (H5Sget_simple_extent_type(spaceid) != H5S_SCALAR)
2230 return NC_EHDFERR;
2231 }
2232
2233 /* Release the space object. */
2234 if (H5Sclose(spaceid) < 0) {
2235 free(h5dimlen);
2236 free(h5dimlenmax);
2237 return NC_EHDFERR;
2238 }
2239
2240 /* Create a phony dimension for each dimension in the
2241 * dataset, unless there already is one the correct
2242 * size. */
2243 for (d = 0; d < var->ndims; d++)
2244 {
2245 nc_bool_t match = NC_FALSE;
2246 /* Is there already a phony dimension of the correct size? */
2247 for(size_t k=0;k<ncindexsize(grp->dim);k++) {
2248 if((dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,k)) == NULL) continue;
2249 if ((dim->len == h5dimlen[d]) &&
2250 ((h5dimlenmax[d] == H5S_UNLIMITED && dim->unlimited) ||
2251 (h5dimlenmax[d] != H5S_UNLIMITED && !dim->unlimited)))
2252 {match = NC_TRUE; break;}
2253 }
2254
2255 /* Didn't find a phony dim? Then create one. */
2256 if (match == NC_FALSE)
2257 {
2258 char phony_dim_name[NC_MAX_NAME + 1];
2259 snprintf(phony_dim_name, sizeof(phony_dim_name), "phony_dim_%d", grp->nc4_info->next_dimid);
2260 LOG((3, "%s: creating phony dim for var %s", __func__, var->hdr.name));
2261 if ((retval = nc4_dim_list_add(grp, phony_dim_name, h5dimlen[d], -1, &dim)))
2262 {
2263 free(h5dimlenmax);
2264 free(h5dimlen);
2265 return retval;
2266 }
2267 /* Create struct for HDF5-specific dim info. */
2268 if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T))))
2269 return NC_ENOMEM;
2270 if (h5dimlenmax[d] == H5S_UNLIMITED)
2271 dim->unlimited = NC_TRUE;
2272 }
2273
2274 /* The variable must remember the dimid. */
2275 var->dimids[d] = dim->hdr.id;
2276 var->dim[d] = dim;
2277 } /* next dim */
2278
2279 /* Free the memory we malloced. */
2280 free(h5dimlen);
2281 free(h5dimlenmax);
2282 }
2283 }
2284 }
2285
2286 return retval;
2287}
2288
2300void
2301reportobject(int uselog, hid_t id, unsigned int type)
2302{
2303 char name[NC_HDF5_MAX_NAME];
2304 ssize_t len;
2305 const char* typename = NULL;
2306 long long printid = (long long)id;
2307
2308 len = H5Iget_name(id, name, NC_HDF5_MAX_NAME);
2309 if(len < 0) return;
2310 name[len] = '\0';
2311
2312 switch (type) {
2313 case H5F_OBJ_FILE: typename = "File"; break;
2314 case H5F_OBJ_DATASET: typename = "Dataset"; break;
2315 case H5F_OBJ_GROUP: typename = "Group"; break;
2316 case H5F_OBJ_DATATYPE: typename = "Datatype"; break;
2317 case H5F_OBJ_ATTR:
2318 typename = "Attribute";
2319 len = H5Aget_name(id, NC_HDF5_MAX_NAME, name);
2320 if(len < 0) len = 0;
2321 name[len] = '\0';
2322 break;
2323 default: typename = "<unknown>"; break;
2324 }
2325#ifdef LOGGING
2326 if(uselog) {
2327 LOG((0,"Type = %s(%lld) name='%s'",typename,printid,name));
2328 } else
2329#endif
2330 {
2331 fprintf(stderr,"Type = %s(%lld) name='%s'",typename,printid,name);
2332 }
2333
2334}
2335
2346static void
2347reportopenobjectsT(int uselog, hid_t fid, int ntypes, unsigned int* otypes)
2348{
2349 int t,i;
2350 ssize_t ocount;
2351 hid_t* idlist = NULL;
2352
2353 /* Always report somehow */
2354#ifdef LOGGING
2355 if(uselog)
2356 LOG((0,"\nReport: open objects on %lld",(long long)fid));
2357 else
2358#endif
2359 fprintf(stdout,"\nReport: open objects on %lld\n",(long long)fid);
2360 size_t maxobjs = (size_t)H5Fget_obj_count(fid,H5F_OBJ_ALL);
2361 if(idlist != NULL) free(idlist);
2362 idlist = (hid_t*)malloc(sizeof(hid_t)*maxobjs);
2363 for(t=0;t<ntypes;t++) {
2364 unsigned int ot = otypes[t];
2365 ocount = H5Fget_obj_ids(fid,ot,maxobjs,idlist);
2366 for(i=0;i<ocount;i++) {
2367 hid_t o = idlist[i];
2368 reportobject(uselog,o,ot);
2369 }
2370 }
2371 if(idlist != NULL) free(idlist);
2372}
2373
2382void
2383reportopenobjects(int uselog, hid_t fid)
2384{
2385 unsigned int OTYPES[5] = {H5F_OBJ_FILE, H5F_OBJ_DATASET, H5F_OBJ_GROUP,
2386 H5F_OBJ_DATATYPE, H5F_OBJ_ATTR};
2387
2388 reportopenobjectsT(uselog, fid ,5, OTYPES);
2389}
2390
2398void
2399showopenobjects5(NC_FILE_INFO_T* h5)
2400{
2401 NC_HDF5_FILE_INFO_T *hdf5_info;
2402
2403 assert(h5 && h5->format_file_info);
2404 hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
2405
2406 fprintf(stderr,"===== begin showopenobjects =====\n");
2407 reportopenobjects(0,hdf5_info->hdfid);
2408 fprintf(stderr,"===== end showopenobjects =====\n");
2409 fflush(stderr);
2410}
2411
2420void
2421showopenobjects(int ncid)
2422{
2423 NC_FILE_INFO_T* h5 = NULL;
2424
2425 /* Find our metadata for this file. */
2426 if (nc4_find_nc_grp_h5(ncid, NULL, NULL, &h5) != NC_NOERR)
2427 fprintf(stderr,"failed\n");
2428 else
2429 showopenobjects5(h5);
2430 fflush(stderr);
2431}
2432
2444int
2445NC4_hdf5get_libversion(unsigned* major,unsigned* minor,unsigned* release)
2446{
2447 if(H5get_libversion(major,minor,release) < 0)
2448 return NC_EHDFERR;
2449 return NC_NOERR;
2450}
2451
2462int
2463NC4_hdf5get_superblock(struct NC_FILE_INFO* h5, int* idp)
2464{
2465 NC_HDF5_FILE_INFO_T *hdf5_info;
2466 int stat = NC_NOERR;
2467 unsigned super;
2468 hid_t plist = -1;
2469
2470 assert(h5 && h5->format_file_info);
2471 hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
2472
2473 if((plist = H5Fget_create_plist(hdf5_info->hdfid)) < 0)
2474 {stat = NC_EHDFERR; goto done;}
2475 if(H5Pget_version(plist, &super, NULL, NULL, NULL) < 0)
2476 {stat = NC_EHDFERR; goto done;}
2477 if(idp) *idp = (int)super;
2478done:
2479 if(plist >= 0) H5Pclose(plist);
2480 return stat;
2481}
2482
2483static int NC4_root_att_exists(NC_FILE_INFO_T*, const char* aname);
2484static int NC4_walk(hid_t, int*);
2485
2511int
2512NC4_isnetcdf4(struct NC_FILE_INFO* h5)
2513{
2514 int stat;
2515 int isnc4 = 0;
2516 int exists;
2517 int count;
2518
2519 /* Look for NC3_STRICT_ATT_NAME */
2520 exists = NC4_root_att_exists(h5,NC3_STRICT_ATT_NAME);
2521 if(exists)
2522 {isnc4 = 1; goto done;}
2523 /* Look for _NCProperties */
2524 exists = NC4_root_att_exists(h5,NCPROPS);
2525 if(exists)
2526 {isnc4 = 1; goto done;}
2527 /* attribute did not exist */
2528 /* => last resort: walk the HDF5 file looking for markers */
2529 count = 0;
2530 stat = NC4_walk(((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid,
2531 &count);
2532 if(stat != NC_NOERR)
2533 isnc4 = 0;
2534 else /* Threshold is at least two matches */
2535 isnc4 = (count >= 2);
2536
2537done:
2538 return isnc4;
2539}
2540
2550static int
2551NC4_root_att_exists(NC_FILE_INFO_T *h5, const char* aname)
2552{
2553 hid_t grpid = -1;
2554 htri_t attr_exists;
2555
2556 /* Get root group ID. */
2557 grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid;
2558
2559 /* See if the NC3_STRICT_ATT_NAME attribute exists */
2560 if ((attr_exists = H5Aexists(grpid, aname))<0)
2561 return -1;
2562 return (attr_exists?1:0);
2563}
2564
2574static int
2575NC4_walk(hid_t gid, int* countp)
2576{
2577 int ncstat = NC_NOERR;
2578 int j,na;
2579 ssize_t len;
2580 hsize_t nobj;
2581 herr_t err;
2582 int otype;
2583 hid_t grpid, dsid;
2584 char name[NC_HDF5_MAX_NAME];
2585
2586 /* walk group members of interest */
2587 err = H5Gget_num_objs(gid, &nobj);
2588 if(err < 0) return err;
2589
2590 for(hsize_t i = 0; i < nobj; i++) {
2591 /* Get name & kind of object in the group */
2592 len = H5Gget_objname_by_idx(gid,i,name,(size_t)NC_HDF5_MAX_NAME);
2593 if(len < 0) return (int)len;
2594
2595 otype = H5Gget_objtype_by_idx(gid, i);
2596 switch(otype) {
2597 case H5G_GROUP:
2598 grpid = H5Gopen1(gid,name);
2599 NC4_walk(grpid,countp);
2600 H5Gclose(grpid);
2601 break;
2602 case H5G_DATASET: /* variables */
2603 /* Check for phony_dim */
2604 if(strcmp(name,"phony_dim")==0)
2605 *countp = *countp + 1;
2606 dsid = H5Dopen1(gid,name);
2607 na = H5Aget_num_attrs(dsid);
2608 for(j = 0; j < na; j++) {
2609 hid_t aid = H5Aopen_idx(dsid,(unsigned int) j);
2610 if(aid >= 0) {
2611 const NC_reservedatt* ra;
2612 ssize_t len = H5Aget_name(aid, NC_HDF5_MAX_NAME, name);
2613 if(len < 0) return (int)len;
2614 /* Is this a netcdf-4 marker attribute */
2615 ra = NC_findreserved(name);
2616 if(ra != NULL)
2617 *countp = *countp + 1;
2618 }
2619 H5Aclose(aid);
2620 }
2621 H5Dclose(dsid);
2622 break;
2623 default:/* ignore */
2624 break;
2625 }
2626 }
2627 return ncstat;
2628}
2629
EXTERNL int nc_free_vlen(nc_vlen_t *vl)
Free memory in a single VLEN object.
Definition dvlen.c:60
Main header file for the C API.
#define NC_EBADTYPE
Not a netcdf data type.
Definition netcdf.h:459
#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:343
#define NC_EFILTER
Filter operation failed.
Definition netcdf.h:562
#define NC_QUANTIZE_GRANULARBR_ATT_NAME
When quantization is used for a variable, an attribute of the appropriate name is added.
Definition netcdf.h:393
#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:345
#define NC_MAX_VAR_DIMS
max per variable dimensions
Definition netcdf.h:331
#define NC_BYTE
signed 1 byte integer
Definition netcdf.h:35
#define NC_EPERM
Write to read only.
Definition netcdf.h:428
#define NC_QUANTIZE_BITROUND
Use BitRound quantization.
Definition netcdf.h:387
#define NC_EDIMSCALE
Problem with HDF5 dimscales.
Definition netcdf.h:553
#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:497
#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:353
#define NC_SHORT
signed 2 byte integer
Definition netcdf.h:37
#define NC_QUANTIZE_GRANULARBR
Use Granular BitRound quantization.
Definition netcdf.h:386
#define NC_QUANTIZE_BITGROOM
Use BitGroom quantization.
Definition netcdf.h:385
#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:536
#define NC_EHDFERR
Error at HDF5 layer.
Definition netcdf.h:530
#define NC_CONTIGUOUS
In HDF5 files you can set storage for each variable to be either contiguous or chunked,...
Definition netcdf.h:354
#define NC_GLOBAL
Attribute id to put/get a global attribute.
Definition netcdf.h:303
#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:355
#define NC_EFILEMETA
Problem with file metadata.
Definition netcdf.h:534
#define NC_ENOTVAR
Variable not found.
Definition netcdf.h:471
#define NC_EINVAL
Invalid Argument.
Definition netcdf.h:427
#define NC_CLASSIC_MODEL
Enforce classic model on netCDF-4.
Definition netcdf.h:166
#define NC_ENDIAN_LITTLE
In HDF5 files you can set the endianness of variables with nc_def_var_endian().
Definition netcdf.h:344
#define NC_MAX_NAME
Maximum for classic library.
Definition netcdf.h:330
#define NC_NOERR
No Error.
Definition netcdf.h:417
#define NC_EDIMMETA
Problem with dimension metadata.
Definition netcdf.h:535
#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:392
#define NC_CHAR
ISO/ASCII character.
Definition netcdf.h:36
#define NC_EVARMETA
Problem with variable metadata.
Definition netcdf.h:537
#define NC_QUANTIZE_BITROUND_ATT_NAME
When quantization is used for a variable, an attribute of the appropriate name is added.
Definition netcdf.h:394
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:809