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