NetCDF 4.10.0
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. */
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
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. */
1136 if (var->chunkcache.size == CHUNK_CACHE_SIZE)
1137 if (chunk_size_bytes > var->chunkcache.size)
1138 {
1139 var->chunkcache.size = chunk_size_bytes * DEFAULT_CHUNKS_IN_CACHE;
1140 if (var->chunkcache.size > DEFAULT_CHUNK_CACHE_SIZE)
1141 var->chunkcache.size = DEFAULT_CHUNK_CACHE_SIZE;
1142 if ((retval = nc4_reopen_dataset(grp, var)))
1143 return retval;
1144 }
1145
1146 return NC_NOERR;
1147}
1148
1164static int
1165commit_type(NC_GRP_INFO_T *grp, NC_TYPE_INFO_T *type)
1166{
1167 NC_HDF5_GRP_INFO_T *hdf5_grp;
1168 NC_HDF5_TYPE_INFO_T *hdf5_type;
1169 hid_t base_hdf_typeid;
1170 int retval;
1171
1172 assert(grp && grp->format_grp_info && type && type->format_type_info);
1173
1174 /* Get HDF5-specific group and type info. */
1175 hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
1176 hdf5_type = (NC_HDF5_TYPE_INFO_T *)type->format_type_info;
1177
1178 /* Did we already record this type? */
1179 if (type->committed)
1180 return NC_NOERR;
1181
1182 /* Is this a compound type? */
1183 if (type->nc_type_class == NC_COMPOUND)
1184 {
1185 NC_FIELD_INFO_T *field;
1186 hid_t hdf_base_typeid, hdf_typeid;
1187 size_t i;
1188
1189 if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_COMPOUND, type->size)) < 0)
1190 return NC_EHDFERR;
1191 LOG((4, "creating compound type %s hdf_typeid 0x%x", type->hdr.name,
1192 hdf5_type->hdf_typeid));
1193
1194 for(i=0;i<nclistlength(type->u.c.field);i++)
1195 {
1196 field = (NC_FIELD_INFO_T *)nclistget(type->u.c.field, i);
1197 assert(field);
1198 if ((retval = nc4_get_hdf_typeid(grp->nc4_info, field->nc_typeid,
1199 &hdf_base_typeid, type->endianness)))
1200 return retval;
1201
1202 /* If this is an array, create a special array type. */
1203 if (field->ndims)
1204 {
1205 int d;
1206 hsize_t dims[NC_MAX_VAR_DIMS];
1207
1208 for (d = 0; d < field->ndims; d++)
1209 dims[d] = (hsize_t)field->dim_size[d];
1210 if ((hdf_typeid = H5Tarray_create1(hdf_base_typeid, field->ndims,
1211 dims, NULL)) < 0)
1212 {
1213 if (H5Tclose(hdf_base_typeid) < 0)
1214 return NC_EHDFERR;
1215 return NC_EHDFERR;
1216 }
1217 if (H5Tclose(hdf_base_typeid) < 0)
1218 return NC_EHDFERR;
1219 }
1220 else
1221 hdf_typeid = hdf_base_typeid;
1222 LOG((4, "inserting field %s offset %d hdf_typeid 0x%x", field->hdr.name,
1223 field->offset, hdf_typeid));
1224 if (H5Tinsert(hdf5_type->hdf_typeid, field->hdr.name, field->offset,
1225 hdf_typeid) < 0)
1226 return NC_EHDFERR;
1227 if (H5Tclose(hdf_typeid) < 0)
1228 return NC_EHDFERR;
1229 }
1230 }
1231 else if (type->nc_type_class == NC_VLEN)
1232 {
1233 /* Find the HDF typeid of the base type of this vlen. */
1234 if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.v.base_nc_typeid,
1235 &base_hdf_typeid, type->endianness)))
1236 return retval;
1237
1238 /* Create a vlen type. */
1239 if ((hdf5_type->hdf_typeid = H5Tvlen_create(base_hdf_typeid)) < 0)
1240 return NC_EHDFERR;
1241 }
1242 else if (type->nc_type_class == NC_OPAQUE)
1243 {
1244 /* Create the opaque type. */
1245 if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_OPAQUE, type->size)) < 0)
1246 return NC_EHDFERR;
1247 }
1248 else if (type->nc_type_class == NC_ENUM)
1249 {
1250 NC_ENUM_MEMBER_INFO_T *enum_m;
1251 size_t i;
1252
1253 if (nclistlength(type->u.e.enum_member) == 0)
1254 return NC_EINVAL;
1255
1256 /* Find the HDF typeid of the base type of this enum. */
1257 if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.e.base_nc_typeid,
1258 &base_hdf_typeid, type->endianness)))
1259 return retval;
1260
1261 /* Create an enum type. */
1262 if ((hdf5_type->hdf_typeid = H5Tenum_create(base_hdf_typeid)) < 0)
1263 return NC_EHDFERR;
1264
1265 /* Add all the members to the HDF5 type. */
1266 for(i=0;i<nclistlength(type->u.e.enum_member);i++) {
1267 enum_m = (NC_ENUM_MEMBER_INFO_T*)nclistget(type->u.e.enum_member,i);
1268 if (H5Tenum_insert(hdf5_type->hdf_typeid, enum_m->name, enum_m->value) < 0)
1269 return NC_EHDFERR;
1270 }
1271 }
1272 else
1273 {
1274 LOG((0, "Unknown class: %d", type->nc_type_class));
1275 return NC_EBADTYPE;
1276 }
1277
1278 /* Commit the type. */
1279 if (H5Tcommit1(hdf5_grp->hdf_grpid, type->hdr.name, hdf5_type->hdf_typeid) < 0)
1280 return NC_EHDFERR;
1281 type->committed = NC_TRUE;
1282 LOG((4, "just committed type %s, HDF typeid: 0x%x", type->hdr.name,
1283 hdf5_type->hdf_typeid));
1284
1285 /* Later we will always use the native typeid. In this case, it is
1286 * a copy of the same type pointed to by hdf_typeid, but it's
1287 * easier to maintain a copy. */
1288 if ((hdf5_type->native_hdf_typeid = H5Tget_native_type(hdf5_type->hdf_typeid,
1289 H5T_DIR_DEFAULT)) < 0)
1290 return NC_EHDFERR;
1291
1292 return NC_NOERR;
1293}
1294
1305static int
1306write_nc3_strict_att(hid_t hdf_grpid)
1307{
1308 hid_t attid = 0, spaceid = 0;
1309 int one = 1;
1310 int retval = NC_NOERR;
1311 htri_t attr_exists;
1312
1313 /* If the attribute already exists, call that a success and return
1314 * NC_NOERR. */
1315 if ((attr_exists = H5Aexists(hdf_grpid, NC3_STRICT_ATT_NAME)) < 0)
1316 return NC_EHDFERR;
1317 if (attr_exists)
1318 return NC_NOERR;
1319
1320 /* Create the attribute to mark this as a file that needs to obey
1321 * strict netcdf-3 rules. */
1322 if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
1323 BAIL(NC_EFILEMETA);
1324 if ((attid = H5Acreate1(hdf_grpid, NC3_STRICT_ATT_NAME,
1325 H5T_NATIVE_INT, spaceid, H5P_DEFAULT)) < 0)
1326 BAIL(NC_EFILEMETA);
1327 if (H5Awrite(attid, H5T_NATIVE_INT, &one) < 0)
1328 BAIL(NC_EFILEMETA);
1329
1330exit:
1331 if (spaceid > 0 && (H5Sclose(spaceid) < 0))
1332 BAIL2(NC_EFILEMETA);
1333 if (attid > 0 && (H5Aclose(attid) < 0))
1334 BAIL2(NC_EFILEMETA);
1335 return retval;
1336}
1337
1350static int
1351create_group(NC_GRP_INFO_T *grp)
1352{
1353 NC_HDF5_GRP_INFO_T *hdf5_grp, *parent_hdf5_grp;
1354 hid_t gcpl_id = -1;
1355 int retval = NC_NOERR;;
1356
1357 assert(grp && grp->format_grp_info && grp->parent &&
1358 grp->parent->format_grp_info);
1359
1360 /* Get HDF5 specific group info for group and parent. */
1361 hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
1362 parent_hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->parent->format_grp_info;
1363 assert(parent_hdf5_grp->hdf_grpid);
1364
1365 /* Create group, with link_creation_order set in the group
1366 * creation property list. */
1367 if ((gcpl_id = H5Pcreate(H5P_GROUP_CREATE)) < 0)
1368 BAIL(NC_EHDFERR);
1369
1370 /* Set track_times to be FALSE. */
1371 if (H5Pset_obj_track_times(gcpl_id, 0) < 0)
1372 BAIL(NC_EHDFERR);
1373
1374 /* Tell HDF5 to keep track of objects in creation order. */
1375 if (H5Pset_link_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0)
1376 BAIL(NC_EHDFERR);
1377
1378 /* Tell HDF5 to keep track of attributes in creation order. */
1379 if (!grp->nc4_info->no_attr_create_order) {
1380 if (H5Pset_attr_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0)
1381 BAIL(NC_EHDFERR);
1382 }
1383
1384 /* Create the group. */
1385 if ((hdf5_grp->hdf_grpid = H5Gcreate2(parent_hdf5_grp->hdf_grpid, grp->hdr.name,
1386 H5P_DEFAULT, gcpl_id, H5P_DEFAULT)) < 0)
1387 BAIL(NC_EHDFERR);
1388
1389exit:
1390 if (gcpl_id > -1 && H5Pclose(gcpl_id) < 0)
1391 BAIL2(NC_EHDFERR);
1392 if (retval)
1393 if (hdf5_grp->hdf_grpid > 0 && H5Gclose(hdf5_grp->hdf_grpid) < 0)
1394 BAIL2(NC_EHDFERR);
1395 return retval;
1396}
1397
1410static int
1411attach_dimscales(NC_GRP_INFO_T *grp)
1412{
1413 NC_VAR_INFO_T *var;
1414 NC_HDF5_VAR_INFO_T *hdf5_var;
1415
1416 /* Attach dimension scales. */
1417 for (size_t v = 0; v < ncindexsize(grp->vars); v++)
1418 {
1419 /* Get pointer to var and HDF5-specific var info. */
1420 var = (NC_VAR_INFO_T *)ncindexith(grp->vars, v);
1421 assert(var && var->format_var_info);
1422 hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
1423
1424 /* Scales themselves do not attach. But I really wish they
1425 * would. */
1426 if (hdf5_var->dimscale)
1427 continue;
1428
1429 /* Find the scale for each dimension, if any, and attach it. */
1430 for (unsigned int d = 0; d < var->ndims; d++)
1431 {
1432 /* Is there a dimscale for this dimension? */
1433 if (hdf5_var->dimscale_attached)
1434 {
1435 if (!hdf5_var->dimscale_attached[d])
1436 {
1437 hid_t dsid; /* Dataset ID for dimension */
1438 assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] &&
1439 var->dim[d]->format_dim_info);
1440
1441 LOG((2, "%s: attaching scale for dimid %d to var %s",
1442 __func__, var->dimids[d], var->hdr.name));
1443
1444 /* Find dataset ID for dimension */
1445 if (var->dim[d]->coord_var)
1446 dsid = ((NC_HDF5_VAR_INFO_T *)(var->dim[d]->coord_var->format_var_info))->hdf_datasetid;
1447 else
1448 dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid;
1449
1450 if (dsid <= 0)
1451 return NC_EDIMSCALE;
1452
1453 /* Attach the scale. */
1454 if (H5DSattach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0)
1455 return NC_EDIMSCALE;
1456 hdf5_var->dimscale_attached[d] = NC_TRUE;
1457 }
1458 }
1459 }
1460 }
1461
1462 return NC_NOERR;
1463}
1464
1475static int
1476var_exists(hid_t grpid, char *name, nc_bool_t *exists)
1477{
1478 htri_t link_exists;
1479
1480 /* Reset the boolean */
1481 *exists = NC_FALSE;
1482
1483 /* Check if the object name exists in the group */
1484 if ((link_exists = H5Lexists(grpid, name, H5P_DEFAULT)) < 0)
1485 return NC_EHDFERR;
1486 if (link_exists)
1487 {
1488 H5G_stat_t statbuf;
1489
1490 /* Get info about the object */
1491 if (H5Gget_objinfo(grpid, name, 1, &statbuf) < 0)
1492 return NC_EHDFERR;
1493
1494 if (H5G_DATASET == statbuf.type)
1495 *exists = NC_TRUE;
1496 }
1497
1498 return NC_NOERR;
1499}
1500
1516static int
1517remove_coord_atts(hid_t hdf_datasetid)
1518{
1519 htri_t attr_exists;
1520
1521 /* If the variable dataset has an optional NC_DIMID_ATT_NAME
1522 * attribute, delete it. */
1523 if ((attr_exists = H5Aexists(hdf_datasetid, NC_DIMID_ATT_NAME)) < 0)
1524 return NC_EHDFERR;
1525 if (attr_exists)
1526 {
1527 if (H5Adelete(hdf_datasetid, NC_DIMID_ATT_NAME) < 0)
1528 return NC_EHDFERR;
1529 }
1530
1531 /* Remove the dimension scale 'CLASS' & 'NAME' attributes. */
1532 if ((attr_exists = H5Aexists(hdf_datasetid,
1533 HDF5_DIMSCALE_CLASS_ATT_NAME)) < 0)
1534 return NC_EHDFERR;
1535 if (attr_exists)
1536 {
1537 if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_CLASS_ATT_NAME) < 0)
1538 return NC_EHDFERR;
1539 }
1540 if ((attr_exists = H5Aexists(hdf_datasetid,
1541 HDF5_DIMSCALE_NAME_ATT_NAME)) < 0)
1542 return NC_EHDFERR;
1543 if (attr_exists)
1544 {
1545 if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_NAME_ATT_NAME) < 0)
1546 return NC_EHDFERR;
1547 }
1548 return NC_NOERR;
1549}
1550
1565static int
1566write_var(NC_VAR_INFO_T *var, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
1567{
1568 NC_HDF5_GRP_INFO_T *hdf5_grp;
1569 NC_HDF5_VAR_INFO_T *hdf5_var;
1570 nc_bool_t replace_existing_var = NC_FALSE;
1571 int retval;
1572
1573 assert(var && var->format_var_info && grp && grp->format_grp_info);
1574
1575 LOG((4, "%s: writing var %s", __func__, var->hdr.name));
1576
1577 /* Get HDF5-specific group and var info. */
1578 hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
1579 hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
1580
1581 /* If the variable has already been created & the fill value changed,
1582 * indicate that the existing variable should be replaced. */
1583 if (var->created && var->fill_val_changed)
1584 {
1585 replace_existing_var = NC_TRUE;
1586 var->fill_val_changed = NC_FALSE;
1587 /* If the variable is going to be replaced, we need to flag any
1588 other attributes associated with the variable as 'dirty', or
1589 else *only* the fill value attribute will be copied over and
1590 the rest will be lost. See
1591 https://github.com/Unidata/netcdf-c/issues/239 */
1592 flag_atts_dirty(var->att);
1593 }
1594
1595 /* Is this a coordinate var that has already been created in
1596 * the HDF5 file as a dimscale dataset? Check for dims with the
1597 * same name in this group. If there is one, check to see if
1598 * this object exists in the HDF group. */
1599 if (var->became_coord_var)
1600 {
1601 if ((NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name))
1602 {
1603 nc_bool_t exists;
1604
1605 if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists)))
1606 return retval;
1607 if (exists)
1608 {
1609 /* Indicate that the variable already exists, and should
1610 * be replaced. */
1611 replace_existing_var = NC_TRUE;
1612 flag_atts_dirty(var->att);
1613 }
1614 }
1615 }
1616
1617 /* Check dims if the variable will be replaced, so that the
1618 * dimensions will be de-attached and re-attached correctly. */
1619 if (replace_existing_var)
1620 {
1621 NC_DIM_INFO_T *d1;
1622
1623 /* Is there a dim with this var's name? */
1624 if ((d1 = (NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name)))
1625 {
1626 nc_bool_t exists;
1627 assert(d1->format_dim_info && d1->hdr.name);
1628
1629 if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists)))
1630 return retval;
1631 if (exists)
1632 {
1633 hid_t dsid;
1634
1635 /* Find dataset ID for dimension */
1636 if (d1->coord_var)
1637 dsid = ((NC_HDF5_VAR_INFO_T *)d1->coord_var->format_var_info)->hdf_datasetid;
1638 else
1639 dsid = ((NC_HDF5_DIM_INFO_T *)d1->format_dim_info)->hdf_dimscaleid;
1640 assert(dsid > 0);
1641
1642 /* If we're replacing an existing dimscale dataset, go to
1643 * every var in the file and detach this dimension scale,
1644 * because we have to delete it. */
1645 if ((retval = rec_detach_scales(grp->nc4_info->root_grp,
1646 var->dimids[0], dsid)))
1647 return retval;
1648 }
1649 }
1650 }
1651
1652 /* If this is not a dimension scale, remove any attached scales,
1653 * and delete dimscale attributes from the var. */
1654 if (var->was_coord_var && hdf5_var->dimscale_attached)
1655 {
1656 /* If the variable already exists in the file, Remove any dimension scale
1657 * attributes from it, if they exist. */
1658 if (var->created)
1659 if ((retval = remove_coord_atts(hdf5_var->hdf_datasetid)))
1660 return retval;
1661
1662 /* If this is a regular var, detach all its dim scales. */
1663 for (unsigned int d = 0; d < var->ndims; d++)
1664 {
1665 if (hdf5_var->dimscale_attached[d])
1666 {
1667 hid_t dsid; /* Dataset ID for dimension */
1668 assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] &&
1669 var->dim[d]->format_dim_info);
1670
1671 /* Find dataset ID for dimension */
1672 if (var->dim[d]->coord_var)
1673 dsid = ((NC_HDF5_VAR_INFO_T *)var->dim[d]->coord_var->format_var_info)->hdf_datasetid;
1674 else
1675 dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid;
1676 assert(dsid > 0);
1677
1678 /* Detach this dim scale. */
1679 if (H5DSdetach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0)
1680 return NC_EHDFERR;
1681 hdf5_var->dimscale_attached[d] = NC_FALSE;
1682 }
1683 }
1684 }
1685
1686 /* Delete the HDF5 dataset that is to be replaced. */
1687 if (replace_existing_var)
1688 {
1689 /* Free the HDF5 dataset id. */
1690 if (hdf5_var->hdf_datasetid && H5Dclose(hdf5_var->hdf_datasetid) < 0)
1691 return NC_EHDFERR;
1692 hdf5_var->hdf_datasetid = 0;
1693
1694 /* Now delete the variable. */
1695 if (H5Gunlink(hdf5_grp->hdf_grpid, var->hdr.name) < 0)
1696 return NC_EDIMMETA;
1697 }
1698
1699 /* Create the dataset. */
1700 if (var->is_new_var || replace_existing_var)
1701 {
1702 if ((retval = var_create_dataset(grp, var, write_dimid)))
1703 return retval;
1704 }
1705 else
1706 {
1707 if (write_dimid && var->ndims)
1708 if ((retval = write_netcdf4_dimid(hdf5_var->hdf_datasetid,
1709 var->dimids[0])))
1710 return retval;
1711 }
1712
1713 if (replace_existing_var)
1714 {
1715 /* If this is a dimension scale, reattach the scale everywhere it
1716 * is used. (Recall that netCDF dimscales are always 1-D). */
1717 if(hdf5_var->dimscale)
1718 {
1719 if ((retval = rec_reattach_scales(grp->nc4_info->root_grp,
1720 var->dimids[0], hdf5_var->hdf_datasetid)))
1721 return retval;
1722 }
1723 /* If it's not a dimension scale, clear the dimscale attached flags,
1724 * so the dimensions are re-attached. */
1725 else
1726 {
1727 if (hdf5_var->dimscale_attached)
1728 memset(hdf5_var->dimscale_attached, 0, sizeof(nc_bool_t) * var->ndims);
1729 }
1730 }
1731
1732 /* Clear coord. var state transition flags */
1733 var->was_coord_var = NC_FALSE;
1734 var->became_coord_var = NC_FALSE;
1735
1736 /* Now check the attributes for this var. */
1737 if (var->attr_dirty)
1738 {
1739 /* Write attributes for this var. */
1740 if ((retval = write_attlist(var->att, var->hdr.id, grp)))
1741 return retval;
1742 var->attr_dirty = NC_FALSE;
1743 }
1744
1745 return NC_NOERR;
1746}
1747
1759int
1760nc4_create_dim_wo_var(NC_DIM_INFO_T *dim)
1761{
1762 NC_HDF5_DIM_INFO_T *hdf5_dim;
1763 NC_HDF5_GRP_INFO_T *hdf5_grp;
1764 hid_t spaceid = -1, create_propid = -1;
1765 hsize_t dims[1], max_dims[1], chunk_dims[1] = {1};
1766 char dimscale_wo_var[NC_MAX_NAME];
1767 int retval = NC_NOERR;
1768
1769 LOG((4, "%s: creating dim %s", __func__, dim->hdr.name));
1770
1771 /* Sanity check */
1772 assert(!dim->coord_var);
1773
1774 /* Get HDF5-specific dim and group info. */
1775 hdf5_grp = (NC_HDF5_GRP_INFO_T *)dim->container->format_grp_info;
1776 hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
1777
1778 /* Create a property list. */
1779 if ((create_propid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
1780 BAIL(NC_EHDFERR);
1781
1782 /* Turn off recording of times associated with this object. */
1783 if (H5Pset_obj_track_times(create_propid, 0) < 0)
1784 BAIL(NC_EHDFERR);
1785
1786 /* Set size of dataset to size of dimension. */
1787 dims[0] = dim->len;
1788 max_dims[0] = dim->len;
1789
1790 /* If this dimension scale is unlimited (i.e. it's an unlimited
1791 * dimension), then set up chunking, with a chunksize of 1. */
1792 if (dim->unlimited)
1793 {
1794 max_dims[0] = H5S_UNLIMITED;
1795 if (H5Pset_chunk(create_propid, 1, chunk_dims) < 0)
1796 BAIL(NC_EHDFERR);
1797 }
1798
1799 /* Set up space. */
1800 if ((spaceid = H5Screate_simple(1, dims, max_dims)) < 0)
1801 BAIL(NC_EHDFERR);
1802
1803 /* Turn on creation-order tracking. */
1804 if (!dim->container->nc4_info->no_attr_create_order) {
1805 if (H5Pset_attr_creation_order(create_propid, H5P_CRT_ORDER_TRACKED|
1806 H5P_CRT_ORDER_INDEXED) < 0)
1807 BAIL(NC_EHDFERR);
1808 }
1809 /* Create the dataset that will be the dimension scale. */
1810 LOG((4, "%s: about to H5Dcreate1 a dimscale dataset %s", __func__,
1811 dim->hdr.name));
1812 if ((hdf5_dim->hdf_dimscaleid = H5Dcreate2(hdf5_grp->hdf_grpid, dim->hdr.name,
1813 H5T_IEEE_F32BE, spaceid,
1814 H5P_DEFAULT, create_propid,
1815 H5P_DEFAULT)) < 0)
1816 BAIL(NC_EHDFERR);
1817
1818 /* Indicate that this is a scale. Also indicate that not
1819 * be shown to the user as a variable. It is hidden. It is
1820 * a DIM WITHOUT A VARIABLE! */
1821 snprintf(dimscale_wo_var, sizeof(dimscale_wo_var), "%s%10d", DIM_WITHOUT_VARIABLE, (int)dim->len);
1822 if (H5DSset_scale(hdf5_dim->hdf_dimscaleid, dimscale_wo_var) < 0)
1823 BAIL(NC_EHDFERR);
1824
1825 /* Since this dimension was created out of order, we cannot rely on
1826 * it getting the correct dimid on file open. We must assign it
1827 * explicitly. */
1828 if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id)))
1829 BAIL(retval);
1830
1831exit:
1832 if (spaceid > 0 && H5Sclose(spaceid) < 0)
1833 BAIL2(NC_EHDFERR);
1834 if (create_propid > 0 && H5Pclose(create_propid) < 0)
1835 BAIL2(NC_EHDFERR);
1836 return retval;
1837}
1838
1851static int
1852write_dim(NC_DIM_INFO_T *dim, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
1853{
1854 NC_HDF5_DIM_INFO_T *hdf5_dim;
1855 int retval = NC_NOERR;
1856
1857 assert(dim && dim->format_dim_info && grp && grp->format_grp_info);
1858
1859 /* Get HDF5-specific dim and group info. */
1860 hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
1861
1862 /* If there's no dimscale dataset for this dim, create one,
1863 * and mark that it should be hidden from netCDF as a
1864 * variable. (That is, it should appear as a dimension
1865 * without an associated variable.) */
1866 if (!hdf5_dim->hdf_dimscaleid)
1867 if ((retval = nc4_create_dim_wo_var(dim)))
1868 BAIL(retval);
1869
1870 /* Did we extend an unlimited dimension? */
1871 if (dim->extended)
1872 {
1873 NC_VAR_INFO_T *v1 = NULL;
1874
1875 assert(dim->unlimited);
1876
1877 /* If this is a dimension with an associated coordinate var,
1878 * then update the length of that coord var. */
1879 v1 = dim->coord_var;
1880 if (v1)
1881 {
1882 NC_HDF5_VAR_INFO_T *hdf5_v1;
1883 hsize_t *new_size;
1884 int d1;
1885
1886 hdf5_v1 = (NC_HDF5_VAR_INFO_T *)v1->format_var_info;
1887
1888 /* Extend the dimension scale dataset to reflect the new
1889 * length of the dimension. */
1890 if (!(new_size = malloc(v1->ndims * sizeof(hsize_t))))
1891 BAIL(NC_ENOMEM);
1892 for (d1 = 0; d1 < v1->ndims; d1++)
1893 {
1894 assert(v1->dim[d1] && v1->dim[d1]->hdr.id == v1->dimids[d1]);
1895 new_size[d1] = v1->dim[d1]->len;
1896 }
1897 if (H5Dset_extent(hdf5_v1->hdf_datasetid, new_size) < 0)
1898 BAIL(NC_EHDFERR);
1899 free(new_size);
1900 }
1901 }
1902
1903 /* If desired, write the secret dimid. This will be used instead of
1904 * the dimid that the dimension would otherwise receive based on
1905 * creation order. This can be necessary when dims and their
1906 * coordinate variables were created in different order. */
1907 if (write_dimid && hdf5_dim->hdf_dimscaleid)
1908 if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id)))
1909 BAIL(retval);
1910
1911exit:
1912
1913 return retval;
1914}
1915
1928int
1929nc4_rec_write_metadata(NC_GRP_INFO_T *grp, nc_bool_t bad_coord_order)
1930{
1931 NC_DIM_INFO_T *dim = NULL;
1932 NC_VAR_INFO_T *var = NULL;
1933 NC_GRP_INFO_T *child_grp = NULL;
1934 int coord_varid = -1;
1935 size_t var_index = 0;
1936 size_t dim_index = 0;
1937 int retval;
1938
1939 assert(grp && grp->hdr.name &&
1940 ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid);
1941 LOG((3, "%s: grp->hdr.name %s, bad_coord_order %d", __func__, grp->hdr.name,
1942 bad_coord_order));
1943
1944 /* Write global attributes for this group. */
1945 if ((retval = write_attlist(grp->att, NC_GLOBAL, grp)))
1946 return retval;
1947
1948 /* Set the pointers to the beginning of the list of dims & vars in this
1949 * group. */
1950 dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, dim_index);
1951 var = (NC_VAR_INFO_T *)ncindexith(grp->vars, var_index);
1952
1953 /* Because of HDF5 ordering the dims and vars have to be stored in
1954 * this way to ensure that the dims and coordinate vars come out in
1955 * the correct order. */
1956 while (dim || var)
1957 {
1958 nc_bool_t found_coord, wrote_coord;
1959
1960 /* Write non-coord dims in order, stopping at the first one that
1961 * has an associated coord var. */
1962 for (found_coord = NC_FALSE; dim && !found_coord; )
1963 {
1964 if (!dim->coord_var)
1965 {
1966 if ((retval = write_dim(dim, grp, bad_coord_order)))
1967 return retval;
1968 }
1969 else
1970 {
1971 coord_varid = dim->coord_var->hdr.id;
1972 found_coord = NC_TRUE;
1973 }
1974 dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, ++dim_index);
1975 }
1976
1977 /* Write each var. When we get to the coord var we are waiting
1978 * for (if any), then we break after writing it. */
1979 for (wrote_coord = NC_FALSE; var && !wrote_coord; )
1980 {
1981 if ((retval = write_var(var, grp, bad_coord_order)))
1982 return retval;
1983 if (found_coord && var->hdr.id == coord_varid)
1984 wrote_coord = NC_TRUE;
1985 var = (NC_VAR_INFO_T *)ncindexith(grp->vars, ++var_index);
1986 }
1987 } /* end while */
1988
1989 /* Attach dimscales to vars in this group. Unless directed not to. */
1990 if (!grp->nc4_info->no_dimscale_attach) {
1991 if ((retval = attach_dimscales(grp)))
1992 return retval;
1993 }
1994
1995 /* If there are any child groups, write their metadata. */
1996 for (size_t i = 0; i < ncindexsize(grp->children); i++)
1997 {
1998 child_grp = (NC_GRP_INFO_T *)ncindexith(grp->children, i);
1999 assert(child_grp);
2000 if ((retval = nc4_rec_write_metadata(child_grp, bad_coord_order)))
2001 return retval;
2002 }
2003 return NC_NOERR;
2004}
2005
2015int
2016nc4_rec_write_groups_types(NC_GRP_INFO_T *grp)
2017{
2018 NC_GRP_INFO_T *child_grp;
2019 NC_HDF5_GRP_INFO_T *hdf5_grp;
2020 NC_TYPE_INFO_T *type;
2021 int retval;
2022 size_t i;
2023
2024 assert(grp && grp->hdr.name && grp->format_grp_info);
2025 LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
2026
2027 /* Get HDF5-specific group info. */
2028 hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
2029
2030 /* Create the group in the HDF5 file if it doesn't exist. */
2031 if (!hdf5_grp->hdf_grpid)
2032 if ((retval = create_group(grp)))
2033 return retval;
2034
2035 /* If this is the root group of a file with strict NC3 rules, write
2036 * an attribute. But don't leave the attribute open. */
2037 if (!grp->parent && (grp->nc4_info->cmode & NC_CLASSIC_MODEL))
2038 if ((retval = write_nc3_strict_att(hdf5_grp->hdf_grpid)))
2039 return retval;
2040
2041 /* If there are any user-defined types, write them now. */
2042 for(i=0;i<ncindexsize(grp->type);i++) {
2043 type = (NC_TYPE_INFO_T *)ncindexith(grp->type, i);
2044 assert(type);
2045 if ((retval = commit_type(grp, type)))
2046 return retval;
2047 }
2048
2049 /* If there are any child groups, write their groups and types. */
2050 for(i=0;i<ncindexsize(grp->children);i++) {
2051 if((child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children,i)) == NULL) continue;
2052 if ((retval = nc4_rec_write_groups_types(child_grp)))
2053 return retval;
2054 }
2055 return NC_NOERR;
2056}
2057
2071int
2072nc4_rec_match_dimscales(NC_GRP_INFO_T *grp)
2073{
2074 NC_GRP_INFO_T *g;
2075 NC_VAR_INFO_T *var;
2076 NC_DIM_INFO_T *dim;
2077 int retval = NC_NOERR;
2078 size_t i;
2079
2080 assert(grp && grp->hdr.name);
2081 LOG((4, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
2082
2083 /* Perform var dimscale match for child groups. */
2084 for (i = 0; i < ncindexsize(grp->children); i++)
2085 {
2086 g = (NC_GRP_INFO_T*)ncindexith(grp->children, i);
2087 assert(g);
2088 if ((retval = nc4_rec_match_dimscales(g)))
2089 return retval;
2090 }
2091
2092 /* Check all the vars in this group. If they have dimscale info,
2093 * try and find a dimension for them. */
2094 for (i = 0; i < ncindexsize(grp->vars); i++)
2095 {
2096 NC_HDF5_VAR_INFO_T *hdf5_var;
2097 int d;
2098
2099 /* Get pointer to var and to the HDF5-specific var info. */
2100 var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i);
2101 assert(var && var->format_var_info);
2102 hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
2103
2104 /* Check all vars and see if dim[i] != NULL if dimids[i] valid. */
2105 /* This loop is very odd. Under normal circumstances, var->dimid[d] is zero
2106 (from the initial calloc) which is a legitimate dimid. The code does not
2107 distinguish this case from the dimscale case where the id might actually
2108 be defined.
2109 The original nc4_find_dim searched up the group tree looking for the given
2110 dimid in one of the dim lists associated with each ancestor group.
2111 I changed nc4_fnd_dim to use the dimid directly using h5->alldims.
2112 However, here that is incorrect because it will find the dimid 0 always
2113 (if any dimensions were defined). Except that when dimscale dimids have
2114 been defined, one or more of the values in var->dimids will have a
2115 legitimate value.
2116 The solution I choose is to modify nc4_var_list_add to initialize dimids to
2117 illegal values (-1). This is another example of the problems with dimscales.
2118 */
2119 const size_t ndims = var->ndims;
2120 for (size_t d = 0; d < ndims; d++)
2121 {
2122 if (var->dim[d] == NULL) {
2123 nc4_find_dim(grp, var->dimids[d], &var->dim[d], NULL);
2124 }
2125 /* assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d]); */
2126 }
2127
2128 /* Skip dimension scale variables */
2129 if (!hdf5_var->dimscale)
2130 {
2131 /* Are there dimscales for this variable? */
2132 if (hdf5_var->dimscale_hdf5_objids)
2133 {
2134 for (size_t d = 0; d < var->ndims; d++)
2135 {
2136 nc_bool_t finished = NC_FALSE;
2137 LOG((5, "%s: var %s has dimscale info...", __func__, var->hdr.name));
2138
2139 /* Check this and parent groups. */
2140 for (g = grp; g && !finished; g = g->parent)
2141 {
2142 /* Check all dims in this group. */
2143 for (size_t j = 0; j < ncindexsize(g->dim); j++)
2144 {
2145 /* Get the HDF5 specific dim info. */
2146 NC_HDF5_DIM_INFO_T *hdf5_dim;
2147 dim = (NC_DIM_INFO_T *)ncindexith(g->dim, j);
2148 assert(dim && dim->format_dim_info);
2149 hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
2150
2151 /* Check for exact match of fileno/objid arrays
2152 * to find identical objects in HDF5 file. */
2153#if H5_VERSION_GE(1,12,0)
2154 int token_cmp;
2155 if (H5Otoken_cmp(hdf5_var->hdf_datasetid, &hdf5_var->dimscale_hdf5_objids[d].token, &hdf5_dim->hdf5_objid.token, &token_cmp) < 0)
2156 return NC_EHDFERR;
2157
2158 if (hdf5_var->dimscale_hdf5_objids[d].fileno == hdf5_dim->hdf5_objid.fileno &&
2159 token_cmp == 0)
2160#else
2161 if (hdf5_var->dimscale_hdf5_objids[d].fileno[0] == hdf5_dim->hdf5_objid.fileno[0] &&
2162 hdf5_var->dimscale_hdf5_objids[d].objno[0] == hdf5_dim->hdf5_objid.objno[0] &&
2163 hdf5_var->dimscale_hdf5_objids[d].fileno[1] == hdf5_dim->hdf5_objid.fileno[1] &&
2164 hdf5_var->dimscale_hdf5_objids[d].objno[1] == hdf5_dim->hdf5_objid.objno[1])
2165#endif
2166 {
2167 LOG((4, "%s: for dimension %d, found dim %s", __func__,
2168 d, dim->hdr.name));
2169 var->dimids[d] = dim->hdr.id;
2170 var->dim[d] = dim;
2171 finished = NC_TRUE;
2172 break;
2173 }
2174 } /* next dim */
2175 } /* next grp */
2176 LOG((5, "%s: dimid for this dimscale is %d", __func__,
2177 var->type_info->hdr.id));
2178 } /* next var->dim */
2179 }
2180 /* No dimscales for this var! Invent phony dimensions. */
2181 else
2182 {
2183 hid_t spaceid = 0;
2184 hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL;
2185 int dataset_ndims;
2186
2187 /* Find the space information for this dimension. */
2188 if ((spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0)
2189 return NC_EHDFERR;
2190
2191 /* Get the len of each dim in the space. */
2192 if (var->ndims)
2193 {
2194 if (!(h5dimlen = malloc(var->ndims * sizeof(hsize_t))))
2195 return NC_ENOMEM;
2196 if (!(h5dimlenmax = malloc(var->ndims * sizeof(hsize_t))))
2197 {
2198 free(h5dimlen);
2199 return NC_ENOMEM;
2200 }
2201 if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, h5dimlen,
2202 h5dimlenmax)) < 0) {
2203 free(h5dimlenmax);
2204 free(h5dimlen);
2205 return NC_EHDFERR;
2206 }
2207 if (dataset_ndims != var->ndims) {
2208 free(h5dimlenmax);
2209 free(h5dimlen);
2210 return NC_EHDFERR;
2211 }
2212 }
2213 else
2214 {
2215 /* Make sure it's scalar. */
2216 if (H5Sget_simple_extent_type(spaceid) != H5S_SCALAR)
2217 return NC_EHDFERR;
2218 }
2219
2220 /* Release the space object. */
2221 if (H5Sclose(spaceid) < 0) {
2222 free(h5dimlen);
2223 free(h5dimlenmax);
2224 return NC_EHDFERR;
2225 }
2226
2227 /* Create a phony dimension for each dimension in the
2228 * dataset, unless there already is one the correct
2229 * size. */
2230 for (d = 0; d < var->ndims; d++)
2231 {
2232 nc_bool_t match = NC_FALSE;
2233 /* Is there already a phony dimension of the correct size? */
2234 for(size_t k=0;k<ncindexsize(grp->dim);k++) {
2235 if((dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,k)) == NULL) continue;
2236 if ((dim->len == h5dimlen[d]) &&
2237 ((h5dimlenmax[d] == H5S_UNLIMITED && dim->unlimited) ||
2238 (h5dimlenmax[d] != H5S_UNLIMITED && !dim->unlimited)))
2239 {match = NC_TRUE; break;}
2240 }
2241
2242 /* Didn't find a phony dim? Then create one. */
2243 if (match == NC_FALSE)
2244 {
2245 char phony_dim_name[NC_MAX_NAME + 1];
2246 snprintf(phony_dim_name, sizeof(phony_dim_name), "phony_dim_%d", grp->nc4_info->next_dimid);
2247 LOG((3, "%s: creating phony dim for var %s", __func__, var->hdr.name));
2248 if ((retval = nc4_dim_list_add(grp, phony_dim_name, h5dimlen[d], -1, &dim)))
2249 {
2250 free(h5dimlenmax);
2251 free(h5dimlen);
2252 return retval;
2253 }
2254 /* Create struct for HDF5-specific dim info. */
2255 if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T))))
2256 return NC_ENOMEM;
2257 if (h5dimlenmax[d] == H5S_UNLIMITED)
2258 dim->unlimited = NC_TRUE;
2259 }
2260
2261 /* The variable must remember the dimid. */
2262 var->dimids[d] = dim->hdr.id;
2263 var->dim[d] = dim;
2264 } /* next dim */
2265
2266 /* Free the memory we malloced. */
2267 free(h5dimlen);
2268 free(h5dimlenmax);
2269 }
2270 }
2271 }
2272
2273 return retval;
2274}
2275
2287void
2288reportobject(int uselog, hid_t id, unsigned int type)
2289{
2290 char name[NC_HDF5_MAX_NAME];
2291 ssize_t len;
2292 const char* typename = NULL;
2293 long long printid = (long long)id;
2294
2295 len = H5Iget_name(id, name, NC_HDF5_MAX_NAME);
2296 if(len < 0) return;
2297 name[len] = '\0';
2298
2299 switch (type) {
2300 case H5F_OBJ_FILE: typename = "File"; break;
2301 case H5F_OBJ_DATASET: typename = "Dataset"; break;
2302 case H5F_OBJ_GROUP: typename = "Group"; break;
2303 case H5F_OBJ_DATATYPE: typename = "Datatype"; break;
2304 case H5F_OBJ_ATTR:
2305 typename = "Attribute";
2306 len = H5Aget_name(id, NC_HDF5_MAX_NAME, name);
2307 if(len < 0) len = 0;
2308 name[len] = '\0';
2309 break;
2310 default: typename = "<unknown>"; break;
2311 }
2312#ifdef LOGGING
2313 if(uselog) {
2314 LOG((0,"Type = %s(%lld) name='%s'",typename,printid,name));
2315 } else
2316#endif
2317 {
2318 fprintf(stderr,"Type = %s(%lld) name='%s'",typename,printid,name);
2319 }
2320
2321}
2322
2333static void
2334reportopenobjectsT(int uselog, hid_t fid, int ntypes, unsigned int* otypes)
2335{
2336 int t,i;
2337 ssize_t ocount;
2338 hid_t* idlist = NULL;
2339
2340 /* Always report somehow */
2341#ifdef LOGGING
2342 if(uselog)
2343 LOG((0,"\nReport: open objects on %lld",(long long)fid));
2344 else
2345#endif
2346 fprintf(stdout,"\nReport: open objects on %lld\n",(long long)fid);
2347 size_t maxobjs = (size_t)H5Fget_obj_count(fid,H5F_OBJ_ALL);
2348 if(idlist != NULL) free(idlist);
2349 idlist = (hid_t*)malloc(sizeof(hid_t)*maxobjs);
2350 for(t=0;t<ntypes;t++) {
2351 unsigned int ot = otypes[t];
2352 ocount = H5Fget_obj_ids(fid,ot,maxobjs,idlist);
2353 for(i=0;i<ocount;i++) {
2354 hid_t o = idlist[i];
2355 reportobject(uselog,o,ot);
2356 }
2357 }
2358 if(idlist != NULL) free(idlist);
2359}
2360
2369void
2370reportopenobjects(int uselog, hid_t fid)
2371{
2372 unsigned int OTYPES[5] = {H5F_OBJ_FILE, H5F_OBJ_DATASET, H5F_OBJ_GROUP,
2373 H5F_OBJ_DATATYPE, H5F_OBJ_ATTR};
2374
2375 reportopenobjectsT(uselog, fid ,5, OTYPES);
2376}
2377
2385void
2386showopenobjects5(NC_FILE_INFO_T* h5)
2387{
2388 NC_HDF5_FILE_INFO_T *hdf5_info;
2389
2390 assert(h5 && h5->format_file_info);
2391 hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
2392
2393 fprintf(stderr,"===== begin showopenobjects =====\n");
2394 reportopenobjects(0,hdf5_info->hdfid);
2395 fprintf(stderr,"===== end showopenobjects =====\n");
2396 fflush(stderr);
2397}
2398
2407void
2408showopenobjects(int ncid)
2409{
2410 NC_FILE_INFO_T* h5 = NULL;
2411
2412 /* Find our metadata for this file. */
2413 if (nc4_find_nc_grp_h5(ncid, NULL, NULL, &h5) != NC_NOERR)
2414 fprintf(stderr,"failed\n");
2415 else
2416 showopenobjects5(h5);
2417 fflush(stderr);
2418}
2419
2431int
2432NC4_hdf5get_libversion(unsigned* major,unsigned* minor,unsigned* release)
2433{
2434 if(H5get_libversion(major,minor,release) < 0)
2435 return NC_EHDFERR;
2436 return NC_NOERR;
2437}
2438
2449int
2450NC4_hdf5get_superblock(struct NC_FILE_INFO* h5, int* idp)
2451{
2452 NC_HDF5_FILE_INFO_T *hdf5_info;
2453 int stat = NC_NOERR;
2454 unsigned super;
2455 hid_t plist = -1;
2456
2457 assert(h5 && h5->format_file_info);
2458 hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
2459
2460 if((plist = H5Fget_create_plist(hdf5_info->hdfid)) < 0)
2461 {stat = NC_EHDFERR; goto done;}
2462 if(H5Pget_version(plist, &super, NULL, NULL, NULL) < 0)
2463 {stat = NC_EHDFERR; goto done;}
2464 if(idp) *idp = (int)super;
2465done:
2466 if(plist >= 0) H5Pclose(plist);
2467 return stat;
2468}
2469
2470static int NC4_root_att_exists(NC_FILE_INFO_T*, const char* aname);
2471static int NC4_walk(hid_t, int*);
2472
2498int
2499NC4_isnetcdf4(struct NC_FILE_INFO* h5)
2500{
2501 int stat;
2502 int isnc4 = 0;
2503 int exists;
2504 int count;
2505
2506 /* Look for NC3_STRICT_ATT_NAME */
2507 exists = NC4_root_att_exists(h5,NC3_STRICT_ATT_NAME);
2508 if(exists)
2509 {isnc4 = 1; goto done;}
2510 /* Look for _NCProperties */
2511 exists = NC4_root_att_exists(h5,NCPROPS);
2512 if(exists)
2513 {isnc4 = 1; goto done;}
2514 /* attribute did not exist */
2515 /* => last resort: walk the HDF5 file looking for markers */
2516 count = 0;
2517 stat = NC4_walk(((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid,
2518 &count);
2519 if(stat != NC_NOERR)
2520 isnc4 = 0;
2521 else /* Threshold is at least two matches */
2522 isnc4 = (count >= 2);
2523
2524done:
2525 return isnc4;
2526}
2527
2537static int
2538NC4_root_att_exists(NC_FILE_INFO_T *h5, const char* aname)
2539{
2540 hid_t grpid = -1;
2541 htri_t attr_exists;
2542
2543 /* Get root group ID. */
2544 grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid;
2545
2546 /* See if the NC3_STRICT_ATT_NAME attribute exists */
2547 if ((attr_exists = H5Aexists(grpid, aname))<0)
2548 return -1;
2549 return (attr_exists?1:0);
2550}
2551
2561static int
2562NC4_walk(hid_t gid, int* countp)
2563{
2564 int ncstat = NC_NOERR;
2565 int j,na;
2566 ssize_t len;
2567 hsize_t nobj;
2568 herr_t err;
2569 int otype;
2570 hid_t grpid, dsid;
2571 char name[NC_HDF5_MAX_NAME];
2572
2573 /* walk group members of interest */
2574 err = H5Gget_num_objs(gid, &nobj);
2575 if(err < 0) return err;
2576
2577 for(hsize_t i = 0; i < nobj; i++) {
2578 /* Get name & kind of object in the group */
2579 len = H5Gget_objname_by_idx(gid,i,name,(size_t)NC_HDF5_MAX_NAME);
2580 if(len < 0) return (int)len;
2581
2582 otype = H5Gget_objtype_by_idx(gid, i);
2583 switch(otype) {
2584 case H5G_GROUP:
2585 grpid = H5Gopen1(gid,name);
2586 NC4_walk(grpid,countp);
2587 H5Gclose(grpid);
2588 break;
2589 case H5G_DATASET: /* variables */
2590 /* Check for phony_dim */
2591 if(strcmp(name,"phony_dim")==0)
2592 *countp = *countp + 1;
2593 dsid = H5Dopen1(gid,name);
2594 na = H5Aget_num_attrs(dsid);
2595 for(j = 0; j < na; j++) {
2596 hid_t aid = H5Aopen_idx(dsid,(unsigned int) j);
2597 if(aid >= 0) {
2598 const NC_reservedatt* ra;
2599 ssize_t len = H5Aget_name(aid, NC_HDF5_MAX_NAME, name);
2600 if(len < 0) return (int)len;
2601 /* Is this a netcdf-4 marker attribute */
2602 ra = NC_findreserved(name);
2603 if(ra != NULL)
2604 *countp = *countp + 1;
2605 }
2606 H5Aclose(aid);
2607 }
2608 H5Dclose(dsid);
2609 break;
2610 default:/* ignore */
2611 break;
2612 }
2613 }
2614 return ncstat;
2615}
2616
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: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:850