/src/gdal/netcdf-c-4.7.4/libhdf5/nc4hdf.c
Line | Count | Source |
1 | | /* Copyright 2018, University Corporation for Atmospheric |
2 | | * Research. See the COPYRIGHT file for copying and redistribution |
3 | | * conditions. */ |
4 | | /** |
5 | | * @file |
6 | | * @internal This file is part of netcdf-4, a netCDF-like interface |
7 | | * for HDF5, or a HDF5 backend for netCDF, depending on your point of |
8 | | * view. |
9 | | * |
10 | | * This file contains functions internal to the netcdf4 library. None of |
11 | | * the functions in this file are exposed in the exetnal API. These |
12 | | * functions handle the HDF interface. |
13 | | * |
14 | | * @author Ed Hartnett, Dennis Heimbigner, Ward Fisher |
15 | | */ |
16 | | |
17 | | #include "config.h" |
18 | | #include "netcdf.h" |
19 | | #include "nc4internal.h" |
20 | | #include "ncdispatch.h" |
21 | | #include "hdf5internal.h" |
22 | | #include <math.h> |
23 | | |
24 | | #ifdef HAVE_INTTYPES_H |
25 | | #define __STDC_FORMAT_MACROS |
26 | | #include <inttypes.h> |
27 | | #endif |
28 | | |
29 | 0 | #define NC_HDF5_MAX_NAME 1024 /**< @internal Max size of HDF5 name. */ |
30 | | |
31 | | /** |
32 | | * @internal Flag attributes in a linked list as dirty. |
33 | | * |
34 | | * @param attlist List of attributes, may be NULL. |
35 | | * |
36 | | * @return NC_NOERR No error. |
37 | | * @author Dennis Heimbigner |
38 | | */ |
39 | | static int |
40 | 0 | flag_atts_dirty(NCindex *attlist) { |
41 | |
|
42 | 0 | NC_ATT_INFO_T *att = NULL; |
43 | 0 | int i; |
44 | |
|
45 | 0 | if(attlist == NULL) { |
46 | 0 | return NC_NOERR; |
47 | 0 | } |
48 | | |
49 | 0 | for(i=0;i<ncindexsize(attlist);i++) { |
50 | 0 | att = (NC_ATT_INFO_T*)ncindexith(attlist,i); |
51 | 0 | if(att == NULL) continue; |
52 | 0 | att->dirty = NC_TRUE; |
53 | 0 | } |
54 | |
|
55 | 0 | return NC_NOERR; |
56 | 0 | } |
57 | | |
58 | | /** |
59 | | * @internal This function is needed to handle one special case: what |
60 | | * if the user defines a dim, writes metadata, then goes back into |
61 | | * define mode and adds a coordinate var for the already existing |
62 | | * dim. In that case, I need to recreate the dim's dimension scale |
63 | | * dataset, and then I need to go to every var in the file which uses |
64 | | * that dimension, and attach the new dimension scale. |
65 | | * |
66 | | * @param grp Pointer to group info struct. |
67 | | * @param dimid Dimension ID. |
68 | | * @param dimscaleid HDF5 dimension scale ID. |
69 | | * |
70 | | * @returns NC_NOERR No error. |
71 | | * @returns NC_EHDFERR HDF5 returned an error. |
72 | | * @author Ed Hartnett |
73 | | */ |
74 | | int |
75 | | rec_reattach_scales(NC_GRP_INFO_T *grp, int dimid, hid_t dimscaleid) |
76 | 0 | { |
77 | 0 | NC_VAR_INFO_T *var; |
78 | 0 | NC_GRP_INFO_T *child_grp; |
79 | 0 | int d, i; |
80 | 0 | int retval; |
81 | |
|
82 | 0 | assert(grp && grp->hdr.name && dimid >= 0 && dimscaleid >= 0); |
83 | 0 | LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name)); |
84 | | |
85 | | /* If there are any child groups, attach dimscale there, if needed. */ |
86 | 0 | for (i = 0; i < ncindexsize(grp->children); i++) |
87 | 0 | { |
88 | 0 | child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children, i); |
89 | 0 | assert(child_grp); |
90 | 0 | if ((retval = rec_reattach_scales(child_grp, dimid, dimscaleid))) |
91 | 0 | return retval; |
92 | 0 | } |
93 | | |
94 | | /* Find any vars that use this dimension id. */ |
95 | 0 | for (i = 0; i < ncindexsize(grp->vars); i++) |
96 | 0 | { |
97 | 0 | NC_HDF5_VAR_INFO_T *hdf5_var; |
98 | |
|
99 | 0 | var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i); |
100 | 0 | assert(var && var->format_var_info); |
101 | 0 | hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; |
102 | |
|
103 | 0 | for (d = 0; d < var->ndims; d++) |
104 | 0 | { |
105 | 0 | if (var->dimids[d] == dimid && !var->dimscale) |
106 | 0 | { |
107 | 0 | LOG((2, "%s: attaching scale for dimid %d to var %s", |
108 | 0 | __func__, var->dimids[d], var->hdr.name)); |
109 | 0 | if (var->created) |
110 | 0 | { |
111 | 0 | if (H5DSattach_scale(hdf5_var->hdf_datasetid, |
112 | 0 | dimscaleid, d) < 0) |
113 | 0 | return NC_EHDFERR; |
114 | 0 | var->dimscale_attached[d] = NC_TRUE; |
115 | 0 | } |
116 | 0 | } |
117 | 0 | } |
118 | 0 | } |
119 | 0 | return NC_NOERR; |
120 | 0 | } |
121 | | |
122 | | /** |
123 | | * @internal This function is needed to handle one special case: what |
124 | | * if the user defines a dim, writes metadata, then goes back into |
125 | | * define mode and adds a coordinate var for the already existing |
126 | | * dim. In that case, I need to recreate the dim's dimension scale |
127 | | * dataset, and then I need to go to every var in the file which uses |
128 | | * that dimension, and attach the new dimension scale. |
129 | | * |
130 | | * @param grp Pointer to group info struct. |
131 | | * @param dimid Dimension ID. |
132 | | * @param dimscaleid HDF5 dimension scale ID. |
133 | | * |
134 | | * @returns NC_NOERR No error. |
135 | | * @returns NC_EHDFERR HDF5 returned an error. |
136 | | * @author Ed Hartnett |
137 | | */ |
138 | | int |
139 | | rec_detach_scales(NC_GRP_INFO_T *grp, int dimid, hid_t dimscaleid) |
140 | 0 | { |
141 | 0 | NC_VAR_INFO_T *var; |
142 | 0 | NC_GRP_INFO_T *child_grp; |
143 | 0 | int d, i; |
144 | 0 | int retval; |
145 | |
|
146 | 0 | assert(grp && grp->hdr.name && dimid >= 0 && dimscaleid >= 0); |
147 | 0 | LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name)); |
148 | | |
149 | | /* If there are any child groups, detach dimscale there, if needed. */ |
150 | 0 | for(i=0;i<ncindexsize(grp->children);i++) { |
151 | 0 | child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children,i); |
152 | 0 | if(child_grp == NULL) continue; |
153 | 0 | if ((retval = rec_detach_scales(child_grp, dimid, dimscaleid))) |
154 | 0 | return retval; |
155 | 0 | } |
156 | | |
157 | | /* Find any vars that use this dimension id. */ |
158 | 0 | for (i = 0; i < ncindexsize(grp->vars); i++) |
159 | 0 | { |
160 | 0 | NC_HDF5_VAR_INFO_T *hdf5_var; |
161 | 0 | var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i); |
162 | 0 | assert(var && var->format_var_info); |
163 | 0 | hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; |
164 | |
|
165 | 0 | for (d = 0; d < var->ndims; d++) |
166 | 0 | { |
167 | 0 | if (var->dimids[d] == dimid && !var->dimscale) |
168 | 0 | { |
169 | 0 | LOG((2, "%s: detaching scale for dimid %d to var %s", |
170 | 0 | __func__, var->dimids[d], var->hdr.name)); |
171 | 0 | if (var->created) |
172 | 0 | { |
173 | 0 | if (var->dimscale_attached && var->dimscale_attached[d]) |
174 | 0 | { |
175 | 0 | if (H5DSdetach_scale(hdf5_var->hdf_datasetid, |
176 | 0 | dimscaleid, d) < 0) |
177 | 0 | return NC_EHDFERR; |
178 | 0 | var->dimscale_attached[d] = NC_FALSE; |
179 | 0 | } |
180 | 0 | } |
181 | 0 | } |
182 | 0 | } |
183 | 0 | } |
184 | 0 | return NC_NOERR; |
185 | 0 | } |
186 | | |
187 | | /** |
188 | | * @internal Open a HDF5 dataset and leave it open. |
189 | | * |
190 | | * @param grp Pointer to group info struct. |
191 | | * @param varid Variable ID. |
192 | | * @param dataset Pointer that gets the HDF5 dataset ID. |
193 | | * |
194 | | * @returns NC_NOERR No error. |
195 | | * @returns NC_EHDFERR HDF5 returned an error. |
196 | | * @author Ed Hartnett |
197 | | */ |
198 | | int |
199 | | nc4_open_var_grp2(NC_GRP_INFO_T *grp, int varid, hid_t *dataset) |
200 | 1.00M | { |
201 | 1.00M | NC_VAR_INFO_T *var; |
202 | 1.00M | NC_HDF5_VAR_INFO_T *hdf5_var; |
203 | | |
204 | 1.00M | assert(grp && grp->format_grp_info && dataset); |
205 | | |
206 | | /* Find the requested varid. */ |
207 | 1.00M | if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid))) |
208 | 0 | return NC_ENOTVAR; |
209 | 1.00M | assert(var && var->hdr.id == varid && var->format_var_info); |
210 | 1.00M | hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; |
211 | | |
212 | | /* Open this dataset if necessary. */ |
213 | 1.00M | if (!hdf5_var->hdf_datasetid) |
214 | 0 | { |
215 | 0 | NC_HDF5_GRP_INFO_T *hdf5_grp; |
216 | 0 | hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; |
217 | |
|
218 | 0 | if ((hdf5_var->hdf_datasetid = H5Dopen2(hdf5_grp->hdf_grpid, |
219 | 0 | var->hdr.name, H5P_DEFAULT)) < 0) |
220 | 0 | return NC_ENOTVAR; |
221 | 0 | } |
222 | | |
223 | 1.00M | *dataset = hdf5_var->hdf_datasetid; |
224 | | |
225 | 1.00M | return NC_NOERR; |
226 | 1.00M | } |
227 | | |
228 | | /** |
229 | | * @internal What fill value should be used for a variable? |
230 | | * |
231 | | * @param h5 Pointer to HDF5 file info struct. |
232 | | * @param var Pointer to variable info struct. |
233 | | * @param fillp Pointer that gets pointer to fill value. |
234 | | * |
235 | | * @returns NC_NOERR No error. |
236 | | * @returns NC_ENOMEM Out of memory. |
237 | | * @author Ed Hartnett |
238 | | */ |
239 | | int |
240 | | nc4_get_fill_value(NC_FILE_INFO_T *h5, NC_VAR_INFO_T *var, void **fillp) |
241 | 70.6k | { |
242 | 70.6k | size_t size; |
243 | 70.6k | int retval; |
244 | | |
245 | | /* Find out how much space we need for this type's fill value. */ |
246 | 70.6k | if (var->type_info->nc_type_class == NC_VLEN) |
247 | 0 | size = sizeof(nc_vlen_t); |
248 | 70.6k | else if (var->type_info->nc_type_class == NC_STRING) |
249 | 0 | size = sizeof(char *); |
250 | 70.6k | else |
251 | 70.6k | { |
252 | 70.6k | if ((retval = nc4_get_typelen_mem(h5, var->type_info->hdr.id, &size))) |
253 | 0 | return retval; |
254 | 70.6k | } |
255 | 70.6k | assert(size); |
256 | | |
257 | | /* Allocate the space. */ |
258 | 70.6k | if (!((*fillp) = calloc(1, size))) |
259 | 0 | return NC_ENOMEM; |
260 | | |
261 | | /* If the user has set a fill_value for this var, use, otherwise |
262 | | * find the default fill value. */ |
263 | 70.6k | if (var->fill_value) |
264 | 464 | { |
265 | 464 | LOG((4, "Found a fill value for var %s", var->hdr.name)); |
266 | 464 | if (var->type_info->nc_type_class == NC_VLEN) |
267 | 0 | { |
268 | 0 | nc_vlen_t *in_vlen = (nc_vlen_t *)(var->fill_value), *fv_vlen = (nc_vlen_t *)(*fillp); |
269 | 0 | size_t basetypesize = 0; |
270 | |
|
271 | 0 | if((retval=nc4_get_typelen_mem(h5, var->type_info->u.v.base_nc_typeid, &basetypesize))) |
272 | 0 | return retval; |
273 | | |
274 | 0 | fv_vlen->len = in_vlen->len; |
275 | 0 | if (!(fv_vlen->p = malloc(basetypesize * in_vlen->len))) |
276 | 0 | { |
277 | 0 | free(*fillp); |
278 | 0 | *fillp = NULL; |
279 | 0 | return NC_ENOMEM; |
280 | 0 | } |
281 | 0 | memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * basetypesize); |
282 | 0 | } |
283 | 464 | else if (var->type_info->nc_type_class == NC_STRING) |
284 | 0 | { |
285 | 0 | if (*(char **)var->fill_value) |
286 | 0 | if (!(**(char ***)fillp = strdup(*(char **)var->fill_value))) |
287 | 0 | { |
288 | 0 | free(*fillp); |
289 | 0 | *fillp = NULL; |
290 | 0 | return NC_ENOMEM; |
291 | 0 | } |
292 | 0 | } |
293 | 464 | else |
294 | 464 | memcpy((*fillp), var->fill_value, size); |
295 | 464 | } |
296 | 70.2k | else |
297 | 70.2k | { |
298 | 70.2k | if (nc4_get_default_fill_value(var->type_info, *fillp)) |
299 | 0 | { |
300 | | /* Note: release memory, but don't return error on failure */ |
301 | 0 | free(*fillp); |
302 | 0 | *fillp = NULL; |
303 | 0 | } |
304 | 70.2k | } |
305 | | |
306 | 70.6k | return NC_NOERR; |
307 | 70.6k | } |
308 | | |
309 | | /** |
310 | | * @internal Given a netcdf type, return appropriate HDF typeid. (All |
311 | | * hdf_typeid's returned from this routine must be H5Tclosed by the |
312 | | * caller). |
313 | | * |
314 | | * @param h5 Pointer to HDF5 file info struct. |
315 | | * @param xtype NetCDF type ID. |
316 | | * @param hdf_typeid Pointer that gets the HDF5 type ID. |
317 | | * @param endianness Desired endianness in HDF5 type. |
318 | | * |
319 | | * @return NC_NOERR No error. |
320 | | * @return NC_ECHAR Conversions of NC_CHAR forbidden. |
321 | | * @return NC_EVARMETA HDF5 returning error creating datatype. |
322 | | * @return NC_EHDFERR HDF5 returning error. |
323 | | * @return NC_EBADTYE Type not found. |
324 | | * @author Ed Hartnett |
325 | | */ |
326 | | int |
327 | | nc4_get_hdf_typeid(NC_FILE_INFO_T *h5, nc_type xtype, |
328 | | hid_t *hdf_typeid, int endianness) |
329 | 276k | { |
330 | 276k | NC_TYPE_INFO_T *type; |
331 | 276k | hid_t typeid = 0; |
332 | 276k | int retval = NC_NOERR; |
333 | | |
334 | 276k | assert(hdf_typeid && h5); |
335 | | |
336 | 276k | *hdf_typeid = -1; |
337 | | |
338 | | /* Determine an appropriate HDF5 datatype */ |
339 | 276k | if (xtype == NC_NAT) |
340 | 0 | return NC_EBADTYPE; |
341 | 276k | else if (xtype == NC_CHAR || xtype == NC_STRING) |
342 | 44.2k | { |
343 | | /* NC_CHAR & NC_STRING types create a new HDF5 datatype */ |
344 | 44.2k | if (xtype == NC_CHAR) |
345 | 44.2k | { |
346 | 44.2k | if ((typeid = H5Tcopy(H5T_C_S1)) < 0) |
347 | 0 | return NC_EHDFERR; |
348 | 44.2k | if (H5Tset_strpad(typeid, H5T_STR_NULLTERM) < 0) |
349 | 0 | BAIL(NC_EVARMETA); |
350 | 44.2k | if(H5Tset_cset(typeid, H5T_CSET_ASCII) < 0) |
351 | 0 | BAIL(NC_EVARMETA); |
352 | | |
353 | | /* Take ownership of the newly created HDF5 datatype */ |
354 | 44.2k | *hdf_typeid = typeid; |
355 | 44.2k | typeid = 0; |
356 | 44.2k | } |
357 | 0 | else |
358 | 0 | { |
359 | 0 | if ((typeid = H5Tcopy(H5T_C_S1)) < 0) |
360 | 0 | return NC_EHDFERR; |
361 | 0 | if (H5Tset_size(typeid, H5T_VARIABLE) < 0) |
362 | 0 | BAIL(NC_EVARMETA); |
363 | 0 | if(H5Tset_cset(typeid, H5T_CSET_UTF8) < 0) |
364 | 0 | BAIL(NC_EVARMETA); |
365 | | |
366 | | /* Take ownership of the newly created HDF5 datatype */ |
367 | 0 | *hdf_typeid = typeid; |
368 | 0 | typeid = 0; |
369 | 0 | } |
370 | 44.2k | } |
371 | 232k | else |
372 | 232k | { |
373 | | /* All other types use an existing HDF5 datatype */ |
374 | 232k | switch (xtype) |
375 | 232k | { |
376 | 185k | case NC_BYTE: /* signed 1 byte integer */ |
377 | 185k | if (endianness == NC_ENDIAN_LITTLE) |
378 | 0 | typeid = H5T_STD_I8LE; |
379 | 185k | else if (endianness == NC_ENDIAN_BIG) |
380 | 0 | typeid = H5T_STD_I8BE; |
381 | 185k | else |
382 | 185k | typeid = H5T_NATIVE_SCHAR; |
383 | 185k | break; |
384 | | |
385 | 3.47k | case NC_SHORT: /* signed 2 byte integer */ |
386 | 3.47k | if (endianness == NC_ENDIAN_LITTLE) |
387 | 0 | typeid = H5T_STD_I16LE; |
388 | 3.47k | else if (endianness == NC_ENDIAN_BIG) |
389 | 0 | typeid = H5T_STD_I16BE; |
390 | 3.47k | else |
391 | 3.47k | typeid = H5T_NATIVE_SHORT; |
392 | 3.47k | break; |
393 | | |
394 | 30.1k | case NC_INT: |
395 | 30.1k | if (endianness == NC_ENDIAN_LITTLE) |
396 | 0 | typeid = H5T_STD_I32LE; |
397 | 30.1k | else if (endianness == NC_ENDIAN_BIG) |
398 | 0 | typeid = H5T_STD_I32BE; |
399 | 30.1k | else |
400 | 30.1k | typeid = H5T_NATIVE_INT; |
401 | 30.1k | break; |
402 | | |
403 | 348 | case NC_UBYTE: |
404 | 348 | if (endianness == NC_ENDIAN_LITTLE) |
405 | 0 | typeid = H5T_STD_U8LE; |
406 | 348 | else if (endianness == NC_ENDIAN_BIG) |
407 | 0 | typeid = H5T_STD_U8BE; |
408 | 348 | else |
409 | 348 | typeid = H5T_NATIVE_UCHAR; |
410 | 348 | break; |
411 | | |
412 | 93 | case NC_USHORT: |
413 | 93 | if (endianness == NC_ENDIAN_LITTLE) |
414 | 0 | typeid = H5T_STD_U16LE; |
415 | 93 | else if (endianness == NC_ENDIAN_BIG) |
416 | 0 | typeid = H5T_STD_U16BE; |
417 | 93 | else |
418 | 93 | typeid = H5T_NATIVE_USHORT; |
419 | 93 | break; |
420 | | |
421 | 1.56k | case NC_UINT: |
422 | 1.56k | if (endianness == NC_ENDIAN_LITTLE) |
423 | 0 | typeid = H5T_STD_U32LE; |
424 | 1.56k | else if (endianness == NC_ENDIAN_BIG) |
425 | 0 | typeid = H5T_STD_U32BE; |
426 | 1.56k | else |
427 | 1.56k | typeid = H5T_NATIVE_UINT; |
428 | 1.56k | break; |
429 | | |
430 | 837 | case NC_INT64: |
431 | 837 | if (endianness == NC_ENDIAN_LITTLE) |
432 | 0 | typeid = H5T_STD_I64LE; |
433 | 837 | else if (endianness == NC_ENDIAN_BIG) |
434 | 0 | typeid = H5T_STD_I64BE; |
435 | 837 | else |
436 | 837 | typeid = H5T_NATIVE_LLONG; |
437 | 837 | break; |
438 | | |
439 | 9 | case NC_UINT64: |
440 | 9 | if (endianness == NC_ENDIAN_LITTLE) |
441 | 0 | typeid = H5T_STD_U64LE; |
442 | 9 | else if (endianness == NC_ENDIAN_BIG) |
443 | 0 | typeid = H5T_STD_U64BE; |
444 | 9 | else |
445 | 9 | typeid = H5T_NATIVE_ULLONG; |
446 | 9 | break; |
447 | | |
448 | 2.23k | case NC_FLOAT: |
449 | 2.23k | if (endianness == NC_ENDIAN_LITTLE) |
450 | 0 | typeid = H5T_IEEE_F32LE; |
451 | 2.23k | else if (endianness == NC_ENDIAN_BIG) |
452 | 0 | typeid = H5T_IEEE_F32BE; |
453 | 2.23k | else |
454 | 2.23k | typeid = H5T_NATIVE_FLOAT; |
455 | 2.23k | break; |
456 | | |
457 | 8.44k | case NC_DOUBLE: |
458 | 8.44k | if (endianness == NC_ENDIAN_LITTLE) |
459 | 0 | typeid = H5T_IEEE_F64LE; |
460 | 8.44k | else if (endianness == NC_ENDIAN_BIG) |
461 | 0 | typeid = H5T_IEEE_F64BE; |
462 | 8.44k | else |
463 | 8.44k | typeid = H5T_NATIVE_DOUBLE; |
464 | 8.44k | break; |
465 | | |
466 | 0 | default: |
467 | | /* Maybe this is a user defined type? */ |
468 | 0 | if (nc4_find_type(h5, xtype, &type)) |
469 | 0 | return NC_EBADTYPE; |
470 | 0 | if (!type) |
471 | 0 | return NC_EBADTYPE; |
472 | 0 | typeid = ((NC_HDF5_TYPE_INFO_T *)type->format_type_info)->hdf_typeid; |
473 | 0 | break; |
474 | 232k | } |
475 | 232k | assert(typeid); |
476 | | |
477 | | /* Copy the HDF5 datatype, so the function operates uniformly */ |
478 | 232k | if ((*hdf_typeid = H5Tcopy(typeid)) < 0) |
479 | 0 | return NC_EHDFERR; |
480 | 232k | typeid = 0; |
481 | 232k | } |
482 | 276k | assert(*hdf_typeid != -1); |
483 | | |
484 | 276k | exit: |
485 | 276k | if (typeid > 0 && H5Tclose(typeid) < 0) |
486 | 0 | BAIL2(NC_EHDFERR); |
487 | 276k | return retval; |
488 | 276k | } |
489 | | |
490 | | /** |
491 | | * @internal Write an attribute. |
492 | | * |
493 | | * @param grp Pointer to group info struct. |
494 | | * @param varid Variable ID or NC_GLOBAL. |
495 | | * @param att Pointer to attribute info struct. |
496 | | * |
497 | | * @returns ::NC_NOERR No error. |
498 | | * @returns ::NC_ENOTVAR Variable not found. |
499 | | * @returns ::NC_EPERM Read-only file. |
500 | | * @returns ::NC_EHDFERR HDF5 returned error. |
501 | | * @returns ::NC_EATTMETA HDF5 returned error with attribute calls. |
502 | | * @author Ed Hartnett |
503 | | */ |
504 | | static int |
505 | | put_att_grpa(NC_GRP_INFO_T *grp, int varid, NC_ATT_INFO_T *att) |
506 | 65.5k | { |
507 | 65.5k | NC_HDF5_GRP_INFO_T *hdf5_grp; |
508 | 65.5k | hid_t datasetid = 0, locid; |
509 | 65.5k | hid_t attid = 0, spaceid = 0, file_typeid = 0; |
510 | 65.5k | hid_t existing_att_typeid = 0, existing_attid = 0, existing_spaceid = 0; |
511 | 65.5k | hsize_t dims[1]; /* netcdf attributes always 1-D. */ |
512 | 65.5k | htri_t attr_exists; |
513 | 65.5k | void *data; |
514 | 65.5k | int phoney_data = 99; |
515 | 65.5k | int retval = NC_NOERR; |
516 | | |
517 | 65.5k | assert(att->hdr.name && grp && grp->format_grp_info); |
518 | 65.5k | LOG((3, "%s: varid %d att->hdr.id %d att->hdr.name %s att->nc_typeid %d " |
519 | 65.5k | "att->len %d", __func__, varid, att->hdr.id, att->hdr.name, |
520 | 65.5k | att->nc_typeid, att->len)); |
521 | | |
522 | | /* Get HDF5-specific group info. */ |
523 | 65.5k | hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; |
524 | | |
525 | | /* If the file is read-only, return an error. */ |
526 | 65.5k | if (grp->nc4_info->no_write) |
527 | 0 | BAIL(NC_EPERM); |
528 | | |
529 | | /* Get the hid to attach the attribute to, or read it from. */ |
530 | 65.5k | if (varid == NC_GLOBAL) |
531 | 53.3k | locid = hdf5_grp->hdf_grpid; |
532 | 12.1k | else |
533 | 12.1k | { |
534 | 12.1k | if ((retval = nc4_open_var_grp2(grp, varid, &datasetid))) |
535 | 0 | BAIL(retval); |
536 | 12.1k | locid = datasetid; |
537 | 12.1k | } |
538 | | |
539 | | /* Get the length ready, and find the HDF type we'll be |
540 | | * writing. */ |
541 | 65.5k | dims[0] = att->len; |
542 | 65.5k | if ((retval = nc4_get_hdf_typeid(grp->nc4_info, att->nc_typeid, |
543 | 65.5k | &file_typeid, 0))) |
544 | 0 | BAIL(retval); |
545 | | |
546 | | /* Even if the length is zero, HDF5 won't let me write with a |
547 | | * NULL pointer. So if the length of the att is zero, point to |
548 | | * some phoney data (which won't be written anyway.)*/ |
549 | 65.5k | if (!dims[0]) |
550 | 11.5k | data = &phoney_data; |
551 | 54.0k | else if (att->data) |
552 | 54.0k | data = att->data; |
553 | 0 | else if (att->stdata) |
554 | 0 | data = att->stdata; |
555 | 0 | else |
556 | 0 | data = att->vldata; |
557 | | |
558 | | /* NC_CHAR types require some extra work. The space ID is set to |
559 | | * scalar, and the type is told how long the string is. If it's |
560 | | * really zero length, set the size to 1. (The fact that it's |
561 | | * really zero will be marked by the NULL dataspace, but HDF5 |
562 | | * doesn't allow me to set the size of the type to zero.)*/ |
563 | 65.5k | if (att->nc_typeid == NC_CHAR) |
564 | 25.7k | { |
565 | 25.7k | size_t string_size = dims[0]; |
566 | 25.7k | if (!string_size) |
567 | 11.5k | { |
568 | 11.5k | string_size = 1; |
569 | 11.5k | if ((spaceid = H5Screate(H5S_NULL)) < 0) |
570 | 0 | BAIL(NC_EATTMETA); |
571 | 11.5k | } |
572 | 14.2k | else |
573 | 14.2k | { |
574 | 14.2k | if ((spaceid = H5Screate(H5S_SCALAR)) < 0) |
575 | 0 | BAIL(NC_EATTMETA); |
576 | 14.2k | } |
577 | 25.7k | if (H5Tset_size(file_typeid, string_size) < 0) |
578 | 0 | BAIL(NC_EATTMETA); |
579 | 25.7k | if (H5Tset_strpad(file_typeid, H5T_STR_NULLTERM) < 0) |
580 | 0 | BAIL(NC_EATTMETA); |
581 | 25.7k | } |
582 | 39.7k | else |
583 | 39.7k | { |
584 | 39.7k | if (!att->len) |
585 | 0 | { |
586 | 0 | if ((spaceid = H5Screate(H5S_NULL)) < 0) |
587 | 0 | BAIL(NC_EATTMETA); |
588 | 0 | } |
589 | 39.7k | else |
590 | 39.7k | { |
591 | 39.7k | if ((spaceid = H5Screate_simple(1, dims, NULL)) < 0) |
592 | 0 | BAIL(NC_EATTMETA); |
593 | 39.7k | } |
594 | 39.7k | } |
595 | | |
596 | | /* Does the att exists already? */ |
597 | 65.5k | if ((attr_exists = H5Aexists(locid, att->hdr.name)) < 0) |
598 | 0 | BAIL(NC_EHDFERR); |
599 | 65.5k | if (attr_exists) |
600 | 0 | { |
601 | 0 | hssize_t npoints; |
602 | | |
603 | | /* Open the attribute. */ |
604 | 0 | if ((existing_attid = H5Aopen(locid, att->hdr.name, H5P_DEFAULT)) < 0) |
605 | 0 | BAIL(NC_EATTMETA); |
606 | | |
607 | | /* Find the type of the existing attribute. */ |
608 | 0 | if ((existing_att_typeid = H5Aget_type(existing_attid)) < 0) |
609 | 0 | BAIL(NC_EATTMETA); |
610 | | |
611 | | /* How big is the attribute? */ |
612 | 0 | if ((existing_spaceid = H5Aget_space(existing_attid)) < 0) |
613 | 0 | BAIL(NC_EATTMETA); |
614 | 0 | if ((npoints = H5Sget_simple_extent_npoints(existing_spaceid)) < 0) |
615 | 0 | BAIL(NC_EATTMETA); |
616 | | |
617 | | /* For text attributes the size is specified in the datatype |
618 | | and it is enough to compare types using H5Tequal(). */ |
619 | 0 | if (!H5Tequal(file_typeid, existing_att_typeid) || |
620 | 0 | (att->nc_typeid != NC_CHAR && npoints != att->len)) |
621 | 0 | { |
622 | | /* The attribute exists but we cannot re-use it. */ |
623 | | |
624 | | /* Delete the attribute. */ |
625 | 0 | if (H5Adelete(locid, att->hdr.name) < 0) |
626 | 0 | BAIL(NC_EHDFERR); |
627 | | |
628 | | /* Re-create the attribute with the type and length |
629 | | reflecting the new value (or values). */ |
630 | 0 | if ((attid = H5Acreate(locid, att->hdr.name, file_typeid, spaceid, |
631 | 0 | H5P_DEFAULT)) < 0) |
632 | 0 | BAIL(NC_EATTMETA); |
633 | | |
634 | | /* Write the values, (even if length is zero). */ |
635 | 0 | if (H5Awrite(attid, file_typeid, data) < 0) |
636 | 0 | BAIL(NC_EATTMETA); |
637 | 0 | } |
638 | 0 | else |
639 | 0 | { |
640 | | /* The attribute exists and we can re-use it. */ |
641 | | |
642 | | /* Write the values, re-using the existing attribute. */ |
643 | 0 | if (H5Awrite(existing_attid, file_typeid, data) < 0) |
644 | 0 | BAIL(NC_EATTMETA); |
645 | 0 | } |
646 | 0 | } |
647 | 65.5k | else |
648 | 65.5k | { |
649 | | /* The attribute does not exist yet. */ |
650 | | |
651 | | /* Create the attribute. */ |
652 | 65.5k | if ((attid = H5Acreate(locid, att->hdr.name, file_typeid, spaceid, |
653 | 65.5k | H5P_DEFAULT)) < 0) |
654 | 0 | BAIL(NC_EATTMETA); |
655 | | |
656 | | /* Write the values, (even if length is zero). */ |
657 | 65.5k | if (H5Awrite(attid, file_typeid, data) < 0) |
658 | 0 | BAIL(NC_EATTMETA); |
659 | 65.5k | } |
660 | | |
661 | 65.5k | exit: |
662 | 65.5k | if (file_typeid && H5Tclose(file_typeid)) |
663 | 0 | BAIL2(NC_EHDFERR); |
664 | 65.5k | if (attid > 0 && H5Aclose(attid) < 0) |
665 | 0 | BAIL2(NC_EHDFERR); |
666 | 65.5k | if (existing_att_typeid && H5Tclose(existing_att_typeid)) |
667 | 0 | BAIL2(NC_EHDFERR); |
668 | 65.5k | if (existing_attid > 0 && H5Aclose(existing_attid) < 0) |
669 | 0 | BAIL2(NC_EHDFERR); |
670 | 65.5k | if (spaceid > 0 && H5Sclose(spaceid) < 0) |
671 | 0 | BAIL2(NC_EHDFERR); |
672 | 65.5k | if (existing_spaceid > 0 && H5Sclose(existing_spaceid) < 0) |
673 | 0 | BAIL2(NC_EHDFERR); |
674 | 65.5k | return retval; |
675 | 65.5k | } |
676 | | |
677 | | /** |
678 | | * @internal Write all the dirty atts in an attlist. |
679 | | * |
680 | | * @param attlist Pointer to the list if attributes. |
681 | | * @param varid Variable ID. |
682 | | * @param grp Pointer to group info struct. |
683 | | * |
684 | | * @returns NC_NOERR No error. |
685 | | * @returns NC_EHDFERR HDF5 returned an error. |
686 | | * @author Ed Hartnett |
687 | | */ |
688 | | static int |
689 | | write_attlist(NCindex *attlist, int varid, NC_GRP_INFO_T *grp) |
690 | 78.2k | { |
691 | 78.2k | NC_ATT_INFO_T *att; |
692 | 78.2k | int retval; |
693 | 78.2k | int i; |
694 | | |
695 | 148k | for(i = 0; i < ncindexsize(attlist); i++) |
696 | 70.3k | { |
697 | 70.3k | att = (NC_ATT_INFO_T *)ncindexith(attlist, i); |
698 | 70.3k | assert(att); |
699 | 70.3k | if (att->dirty) |
700 | 65.5k | { |
701 | 65.5k | LOG((4, "%s: writing att %s to varid %d", __func__, att->hdr.name, varid)); |
702 | 65.5k | if ((retval = put_att_grpa(grp, varid, att))) |
703 | 0 | return retval; |
704 | 65.5k | att->dirty = NC_FALSE; |
705 | 65.5k | att->created = NC_TRUE; |
706 | 65.5k | } |
707 | 70.3k | } |
708 | 78.2k | return NC_NOERR; |
709 | 78.2k | } |
710 | | |
711 | | /** |
712 | | * @internal HDF5 dimension scales cannot themselves have scales |
713 | | * attached. This is a problem for multidimensional coordinate |
714 | | * variables. So this function writes a special attribute for such a |
715 | | * variable, which has the ids of all the dimensions for that |
716 | | * coordinate variable. |
717 | | * |
718 | | * @param var Pointer to var info struct. |
719 | | * |
720 | | * @returns NC_NOERR No error. |
721 | | * @returns NC_EHDFERR HDF5 returned an error. |
722 | | * @author Ed Hartnett |
723 | | */ |
724 | | static int |
725 | | write_coord_dimids(NC_VAR_INFO_T *var) |
726 | 10.8k | { |
727 | 10.8k | NC_HDF5_VAR_INFO_T *hdf5_var; |
728 | 10.8k | hsize_t coords_len[1]; |
729 | 10.8k | hid_t c_spaceid = -1, c_attid = -1; |
730 | 10.8k | int retval = NC_NOERR; |
731 | | |
732 | 10.8k | assert(var && var->format_var_info); |
733 | | |
734 | | /* Get HDF5-specific var info. */ |
735 | 10.8k | hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; |
736 | | |
737 | | /* Set up space for attribute. */ |
738 | 10.8k | coords_len[0] = var->ndims; |
739 | 10.8k | if ((c_spaceid = H5Screate_simple(1, coords_len, coords_len)) < 0) |
740 | 0 | BAIL(NC_EHDFERR); |
741 | | |
742 | | /* Create the attribute. */ |
743 | 10.8k | if ((c_attid = H5Acreate(hdf5_var->hdf_datasetid, COORDINATES, |
744 | 10.8k | H5T_NATIVE_INT, c_spaceid, H5P_DEFAULT)) < 0) |
745 | 0 | BAIL(NC_EHDFERR); |
746 | | |
747 | | /* Write our attribute. */ |
748 | 10.8k | if (H5Awrite(c_attid, H5T_NATIVE_INT, var->dimids) < 0) |
749 | 0 | BAIL(NC_EHDFERR); |
750 | | |
751 | 10.8k | exit: |
752 | 10.8k | if (c_spaceid >= 0 && H5Sclose(c_spaceid) < 0) |
753 | 0 | BAIL2(NC_EHDFERR); |
754 | 10.8k | if (c_attid >= 0 && H5Aclose(c_attid) < 0) |
755 | 0 | BAIL2(NC_EHDFERR); |
756 | 10.8k | return retval; |
757 | 10.8k | } |
758 | | |
759 | | /** |
760 | | * @internal Write a special attribute for the netCDF-4 dimension ID. |
761 | | * |
762 | | * @param datasetid HDF5 datasset ID. |
763 | | * @param dimid NetCDF dimension ID. |
764 | | * |
765 | | * @returns NC_NOERR No error. |
766 | | * @returns NC_EHDFERR HDF5 returned an error. |
767 | | * @author Ed Hartnett |
768 | | */ |
769 | | static int |
770 | | write_netcdf4_dimid(hid_t datasetid, int dimid) |
771 | 46.6k | { |
772 | 46.6k | hid_t dimid_spaceid = -1, dimid_attid = -1; |
773 | 46.6k | htri_t attr_exists; |
774 | 46.6k | int retval = NC_NOERR; |
775 | | |
776 | | /* Create the space. */ |
777 | 46.6k | if ((dimid_spaceid = H5Screate(H5S_SCALAR)) < 0) |
778 | 0 | BAIL(NC_EHDFERR); |
779 | | |
780 | | /* Does the attribute already exist? If so, don't try to create it. */ |
781 | 46.6k | if ((attr_exists = H5Aexists(datasetid, NC_DIMID_ATT_NAME)) < 0) |
782 | 0 | BAIL(NC_EHDFERR); |
783 | 46.6k | if (attr_exists) |
784 | 12.1k | dimid_attid = H5Aopen_by_name(datasetid, ".", NC_DIMID_ATT_NAME, |
785 | 12.1k | H5P_DEFAULT, H5P_DEFAULT); |
786 | 34.4k | else |
787 | | /* Create the attribute if needed. */ |
788 | 34.4k | dimid_attid = H5Acreate(datasetid, NC_DIMID_ATT_NAME, |
789 | 34.4k | H5T_NATIVE_INT, dimid_spaceid, H5P_DEFAULT); |
790 | 46.6k | if (dimid_attid < 0) |
791 | 0 | BAIL(NC_EHDFERR); |
792 | | |
793 | | |
794 | | /* Write it. */ |
795 | 46.6k | LOG((4, "%s: writing secret dimid %d", __func__, dimid)); |
796 | 46.6k | if (H5Awrite(dimid_attid, H5T_NATIVE_INT, &dimid) < 0) |
797 | 0 | BAIL(NC_EHDFERR); |
798 | | |
799 | 46.6k | exit: |
800 | | /* Close stuff*/ |
801 | 46.6k | if (dimid_spaceid >= 0 && H5Sclose(dimid_spaceid) < 0) |
802 | 0 | BAIL2(NC_EHDFERR); |
803 | 46.6k | if (dimid_attid >= 0 && H5Aclose(dimid_attid) < 0) |
804 | 0 | BAIL2(NC_EHDFERR); |
805 | | |
806 | 46.6k | return retval; |
807 | 46.6k | } |
808 | | |
809 | | /** |
810 | | * @internal This function creates the HDF5 dataset for a variable. |
811 | | * |
812 | | * @param grp Pointer to group info struct. |
813 | | * @param var Pointer to variable info struct. |
814 | | * @param write_dimid True to write dimid. |
815 | | * |
816 | | * @return ::NC_NOERR |
817 | | * @returns NC_ECHAR Conversions of NC_CHAR forbidden. |
818 | | * @returns NC_EVARMETA HDF5 returning error creating datatype. |
819 | | * @returns NC_EHDFERR HDF5 returning error. |
820 | | * @returns NC_EBADTYE Type not found. |
821 | | * @author Ed Hartnett |
822 | | */ |
823 | | static int |
824 | | var_create_dataset(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, nc_bool_t write_dimid) |
825 | 70.3k | { |
826 | 70.3k | NC_HDF5_GRP_INFO_T *hdf5_grp; |
827 | 70.3k | NC_HDF5_VAR_INFO_T *hdf5_var; |
828 | 70.3k | hid_t plistid = 0, access_plistid = 0, typeid = 0, spaceid = 0; |
829 | 70.3k | hsize_t chunksize[H5S_MAX_RANK], dimsize[H5S_MAX_RANK], maxdimsize[H5S_MAX_RANK]; |
830 | 70.3k | int d; |
831 | 70.3k | void *fillp = NULL; |
832 | 70.3k | NC_DIM_INFO_T *dim = NULL; |
833 | 70.3k | char *name_to_use; |
834 | 70.3k | int retval; |
835 | | |
836 | 70.3k | assert(grp && grp->format_grp_info && var && var->format_var_info); |
837 | | |
838 | 70.3k | LOG((3, "%s:: name %s", __func__, var->hdr.name)); |
839 | | |
840 | | /* Get HDF5-specific group and var info. */ |
841 | 70.3k | hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; |
842 | 70.3k | hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; |
843 | | |
844 | | /* Scalar or not, we need a creation property list. */ |
845 | 70.3k | if ((plistid = H5Pcreate(H5P_DATASET_CREATE)) < 0) |
846 | 0 | BAIL(NC_EHDFERR); |
847 | 70.3k | if ((access_plistid = H5Pcreate(H5P_DATASET_ACCESS)) < 0) |
848 | 0 | BAIL(NC_EHDFERR); |
849 | | |
850 | | /* Turn off object tracking times in HDF5. */ |
851 | 70.3k | if (H5Pset_obj_track_times(plistid, 0) < 0) |
852 | 0 | BAIL(NC_EHDFERR); |
853 | | |
854 | | /* Find the HDF5 type of the dataset. */ |
855 | 70.3k | if ((retval = nc4_get_hdf_typeid(grp->nc4_info, var->type_info->hdr.id, &typeid, |
856 | 70.3k | var->type_info->endianness))) |
857 | 0 | BAIL(retval); |
858 | | |
859 | | /* Figure out what fill value to set, if any. */ |
860 | 70.3k | if (var->no_fill) |
861 | 0 | { |
862 | | /* Required to truly turn HDF5 fill values off */ |
863 | 0 | if (H5Pset_fill_time(plistid, H5D_FILL_TIME_NEVER) < 0) |
864 | 0 | BAIL(NC_EHDFERR); |
865 | 0 | } |
866 | 70.3k | else |
867 | 70.3k | { |
868 | 70.3k | if ((retval = nc4_get_fill_value(grp->nc4_info, var, &fillp))) |
869 | 0 | BAIL(retval); |
870 | | |
871 | | /* If there is a fill value, set it. */ |
872 | 70.3k | if (fillp) |
873 | 70.3k | { |
874 | 70.3k | if (var->type_info->nc_type_class == NC_STRING) |
875 | 0 | { |
876 | 0 | if (H5Pset_fill_value(plistid, typeid, fillp) < 0) |
877 | 0 | BAIL(NC_EHDFERR); |
878 | 0 | } |
879 | 70.3k | else |
880 | 70.3k | { |
881 | | /* The fill value set in HDF5 must always be presented as |
882 | | * a native type, even if the endianness for this dataset |
883 | | * is non-native. HDF5 will translate the fill value to |
884 | | * the target endiannesss. */ |
885 | 70.3k | hid_t fill_typeid = 0; |
886 | | |
887 | 70.3k | if ((retval = nc4_get_hdf_typeid(grp->nc4_info, var->type_info->hdr.id, &fill_typeid, |
888 | 70.3k | NC_ENDIAN_NATIVE))) |
889 | 0 | BAIL(retval); |
890 | 70.3k | if (H5Pset_fill_value(plistid, fill_typeid, fillp) < 0) |
891 | 0 | { |
892 | 0 | if (H5Tclose(fill_typeid) < 0) |
893 | 0 | BAIL(NC_EHDFERR); |
894 | 0 | BAIL(NC_EHDFERR); |
895 | 0 | } |
896 | 70.3k | if (H5Tclose(fill_typeid) < 0) |
897 | 0 | BAIL(NC_EHDFERR); |
898 | 70.3k | } |
899 | 70.3k | } |
900 | 70.3k | } |
901 | | |
902 | | /* If the user wants to shuffle the data, set that up now. */ |
903 | 70.3k | if (var->shuffle) { |
904 | 0 | if (H5Pset_shuffle(plistid) < 0) |
905 | 0 | BAIL(NC_EHDFERR); |
906 | 0 | } |
907 | | |
908 | | /* If the user wants to compress the data, using either zlib |
909 | | * (a.k.a deflate) or szip, or another filter, set that up now. |
910 | | * Szip and zip can be turned on |
911 | | * either directly with nc_def_var_szip/deflate(), or using |
912 | | * nc_def_var_filter(). If the user |
913 | | * has specified a filter, it will be applied here. */ |
914 | 70.3k | if(var->filters != NULL) { |
915 | 0 | int j; |
916 | 0 | for(j=0;j<nclistlength(var->filters);j++) { |
917 | 0 | NC_FILTER_SPEC_HDF5* fi = (NC_FILTER_SPEC_HDF5*)nclistget(var->filters,j); |
918 | 0 | size_t nparams; |
919 | 0 | unsigned int* params; |
920 | 0 | nparams = fi->nparams; |
921 | 0 | params = fi->params; |
922 | 0 | if(fi->filterid == H5Z_FILTER_DEFLATE) {/* Handle zip case here */ |
923 | 0 | unsigned level; |
924 | 0 | if(nparams != 1) |
925 | 0 | BAIL(NC_EFILTER); |
926 | 0 | level = (int)params[0]; |
927 | 0 | if(H5Pset_deflate(plistid, level) < 0) |
928 | 0 | BAIL(NC_EFILTER); |
929 | 0 | } else if(fi->filterid == H5Z_FILTER_SZIP) {/* Handle szip case here */ |
930 | 0 | int options_mask; |
931 | 0 | int bits_per_pixel; |
932 | 0 | if(nparams != 2) |
933 | 0 | BAIL(NC_EFILTER); |
934 | 0 | options_mask = (int)params[0]; |
935 | 0 | bits_per_pixel = (int)params[1]; |
936 | 0 | if(H5Pset_szip(plistid, options_mask, bits_per_pixel) < 0) |
937 | 0 | BAIL(NC_EFILTER); |
938 | 0 | } else { |
939 | 0 | herr_t code = H5Pset_filter(plistid, (unsigned int)fi->filterid, H5Z_FLAG_MANDATORY, nparams, params); |
940 | 0 | if(code < 0) { |
941 | 0 | BAIL(NC_EFILTER); |
942 | 0 | } |
943 | 0 | } |
944 | 0 | } |
945 | 0 | } |
946 | | |
947 | | /* If the user wants to fletcher error correction, set that up now. */ |
948 | 70.3k | if (var->fletcher32) |
949 | 0 | if (H5Pset_fletcher32(plistid) < 0) |
950 | 0 | BAIL(NC_EHDFERR); |
951 | | |
952 | | /* If ndims non-zero, get info for all dimensions. We look up the |
953 | | dimids and get the len of each dimension. We need this to create |
954 | | the space for the dataset. In netCDF a dimension length of zero |
955 | | means an unlimited dimension. */ |
956 | 70.3k | if (var->ndims) |
957 | 28.0k | { |
958 | 28.0k | int unlimdim = 0; |
959 | | |
960 | | /* Check to see if any unlimited dimensions are used in this var. */ |
961 | 71.9k | for (d = 0; d < var->ndims; d++) { |
962 | 43.9k | dim = var->dim[d]; |
963 | 43.9k | assert(dim && dim->hdr.id == var->dimids[d]); |
964 | 43.9k | if (dim->unlimited) |
965 | 21.6k | unlimdim++; |
966 | 43.9k | } |
967 | | |
968 | | /* If there are no unlimited dims, and no filters, and the user |
969 | | * has not specified chunksizes, use contiguous variable for |
970 | | * better performance. */ |
971 | 28.0k | if (!var->shuffle && !var->fletcher32 && nclistlength(var->filters) == 0 && |
972 | 28.0k | (var->chunksizes == NULL || !var->chunksizes[0]) && !unlimdim) |
973 | 0 | var->storage = NC_CONTIGUOUS; |
974 | | |
975 | | /* Gather current & maximum dimension sizes, along with chunk |
976 | | * sizes. */ |
977 | 71.9k | for (d = 0; d < var->ndims; d++) |
978 | 43.9k | { |
979 | 43.9k | dim = var->dim[d]; |
980 | 43.9k | assert(dim && dim->hdr.id == var->dimids[d]); |
981 | 43.9k | dimsize[d] = dim->unlimited ? NC_HDF5_UNLIMITED_DIMSIZE : dim->len; |
982 | 43.9k | maxdimsize[d] = dim->unlimited ? H5S_UNLIMITED : (hsize_t)dim->len; |
983 | 43.9k | if (var->storage == NC_CHUNKED) |
984 | 23.4k | { |
985 | 23.4k | if (var->chunksizes[d]) |
986 | 23.4k | chunksize[d] = var->chunksizes[d]; |
987 | 0 | else |
988 | 0 | { |
989 | 0 | size_t type_size; |
990 | 0 | if (var->type_info->nc_type_class == NC_STRING) |
991 | 0 | type_size = sizeof(char *); |
992 | 0 | else |
993 | 0 | type_size = var->type_info->size; |
994 | | |
995 | | /* Unlimited dim always gets chunksize of 1. */ |
996 | 0 | if (dim->unlimited) |
997 | 0 | chunksize[d] = 1; |
998 | 0 | else |
999 | 0 | chunksize[d] = pow((double)DEFAULT_CHUNK_SIZE/type_size, |
1000 | 0 | 1/(double)(var->ndims - unlimdim)); |
1001 | | |
1002 | | /* If the chunksize is greater than the dim |
1003 | | * length, make it the dim length. */ |
1004 | 0 | if (!dim->unlimited && chunksize[d] > dim->len) |
1005 | 0 | chunksize[d] = dim->len; |
1006 | | |
1007 | | /* Remember the computed chunksize */ |
1008 | 0 | var->chunksizes[d] = chunksize[d]; |
1009 | 0 | } |
1010 | 23.4k | } |
1011 | 43.9k | } |
1012 | | |
1013 | | /* Create the dataspace. */ |
1014 | 28.0k | if ((spaceid = H5Screate_simple(var->ndims, dimsize, maxdimsize)) < 0) |
1015 | 0 | BAIL(NC_EHDFERR); |
1016 | 28.0k | } |
1017 | 42.2k | else |
1018 | 42.2k | { |
1019 | 42.2k | if ((spaceid = H5Screate(H5S_SCALAR)) < 0) |
1020 | 0 | BAIL(NC_EHDFERR); |
1021 | 42.2k | } |
1022 | | |
1023 | | /* Set the var storage to contiguous, compact, or chunked. Don't |
1024 | | * try to set chunking for scalar vars, they will default to |
1025 | | * contiguous if not set to compact. */ |
1026 | 70.3k | if (var->storage == NC_CONTIGUOUS) |
1027 | 57.5k | { |
1028 | 57.5k | if (H5Pset_layout(plistid, H5D_CONTIGUOUS) < 0) |
1029 | 0 | BAIL(NC_EHDFERR); |
1030 | 57.5k | } |
1031 | 12.7k | else if (var->storage == NC_COMPACT) |
1032 | 0 | { |
1033 | 0 | if (H5Pset_layout(plistid, H5D_COMPACT) < 0) |
1034 | 0 | BAIL(NC_EHDFERR); |
1035 | 0 | } |
1036 | 12.7k | else if (var->ndims) |
1037 | 12.7k | { |
1038 | 12.7k | if (H5Pset_chunk(plistid, var->ndims, chunksize) < 0) |
1039 | 0 | BAIL(NC_EHDFERR); |
1040 | 12.7k | } |
1041 | | |
1042 | | /* Turn on creation order tracking. */ |
1043 | 70.3k | if (H5Pset_attr_creation_order(plistid, H5P_CRT_ORDER_TRACKED| |
1044 | 70.3k | H5P_CRT_ORDER_INDEXED) < 0) |
1045 | 0 | BAIL(NC_EHDFERR); |
1046 | | |
1047 | | /* Set per-var chunk cache, for chunked datasets. */ |
1048 | 70.3k | if (var->storage == NC_CHUNKED && var->chunk_cache_size) |
1049 | 12.7k | if (H5Pset_chunk_cache(access_plistid, var->chunk_cache_nelems, |
1050 | 12.7k | var->chunk_cache_size, var->chunk_cache_preemption) < 0) |
1051 | 0 | BAIL(NC_EHDFERR); |
1052 | | |
1053 | | /* At long last, create the dataset. */ |
1054 | 70.3k | name_to_use = var->hdf5_name ? var->hdf5_name : var->hdr.name; |
1055 | 70.3k | LOG((4, "%s: about to H5Dcreate2 dataset %s of type 0x%x", __func__, |
1056 | 70.3k | name_to_use, typeid)); |
1057 | 70.3k | if ((hdf5_var->hdf_datasetid = H5Dcreate2(hdf5_grp->hdf_grpid, name_to_use, typeid, |
1058 | 70.3k | spaceid, H5P_DEFAULT, plistid, access_plistid)) < 0) |
1059 | 0 | BAIL(NC_EHDFERR); |
1060 | 70.3k | var->created = NC_TRUE; |
1061 | 70.3k | var->is_new_var = NC_FALSE; |
1062 | | |
1063 | | /* Always write the hidden coordinates attribute, which lists the |
1064 | | * dimids of this var. When present, this speeds opens. When no |
1065 | | * present, dimscale matching is used. */ |
1066 | 70.3k | if (var->ndims > 1) |
1067 | 10.8k | if ((retval = write_coord_dimids(var))) |
1068 | 0 | BAIL(retval); |
1069 | | |
1070 | | /* If this is a dimscale, mark it as such in the HDF5 file. Also |
1071 | | * find the dimension info and store the dataset id of the dimscale |
1072 | | * dataset. */ |
1073 | 70.3k | if (var->dimscale) |
1074 | 1.61k | { |
1075 | 1.61k | if (H5DSset_scale(hdf5_var->hdf_datasetid, var->hdr.name) < 0) |
1076 | 0 | BAIL(NC_EHDFERR); |
1077 | | |
1078 | | /* If this is a multidimensional coordinate variable, write a |
1079 | | * coordinates attribute. */ |
1080 | | /* if (var->ndims > 1) */ |
1081 | | /* if ((retval = write_coord_dimids(var))) */ |
1082 | | /* BAIL(retval); */ |
1083 | | |
1084 | | /* If desired, write the netCDF dimid. */ |
1085 | 1.61k | if (write_dimid) |
1086 | 1.61k | if ((retval = write_netcdf4_dimid(hdf5_var->hdf_datasetid, var->dimids[0]))) |
1087 | 0 | BAIL(retval); |
1088 | 1.61k | } |
1089 | | |
1090 | | /* Write attributes for this var. */ |
1091 | 70.3k | if ((retval = write_attlist(var->att, var->hdr.id, grp))) |
1092 | 0 | BAIL(retval); |
1093 | 70.3k | var->attr_dirty = NC_FALSE; |
1094 | | |
1095 | 70.3k | exit: |
1096 | 70.3k | if (typeid > 0 && H5Tclose(typeid) < 0) |
1097 | 0 | BAIL2(NC_EHDFERR); |
1098 | 70.3k | if (plistid > 0 && H5Pclose(plistid) < 0) |
1099 | 0 | BAIL2(NC_EHDFERR); |
1100 | 70.3k | if (access_plistid > 0 && H5Pclose(access_plistid) < 0) |
1101 | 0 | BAIL2(NC_EHDFERR); |
1102 | 70.3k | if (spaceid > 0 && H5Sclose(spaceid) < 0) |
1103 | 0 | BAIL2(NC_EHDFERR); |
1104 | 70.3k | if (fillp) |
1105 | 70.3k | { |
1106 | 70.3k | if (var->type_info->nc_type_class == NC_VLEN) |
1107 | 0 | nc_free_vlen((nc_vlen_t *)fillp); |
1108 | 70.3k | else if (var->type_info->nc_type_class == NC_STRING && *(char **)fillp) |
1109 | 0 | free(*(char **)fillp); |
1110 | 70.3k | free(fillp); |
1111 | 70.3k | } |
1112 | | |
1113 | 70.3k | return retval; |
1114 | 70.3k | } |
1115 | | |
1116 | | /** |
1117 | | * @internal Adjust the chunk cache of a var for better |
1118 | | * performance. |
1119 | | * |
1120 | | * @note For contiguous and compact storage vars, or when parallel I/O |
1121 | | * is in use, this function will do nothing and return ::NC_NOERR; |
1122 | | * |
1123 | | * @param grp Pointer to group info struct. |
1124 | | * @param var Pointer to var info struct. |
1125 | | * |
1126 | | * @return ::NC_NOERR No error. |
1127 | | * @author Ed Hartnett |
1128 | | */ |
1129 | | int |
1130 | | nc4_adjust_var_cache(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var) |
1131 | 98.3k | { |
1132 | 98.3k | size_t chunk_size_bytes = 1; |
1133 | 98.3k | int d; |
1134 | 98.3k | int retval; |
1135 | | |
1136 | | /* Nothing to be done for contiguous or compact data. */ |
1137 | 98.3k | if (var->storage != NC_CHUNKED) |
1138 | 72.8k | return NC_NOERR; |
1139 | | |
1140 | | #ifdef USE_PARALLEL4 |
1141 | | /* Don't set cache for files using parallel I/O. */ |
1142 | | if (grp->nc4_info->parallel) |
1143 | | return NC_NOERR; |
1144 | | #endif |
1145 | | |
1146 | | /* How many bytes in the chunk? */ |
1147 | 72.5k | for (d = 0; d < var->ndims; d++) |
1148 | 46.9k | chunk_size_bytes *= var->chunksizes[d]; |
1149 | 25.5k | if (var->type_info->size) |
1150 | 25.5k | chunk_size_bytes *= var->type_info->size; |
1151 | 0 | else |
1152 | 0 | chunk_size_bytes *= sizeof(char *); |
1153 | | |
1154 | | /* If the chunk cache is too small, and the user has not changed |
1155 | | * the default value of the chunk cache size, then increase the |
1156 | | * size of the cache. */ |
1157 | 25.5k | if (var->chunk_cache_size == CHUNK_CACHE_SIZE) |
1158 | 25.5k | if (chunk_size_bytes > var->chunk_cache_size) |
1159 | 0 | { |
1160 | 0 | var->chunk_cache_size = chunk_size_bytes * DEFAULT_CHUNKS_IN_CACHE; |
1161 | 0 | if (var->chunk_cache_size > MAX_DEFAULT_CACHE_SIZE) |
1162 | 0 | var->chunk_cache_size = MAX_DEFAULT_CACHE_SIZE; |
1163 | 0 | if ((retval = nc4_reopen_dataset(grp, var))) |
1164 | 0 | return retval; |
1165 | 0 | } |
1166 | | |
1167 | 25.5k | return NC_NOERR; |
1168 | 25.5k | } |
1169 | | |
1170 | | /** |
1171 | | * @internal Create a HDF5 defined type from a NC_TYPE_INFO_T struct, |
1172 | | * and commit it to the file. |
1173 | | * |
1174 | | * @param grp Pointer to group info struct. |
1175 | | * @param type Pointer to type info struct. |
1176 | | * |
1177 | | * @return NC_NOERR No error. |
1178 | | * @return NC_EHDFERR HDF5 error. |
1179 | | * @return NC_ECHAR Conversions of NC_CHAR forbidden. |
1180 | | * @return NC_EVARMETA HDF5 returning error creating datatype. |
1181 | | * @return NC_EHDFERR HDF5 returning error. |
1182 | | * @return NC_EBADTYE Type not found. |
1183 | | * @author Ed Hartnett |
1184 | | */ |
1185 | | static int |
1186 | | commit_type(NC_GRP_INFO_T *grp, NC_TYPE_INFO_T *type) |
1187 | 0 | { |
1188 | 0 | NC_HDF5_GRP_INFO_T *hdf5_grp; |
1189 | 0 | NC_HDF5_TYPE_INFO_T *hdf5_type; |
1190 | 0 | hid_t base_hdf_typeid; |
1191 | 0 | int retval; |
1192 | |
|
1193 | 0 | assert(grp && grp->format_grp_info && type && type->format_type_info); |
1194 | | |
1195 | | /* Get HDF5-specific group and type info. */ |
1196 | 0 | hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; |
1197 | 0 | hdf5_type = (NC_HDF5_TYPE_INFO_T *)type->format_type_info; |
1198 | | |
1199 | | /* Did we already record this type? */ |
1200 | 0 | if (type->committed) |
1201 | 0 | return NC_NOERR; |
1202 | | |
1203 | | /* Is this a compound type? */ |
1204 | 0 | if (type->nc_type_class == NC_COMPOUND) |
1205 | 0 | { |
1206 | 0 | NC_FIELD_INFO_T *field; |
1207 | 0 | hid_t hdf_base_typeid, hdf_typeid; |
1208 | 0 | int i; |
1209 | |
|
1210 | 0 | if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_COMPOUND, type->size)) < 0) |
1211 | 0 | return NC_EHDFERR; |
1212 | 0 | LOG((4, "creating compound type %s hdf_typeid 0x%x", type->hdr.name, |
1213 | 0 | hdf5_type->hdf_typeid)); |
1214 | |
|
1215 | 0 | for(i=0;i<nclistlength(type->u.c.field);i++) |
1216 | 0 | { |
1217 | 0 | field = (NC_FIELD_INFO_T *)nclistget(type->u.c.field, i); |
1218 | 0 | assert(field); |
1219 | 0 | if ((retval = nc4_get_hdf_typeid(grp->nc4_info, field->nc_typeid, |
1220 | 0 | &hdf_base_typeid, type->endianness))) |
1221 | 0 | return retval; |
1222 | | |
1223 | | /* If this is an array, create a special array type. */ |
1224 | 0 | if (field->ndims) |
1225 | 0 | { |
1226 | 0 | int d; |
1227 | 0 | hsize_t dims[NC_MAX_VAR_DIMS]; |
1228 | |
|
1229 | 0 | for (d = 0; d < field->ndims; d++) |
1230 | 0 | dims[d] = field->dim_size[d]; |
1231 | 0 | if ((hdf_typeid = H5Tarray_create(hdf_base_typeid, field->ndims, |
1232 | 0 | dims, NULL)) < 0) |
1233 | 0 | { |
1234 | 0 | if (H5Tclose(hdf_base_typeid) < 0) |
1235 | 0 | return NC_EHDFERR; |
1236 | 0 | return NC_EHDFERR; |
1237 | 0 | } |
1238 | 0 | if (H5Tclose(hdf_base_typeid) < 0) |
1239 | 0 | return NC_EHDFERR; |
1240 | 0 | } |
1241 | 0 | else |
1242 | 0 | hdf_typeid = hdf_base_typeid; |
1243 | 0 | LOG((4, "inserting field %s offset %d hdf_typeid 0x%x", field->hdr.name, |
1244 | 0 | field->offset, hdf_typeid)); |
1245 | 0 | if (H5Tinsert(hdf5_type->hdf_typeid, field->hdr.name, field->offset, |
1246 | 0 | hdf_typeid) < 0) |
1247 | 0 | return NC_EHDFERR; |
1248 | 0 | if (H5Tclose(hdf_typeid) < 0) |
1249 | 0 | return NC_EHDFERR; |
1250 | 0 | } |
1251 | 0 | } |
1252 | 0 | else if (type->nc_type_class == NC_VLEN) |
1253 | 0 | { |
1254 | | /* Find the HDF typeid of the base type of this vlen. */ |
1255 | 0 | if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.v.base_nc_typeid, |
1256 | 0 | &base_hdf_typeid, type->endianness))) |
1257 | 0 | return retval; |
1258 | | |
1259 | | /* Create a vlen type. */ |
1260 | 0 | if ((hdf5_type->hdf_typeid = H5Tvlen_create(base_hdf_typeid)) < 0) |
1261 | 0 | return NC_EHDFERR; |
1262 | 0 | } |
1263 | 0 | else if (type->nc_type_class == NC_OPAQUE) |
1264 | 0 | { |
1265 | | /* Create the opaque type. */ |
1266 | 0 | if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_OPAQUE, type->size)) < 0) |
1267 | 0 | return NC_EHDFERR; |
1268 | 0 | } |
1269 | 0 | else if (type->nc_type_class == NC_ENUM) |
1270 | 0 | { |
1271 | 0 | NC_ENUM_MEMBER_INFO_T *enum_m; |
1272 | 0 | int i; |
1273 | |
|
1274 | 0 | if (nclistlength(type->u.e.enum_member) == 0) |
1275 | 0 | return NC_EINVAL; |
1276 | | |
1277 | | /* Find the HDF typeid of the base type of this enum. */ |
1278 | 0 | if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.e.base_nc_typeid, |
1279 | 0 | &base_hdf_typeid, type->endianness))) |
1280 | 0 | return retval; |
1281 | | |
1282 | | /* Create an enum type. */ |
1283 | 0 | if ((hdf5_type->hdf_typeid = H5Tenum_create(base_hdf_typeid)) < 0) |
1284 | 0 | return NC_EHDFERR; |
1285 | | |
1286 | | /* Add all the members to the HDF5 type. */ |
1287 | 0 | for(i=0;i<nclistlength(type->u.e.enum_member);i++) { |
1288 | 0 | enum_m = (NC_ENUM_MEMBER_INFO_T*)nclistget(type->u.e.enum_member,i); |
1289 | 0 | if (H5Tenum_insert(hdf5_type->hdf_typeid, enum_m->name, enum_m->value) < 0) |
1290 | 0 | return NC_EHDFERR; |
1291 | 0 | } |
1292 | 0 | } |
1293 | 0 | else |
1294 | 0 | { |
1295 | 0 | LOG((0, "Unknown class: %d", type->nc_type_class)); |
1296 | 0 | return NC_EBADTYPE; |
1297 | 0 | } |
1298 | | |
1299 | | /* Commit the type. */ |
1300 | 0 | if (H5Tcommit(hdf5_grp->hdf_grpid, type->hdr.name, hdf5_type->hdf_typeid) < 0) |
1301 | 0 | return NC_EHDFERR; |
1302 | 0 | type->committed = NC_TRUE; |
1303 | 0 | LOG((4, "just committed type %s, HDF typeid: 0x%x", type->hdr.name, |
1304 | 0 | hdf5_type->hdf_typeid)); |
1305 | | |
1306 | | /* Later we will always use the native typeid. In this case, it is |
1307 | | * a copy of the same type pointed to by hdf_typeid, but it's |
1308 | | * easier to maintain a copy. */ |
1309 | 0 | if ((hdf5_type->native_hdf_typeid = H5Tget_native_type(hdf5_type->hdf_typeid, |
1310 | 0 | H5T_DIR_DEFAULT)) < 0) |
1311 | 0 | return NC_EHDFERR; |
1312 | | |
1313 | 0 | return NC_NOERR; |
1314 | 0 | } |
1315 | | |
1316 | | /** |
1317 | | * @internal Write an attribute, with value 1, to indicate that strict |
1318 | | * NC3 rules apply to this file. |
1319 | | * |
1320 | | * @param hdf_grpid HDF5 group ID. |
1321 | | * |
1322 | | * @returns NC_NOERR No error. |
1323 | | * @returns NC_EHDFERR HDF5 returned an error. |
1324 | | * @author Ed Hartnett |
1325 | | */ |
1326 | | static int |
1327 | | write_nc3_strict_att(hid_t hdf_grpid) |
1328 | 2.52k | { |
1329 | 2.52k | hid_t attid = 0, spaceid = 0; |
1330 | 2.52k | int one = 1; |
1331 | 2.52k | int retval = NC_NOERR; |
1332 | 2.52k | htri_t attr_exists; |
1333 | | |
1334 | | /* If the attribute already exists, call that a success and return |
1335 | | * NC_NOERR. */ |
1336 | 2.52k | if ((attr_exists = H5Aexists(hdf_grpid, NC3_STRICT_ATT_NAME)) < 0) |
1337 | 0 | return NC_EHDFERR; |
1338 | 2.52k | if (attr_exists) |
1339 | 414 | return NC_NOERR; |
1340 | | |
1341 | | /* Create the attribute to mark this as a file that needs to obey |
1342 | | * strict netcdf-3 rules. */ |
1343 | 2.11k | if ((spaceid = H5Screate(H5S_SCALAR)) < 0) |
1344 | 0 | BAIL(NC_EFILEMETA); |
1345 | 2.11k | if ((attid = H5Acreate(hdf_grpid, NC3_STRICT_ATT_NAME, |
1346 | 2.11k | H5T_NATIVE_INT, spaceid, H5P_DEFAULT)) < 0) |
1347 | 0 | BAIL(NC_EFILEMETA); |
1348 | 2.11k | if (H5Awrite(attid, H5T_NATIVE_INT, &one) < 0) |
1349 | 0 | BAIL(NC_EFILEMETA); |
1350 | | |
1351 | 2.11k | exit: |
1352 | 2.11k | if (spaceid > 0 && (H5Sclose(spaceid) < 0)) |
1353 | 0 | BAIL2(NC_EFILEMETA); |
1354 | 2.11k | if (attid > 0 && (H5Aclose(attid) < 0)) |
1355 | 0 | BAIL2(NC_EFILEMETA); |
1356 | 2.11k | return retval; |
1357 | 2.11k | } |
1358 | | |
1359 | | /** |
1360 | | * @internal Create a HDF5 group that is not the root group. HDF5 |
1361 | | * properties are set in the group to ensure that objects and |
1362 | | * attributes are kept in creation order, instead of alphebetical |
1363 | | * order (the HDF5 default). |
1364 | | * |
1365 | | * @param grp Pointer to group info struct. |
1366 | | * |
1367 | | * @return NC_NOERR No error. |
1368 | | * @return NC_EHDFERR HDF5 error. |
1369 | | * @author Ed Hartnett |
1370 | | */ |
1371 | | static int |
1372 | | create_group(NC_GRP_INFO_T *grp) |
1373 | 0 | { |
1374 | 0 | NC_HDF5_GRP_INFO_T *hdf5_grp, *parent_hdf5_grp; |
1375 | 0 | hid_t gcpl_id = -1; |
1376 | 0 | int retval = NC_NOERR;; |
1377 | |
|
1378 | 0 | assert(grp && grp->format_grp_info && grp->parent && |
1379 | 0 | grp->parent->format_grp_info); |
1380 | | |
1381 | | /* Get HDF5 specific group info for group and parent. */ |
1382 | 0 | hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; |
1383 | 0 | parent_hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->parent->format_grp_info; |
1384 | 0 | assert(parent_hdf5_grp->hdf_grpid); |
1385 | | |
1386 | | /* Create group, with link_creation_order set in the group |
1387 | | * creation property list. */ |
1388 | 0 | if ((gcpl_id = H5Pcreate(H5P_GROUP_CREATE)) < 0) |
1389 | 0 | BAIL(NC_EHDFERR); |
1390 | | |
1391 | | /* Set track_times to be FALSE. */ |
1392 | 0 | if (H5Pset_obj_track_times(gcpl_id, 0) < 0) |
1393 | 0 | BAIL(NC_EHDFERR); |
1394 | | |
1395 | | /* Tell HDF5 to keep track of objects in creation order. */ |
1396 | 0 | if (H5Pset_link_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0) |
1397 | 0 | BAIL(NC_EHDFERR); |
1398 | | |
1399 | | /* Tell HDF5 to keep track of attributes in creation order. */ |
1400 | 0 | if (H5Pset_attr_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0) |
1401 | 0 | BAIL(NC_EHDFERR); |
1402 | | |
1403 | | /* Create the group. */ |
1404 | 0 | if ((hdf5_grp->hdf_grpid = H5Gcreate2(parent_hdf5_grp->hdf_grpid, grp->hdr.name, |
1405 | 0 | H5P_DEFAULT, gcpl_id, H5P_DEFAULT)) < 0) |
1406 | 0 | BAIL(NC_EHDFERR); |
1407 | | |
1408 | 0 | exit: |
1409 | 0 | if (gcpl_id > -1 && H5Pclose(gcpl_id) < 0) |
1410 | 0 | BAIL2(NC_EHDFERR); |
1411 | 0 | if (retval) |
1412 | 0 | if (hdf5_grp->hdf_grpid > 0 && H5Gclose(hdf5_grp->hdf_grpid) < 0) |
1413 | 0 | BAIL2(NC_EHDFERR); |
1414 | 0 | return retval; |
1415 | 0 | } |
1416 | | |
1417 | | /** |
1418 | | * @internal After all the datasets of the file have been read, it's |
1419 | | * time to sort the wheat from the chaff. Which of the datasets are |
1420 | | * netCDF dimensions, and which are coordinate variables, and which |
1421 | | * are non-coordinate variables. |
1422 | | * |
1423 | | * @param grp Pointer to group info struct. |
1424 | | * |
1425 | | * @return ::NC_NOERR No error. |
1426 | | * @author Ed Hartnett |
1427 | | */ |
1428 | | static int |
1429 | | attach_dimscales(NC_GRP_INFO_T *grp) |
1430 | 7.89k | { |
1431 | 7.89k | NC_VAR_INFO_T *var; |
1432 | 7.89k | NC_HDF5_VAR_INFO_T *hdf5_var; |
1433 | 7.89k | int d, v; |
1434 | | |
1435 | | /* Attach dimension scales. */ |
1436 | 91.5k | for (v = 0; v < ncindexsize(grp->vars); v++) |
1437 | 83.6k | { |
1438 | | /* Get pointer to var and HDF5-specific var info. */ |
1439 | 83.6k | var = (NC_VAR_INFO_T *)ncindexith(grp->vars, v); |
1440 | 83.6k | assert(var && var->format_var_info); |
1441 | 83.6k | hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; |
1442 | | |
1443 | | /* Scales themselves do not attach. But I really wish they |
1444 | | * would. */ |
1445 | 83.6k | if (var->dimscale) |
1446 | 2.36k | continue; |
1447 | | |
1448 | | /* Find the scale for each dimension, if any, and attach it. */ |
1449 | 130k | for (d = 0; d < var->ndims; d++) |
1450 | 49.3k | { |
1451 | | /* Is there a dimscale for this dimension? */ |
1452 | 49.3k | if (var->dimscale_attached) |
1453 | 49.3k | { |
1454 | 49.3k | if (!var->dimscale_attached[d]) |
1455 | 41.3k | { |
1456 | 41.3k | hid_t dsid; /* Dataset ID for dimension */ |
1457 | 41.3k | assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] && |
1458 | 41.3k | var->dim[d]->format_dim_info); |
1459 | | |
1460 | 41.3k | LOG((2, "%s: attaching scale for dimid %d to var %s", |
1461 | 41.3k | __func__, var->dimids[d], var->hdr.name)); |
1462 | | |
1463 | | /* Find dataset ID for dimension */ |
1464 | 41.3k | if (var->dim[d]->coord_var) |
1465 | 13.5k | dsid = ((NC_HDF5_VAR_INFO_T *)(var->dim[d]->coord_var->format_var_info))->hdf_datasetid; |
1466 | 27.8k | else |
1467 | 27.8k | dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid; |
1468 | 41.3k | assert(dsid > 0); |
1469 | | |
1470 | | /* Attach the scale. */ |
1471 | 41.3k | if (H5DSattach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0) |
1472 | 0 | return NC_EHDFERR; |
1473 | 41.3k | var->dimscale_attached[d] = NC_TRUE; |
1474 | 41.3k | } |
1475 | 49.3k | } |
1476 | 49.3k | } |
1477 | 81.3k | } |
1478 | | |
1479 | 7.89k | return NC_NOERR; |
1480 | 7.89k | } |
1481 | | |
1482 | | /** |
1483 | | * @internal Does a variable exist? |
1484 | | * |
1485 | | * @param grpid HDF5 group ID. |
1486 | | * @param name Name of variable. |
1487 | | * @param exists Pointer that gets 1 of the variable exists, 0 otherwise. |
1488 | | * |
1489 | | * @return ::NC_NOERR No error. |
1490 | | * @author Ed Hartnett |
1491 | | */ |
1492 | | static int |
1493 | | var_exists(hid_t grpid, char *name, nc_bool_t *exists) |
1494 | 0 | { |
1495 | 0 | htri_t link_exists; |
1496 | | |
1497 | | /* Reset the boolean */ |
1498 | 0 | *exists = NC_FALSE; |
1499 | | |
1500 | | /* Check if the object name exists in the group */ |
1501 | 0 | if ((link_exists = H5Lexists(grpid, name, H5P_DEFAULT)) < 0) |
1502 | 0 | return NC_EHDFERR; |
1503 | 0 | if (link_exists) |
1504 | 0 | { |
1505 | 0 | H5G_stat_t statbuf; |
1506 | | |
1507 | | /* Get info about the object */ |
1508 | 0 | if (H5Gget_objinfo(grpid, name, 1, &statbuf) < 0) |
1509 | 0 | return NC_EHDFERR; |
1510 | | |
1511 | 0 | if (H5G_DATASET == statbuf.type) |
1512 | 0 | *exists = NC_TRUE; |
1513 | 0 | } |
1514 | | |
1515 | 0 | return NC_NOERR; |
1516 | 0 | } |
1517 | | |
1518 | | /** |
1519 | | * @internal Convert a coordinate variable HDF5 dataset into one that |
1520 | | * is not a coordinate variable. This happens during renaming of vars |
1521 | | * and dims. This function removes the HDF5 NAME and CLASS attributes |
1522 | | * associated with dimension scales, and also the NC_DIMID_ATT_NAME |
1523 | | * attribute which may be present, and, if it does, holds the dimid of |
1524 | | * the coordinate variable. |
1525 | | * |
1526 | | * @param hdf_datasetid The HDF5 dataset ID of the coordinate variable |
1527 | | * dataset. |
1528 | | * |
1529 | | * @return ::NC_NOERR No error. |
1530 | | * @return ::NC_EHDFERR HDF5 error. |
1531 | | * @author Ed Hartnett |
1532 | | */ |
1533 | | static int |
1534 | | remove_coord_atts(hid_t hdf_datasetid) |
1535 | 0 | { |
1536 | 0 | htri_t attr_exists; |
1537 | | |
1538 | | /* If the variable dataset has an optional NC_DIMID_ATT_NAME |
1539 | | * attribute, delete it. */ |
1540 | 0 | if ((attr_exists = H5Aexists(hdf_datasetid, NC_DIMID_ATT_NAME)) < 0) |
1541 | 0 | return NC_EHDFERR; |
1542 | 0 | if (attr_exists) |
1543 | 0 | { |
1544 | 0 | if (H5Adelete(hdf_datasetid, NC_DIMID_ATT_NAME) < 0) |
1545 | 0 | return NC_EHDFERR; |
1546 | 0 | } |
1547 | | |
1548 | | /* Remove the dimension scale 'CLASS' & 'NAME' attributes. */ |
1549 | 0 | if ((attr_exists = H5Aexists(hdf_datasetid, |
1550 | 0 | HDF5_DIMSCALE_CLASS_ATT_NAME)) < 0) |
1551 | 0 | return NC_EHDFERR; |
1552 | 0 | if (attr_exists) |
1553 | 0 | { |
1554 | 0 | if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_CLASS_ATT_NAME) < 0) |
1555 | 0 | return NC_EHDFERR; |
1556 | 0 | } |
1557 | 0 | if ((attr_exists = H5Aexists(hdf_datasetid, |
1558 | 0 | HDF5_DIMSCALE_NAME_ATT_NAME)) < 0) |
1559 | 0 | return NC_EHDFERR; |
1560 | 0 | if (attr_exists) |
1561 | 0 | { |
1562 | 0 | if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_NAME_ATT_NAME) < 0) |
1563 | 0 | return NC_EHDFERR; |
1564 | 0 | } |
1565 | 0 | return NC_NOERR; |
1566 | 0 | } |
1567 | | |
1568 | | /** |
1569 | | * @internal This function writes a variable. The principle difficulty |
1570 | | * comes from the possibility that this is a coordinate variable, and |
1571 | | * was already written to the file as a dimension-only dimscale. If |
1572 | | * this occurs, then it must be deleted and recreated. |
1573 | | * |
1574 | | * @param var Pointer to variable info struct. |
1575 | | * @param grp Pointer to group info struct. |
1576 | | * @param write_dimid |
1577 | | * |
1578 | | * @returns NC_NOERR No error. |
1579 | | * @returns NC_EHDFERR HDF5 returned an error. |
1580 | | * @author Ed Hartnett, Quincey Koziol |
1581 | | */ |
1582 | | static int |
1583 | | write_var(NC_VAR_INFO_T *var, NC_GRP_INFO_T *grp, nc_bool_t write_dimid) |
1584 | 83.6k | { |
1585 | 83.6k | NC_HDF5_GRP_INFO_T *hdf5_grp; |
1586 | 83.6k | NC_HDF5_VAR_INFO_T *hdf5_var; |
1587 | 83.6k | nc_bool_t replace_existing_var = NC_FALSE; |
1588 | 83.6k | int retval; |
1589 | | |
1590 | 83.6k | assert(var && var->format_var_info && grp && grp->format_grp_info); |
1591 | | |
1592 | 83.6k | LOG((4, "%s: writing var %s", __func__, var->hdr.name)); |
1593 | | |
1594 | | /* Get HDF5-specific group and var info. */ |
1595 | 83.6k | hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; |
1596 | 83.6k | hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; |
1597 | | |
1598 | | /* If the variable has already been created & the fill value changed, |
1599 | | * indicate that the existing variable should be replaced. */ |
1600 | 83.6k | if (var->created && var->fill_val_changed) |
1601 | 0 | { |
1602 | 0 | replace_existing_var = NC_TRUE; |
1603 | 0 | var->fill_val_changed = NC_FALSE; |
1604 | | /* If the variable is going to be replaced, we need to flag any |
1605 | | other attributes associated with the variable as 'dirty', or |
1606 | | else *only* the fill value attribute will be copied over and |
1607 | | the rest will be lost. See |
1608 | | https://github.com/Unidata/netcdf-c/issues/239 */ |
1609 | 0 | flag_atts_dirty(var->att); |
1610 | 0 | } |
1611 | | |
1612 | | /* Is this a coordinate var that has already been created in |
1613 | | * the HDF5 file as a dimscale dataset? Check for dims with the |
1614 | | * same name in this group. If there is one, check to see if |
1615 | | * this object exists in the HDF group. */ |
1616 | 83.6k | if (var->became_coord_var) |
1617 | 0 | { |
1618 | 0 | if ((NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name)) |
1619 | 0 | { |
1620 | 0 | nc_bool_t exists; |
1621 | |
|
1622 | 0 | if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists))) |
1623 | 0 | return retval; |
1624 | 0 | if (exists) |
1625 | 0 | { |
1626 | | /* Indicate that the variable already exists, and should |
1627 | | * be replaced. */ |
1628 | 0 | replace_existing_var = NC_TRUE; |
1629 | 0 | flag_atts_dirty(var->att); |
1630 | 0 | } |
1631 | 0 | } |
1632 | 0 | } |
1633 | | |
1634 | | /* Check dims if the variable will be replaced, so that the |
1635 | | * dimensions will be de-attached and re-attached correctly. */ |
1636 | 83.6k | if (replace_existing_var) |
1637 | 0 | { |
1638 | 0 | NC_DIM_INFO_T *d1; |
1639 | | |
1640 | | /* Is there a dim with this var's name? */ |
1641 | 0 | if ((d1 = (NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name))) |
1642 | 0 | { |
1643 | 0 | nc_bool_t exists; |
1644 | 0 | assert(d1->format_dim_info && d1->hdr.name); |
1645 | |
|
1646 | 0 | if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists))) |
1647 | 0 | return retval; |
1648 | 0 | if (exists) |
1649 | 0 | { |
1650 | 0 | hid_t dsid; |
1651 | | |
1652 | | /* Find dataset ID for dimension */ |
1653 | 0 | if (d1->coord_var) |
1654 | 0 | dsid = ((NC_HDF5_VAR_INFO_T *)d1->coord_var->format_var_info)->hdf_datasetid; |
1655 | 0 | else |
1656 | 0 | dsid = ((NC_HDF5_DIM_INFO_T *)d1->format_dim_info)->hdf_dimscaleid; |
1657 | 0 | assert(dsid > 0); |
1658 | | |
1659 | | /* If we're replacing an existing dimscale dataset, go to |
1660 | | * every var in the file and detach this dimension scale, |
1661 | | * because we have to delete it. */ |
1662 | 0 | if ((retval = rec_detach_scales(grp->nc4_info->root_grp, |
1663 | 0 | var->dimids[0], dsid))) |
1664 | 0 | return retval; |
1665 | 0 | } |
1666 | 0 | } |
1667 | 0 | } |
1668 | | |
1669 | | /* If this is not a dimension scale, remove any attached scales, |
1670 | | * and delete dimscale attributes from the var. */ |
1671 | 83.6k | if (var->was_coord_var && var->dimscale_attached) |
1672 | 0 | { |
1673 | 0 | int d; |
1674 | | |
1675 | | /* If the variable already exists in the file, Remove any dimension scale |
1676 | | * attributes from it, if they exist. */ |
1677 | 0 | if (var->created) |
1678 | 0 | if ((retval = remove_coord_atts(hdf5_var->hdf_datasetid))) |
1679 | 0 | return retval; |
1680 | | |
1681 | | /* If this is a regular var, detach all its dim scales. */ |
1682 | 0 | for (d = 0; d < var->ndims; d++) |
1683 | 0 | { |
1684 | 0 | if (var->dimscale_attached[d]) |
1685 | 0 | { |
1686 | 0 | hid_t dsid; /* Dataset ID for dimension */ |
1687 | 0 | assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] && |
1688 | 0 | var->dim[d]->format_dim_info); |
1689 | | |
1690 | | /* Find dataset ID for dimension */ |
1691 | 0 | if (var->dim[d]->coord_var) |
1692 | 0 | dsid = ((NC_HDF5_VAR_INFO_T *)var->dim[d]->coord_var->format_var_info)->hdf_datasetid; |
1693 | 0 | else |
1694 | 0 | dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid; |
1695 | 0 | assert(dsid > 0); |
1696 | | |
1697 | | /* Detach this dim scale. */ |
1698 | 0 | if (H5DSdetach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0) |
1699 | 0 | return NC_EHDFERR; |
1700 | 0 | var->dimscale_attached[d] = NC_FALSE; |
1701 | 0 | } |
1702 | 0 | } |
1703 | 0 | } |
1704 | | |
1705 | | /* Delete the HDF5 dataset that is to be replaced. */ |
1706 | 83.6k | if (replace_existing_var) |
1707 | 0 | { |
1708 | | /* Free the HDF5 dataset id. */ |
1709 | 0 | if (hdf5_var->hdf_datasetid && H5Dclose(hdf5_var->hdf_datasetid) < 0) |
1710 | 0 | return NC_EHDFERR; |
1711 | 0 | hdf5_var->hdf_datasetid = 0; |
1712 | | |
1713 | | /* Now delete the variable. */ |
1714 | 0 | if (H5Gunlink(hdf5_grp->hdf_grpid, var->hdr.name) < 0) |
1715 | 0 | return NC_EDIMMETA; |
1716 | 0 | } |
1717 | | |
1718 | | /* Create the dataset. */ |
1719 | 83.6k | if (var->is_new_var || replace_existing_var) |
1720 | 70.3k | { |
1721 | 70.3k | if ((retval = var_create_dataset(grp, var, write_dimid))) |
1722 | 0 | return retval; |
1723 | 70.3k | } |
1724 | 13.3k | else |
1725 | 13.3k | { |
1726 | 13.3k | if (write_dimid && var->ndims) |
1727 | 2.45k | if ((retval = write_netcdf4_dimid(hdf5_var->hdf_datasetid, |
1728 | 2.45k | var->dimids[0]))) |
1729 | 0 | return retval; |
1730 | 13.3k | } |
1731 | | |
1732 | 83.6k | if (replace_existing_var) |
1733 | 0 | { |
1734 | | /* If this is a dimension scale, reattach the scale everywhere it |
1735 | | * is used. (Recall that netCDF dimscales are always 1-D). */ |
1736 | 0 | if(var->dimscale) |
1737 | 0 | { |
1738 | 0 | if ((retval = rec_reattach_scales(grp->nc4_info->root_grp, |
1739 | 0 | var->dimids[0], hdf5_var->hdf_datasetid))) |
1740 | 0 | return retval; |
1741 | 0 | } |
1742 | | /* If it's not a dimension scale, clear the dimscale attached flags, |
1743 | | * so the dimensions are re-attached. */ |
1744 | 0 | else |
1745 | 0 | { |
1746 | 0 | if (var->dimscale_attached) |
1747 | 0 | memset(var->dimscale_attached, 0, sizeof(nc_bool_t) * var->ndims); |
1748 | 0 | } |
1749 | 0 | } |
1750 | | |
1751 | | /* Clear coord. var state transition flags */ |
1752 | 83.6k | var->was_coord_var = NC_FALSE; |
1753 | 83.6k | var->became_coord_var = NC_FALSE; |
1754 | | |
1755 | | /* Now check the attributes for this var. */ |
1756 | 83.6k | if (var->attr_dirty) |
1757 | 0 | { |
1758 | | /* Write attributes for this var. */ |
1759 | 0 | if ((retval = write_attlist(var->att, var->hdr.id, grp))) |
1760 | 0 | return retval; |
1761 | 0 | var->attr_dirty = NC_FALSE; |
1762 | 0 | } |
1763 | | |
1764 | 83.6k | return NC_NOERR; |
1765 | 83.6k | } |
1766 | | |
1767 | | /** |
1768 | | * @internal Write a HDF5 dataset which is a dimension without a |
1769 | | * coordinate variable. This is a special 1-D dataset. |
1770 | | * |
1771 | | * @param dim Pointer to dim info struct. |
1772 | | * @param grp Pointer to group info struct. |
1773 | | * @param write_dimid |
1774 | | * |
1775 | | * @returns ::NC_NOERR No error. |
1776 | | * @returns ::NC_EPERM Read-only file. |
1777 | | * @returns ::NC_EHDFERR HDF5 returned error. |
1778 | | * @author Ed Hartnett |
1779 | | */ |
1780 | | int |
1781 | | nc4_create_dim_wo_var(NC_DIM_INFO_T *dim) |
1782 | 30.7k | { |
1783 | 30.7k | NC_HDF5_DIM_INFO_T *hdf5_dim; |
1784 | 30.7k | NC_HDF5_GRP_INFO_T *hdf5_grp; |
1785 | 30.7k | hid_t spaceid = -1, create_propid = -1; |
1786 | 30.7k | hsize_t dims[1], max_dims[1], chunk_dims[1] = {1}; |
1787 | 30.7k | char dimscale_wo_var[NC_MAX_NAME]; |
1788 | 30.7k | int retval = NC_NOERR; |
1789 | | |
1790 | 30.7k | LOG((4, "%s: creating dim %s", __func__, dim->hdr.name)); |
1791 | | |
1792 | | /* Sanity check */ |
1793 | 30.7k | assert(!dim->coord_var); |
1794 | | |
1795 | | /* Get HDF5-specific dim and group info. */ |
1796 | 30.7k | hdf5_grp = (NC_HDF5_GRP_INFO_T *)dim->container->format_grp_info; |
1797 | 30.7k | hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; |
1798 | | |
1799 | | /* Create a property list. */ |
1800 | 30.7k | if ((create_propid = H5Pcreate(H5P_DATASET_CREATE)) < 0) |
1801 | 0 | BAIL(NC_EHDFERR); |
1802 | | |
1803 | | /* Turn off recording of times associated with this object. */ |
1804 | 30.7k | if (H5Pset_obj_track_times(create_propid, 0) < 0) |
1805 | 0 | BAIL(NC_EHDFERR); |
1806 | | |
1807 | | /* Set size of dataset to size of dimension. */ |
1808 | 30.7k | dims[0] = dim->len; |
1809 | 30.7k | max_dims[0] = dim->len; |
1810 | | |
1811 | | /* If this dimension scale is unlimited (i.e. it's an unlimited |
1812 | | * dimension), then set up chunking, with a chunksize of 1. */ |
1813 | 30.7k | if (dim->unlimited) |
1814 | 18.2k | { |
1815 | 18.2k | max_dims[0] = H5S_UNLIMITED; |
1816 | 18.2k | if (H5Pset_chunk(create_propid, 1, chunk_dims) < 0) |
1817 | 0 | BAIL(NC_EHDFERR); |
1818 | 18.2k | } |
1819 | | |
1820 | | /* Set up space. */ |
1821 | 30.7k | if ((spaceid = H5Screate_simple(1, dims, max_dims)) < 0) |
1822 | 0 | BAIL(NC_EHDFERR); |
1823 | | |
1824 | | /* Turn on creation-order tracking. */ |
1825 | 30.7k | if (H5Pset_attr_creation_order(create_propid, H5P_CRT_ORDER_TRACKED| |
1826 | 30.7k | H5P_CRT_ORDER_INDEXED) < 0) |
1827 | 0 | BAIL(NC_EHDFERR); |
1828 | | |
1829 | | /* Create the dataset that will be the dimension scale. */ |
1830 | 30.7k | LOG((4, "%s: about to H5Dcreate1 a dimscale dataset %s", __func__, |
1831 | 30.7k | dim->hdr.name)); |
1832 | 30.7k | if ((hdf5_dim->hdf_dimscaleid = H5Dcreate2(hdf5_grp->hdf_grpid, dim->hdr.name, |
1833 | 30.7k | H5T_IEEE_F32BE, spaceid, |
1834 | 30.7k | H5P_DEFAULT, create_propid, |
1835 | 30.7k | H5P_DEFAULT)) < 0) |
1836 | 0 | BAIL(NC_EHDFERR); |
1837 | | |
1838 | | /* Indicate that this is a scale. Also indicate that not |
1839 | | * be shown to the user as a variable. It is hidden. It is |
1840 | | * a DIM WITHOUT A VARIABLE! */ |
1841 | 30.7k | sprintf(dimscale_wo_var, "%s%10d", DIM_WITHOUT_VARIABLE, (int)dim->len); |
1842 | 30.7k | if (H5DSset_scale(hdf5_dim->hdf_dimscaleid, dimscale_wo_var) < 0) |
1843 | 0 | BAIL(NC_EHDFERR); |
1844 | | |
1845 | | /* Since this dimension was created out of order, we cannot rely on |
1846 | | * it getting the correct dimid on file open. We must assign it |
1847 | | * explicitly. */ |
1848 | 30.7k | if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id))) |
1849 | 0 | BAIL(retval); |
1850 | | |
1851 | 30.7k | exit: |
1852 | 30.7k | if (spaceid > 0 && H5Sclose(spaceid) < 0) |
1853 | 0 | BAIL2(NC_EHDFERR); |
1854 | 30.7k | if (create_propid > 0 && H5Pclose(create_propid) < 0) |
1855 | 0 | BAIL2(NC_EHDFERR); |
1856 | 30.7k | return retval; |
1857 | 30.7k | } |
1858 | | |
1859 | | /** |
1860 | | * @internal Write a dimension. |
1861 | | * |
1862 | | * @param dim Pointer to dim info struct. |
1863 | | * @param grp Pointer to group info struct. |
1864 | | * @param write_dimid |
1865 | | * |
1866 | | * @returns ::NC_NOERR No error. |
1867 | | * @returns ::NC_EPERM Read-only file. |
1868 | | * @returns ::NC_EHDFERR HDF5 returned error. |
1869 | | * @author Ed Hartnett |
1870 | | */ |
1871 | | static int |
1872 | | write_dim(NC_DIM_INFO_T *dim, NC_GRP_INFO_T *grp, nc_bool_t write_dimid) |
1873 | 37.2k | { |
1874 | 37.2k | NC_HDF5_DIM_INFO_T *hdf5_dim; |
1875 | 37.2k | int retval = NC_NOERR; |
1876 | | |
1877 | 37.2k | assert(dim && dim->format_dim_info && grp && grp->format_grp_info); |
1878 | | |
1879 | | /* Get HDF5-specific dim and group info. */ |
1880 | 37.2k | hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; |
1881 | | |
1882 | | /* If there's no dimscale dataset for this dim, create one, |
1883 | | * and mark that it should be hidden from netCDF as a |
1884 | | * variable. (That is, it should appear as a dimension |
1885 | | * without an associated variable.) */ |
1886 | 37.2k | if (!hdf5_dim->hdf_dimscaleid) |
1887 | 30.7k | if ((retval = nc4_create_dim_wo_var(dim))) |
1888 | 0 | BAIL(retval); |
1889 | | |
1890 | | /* Did we extend an unlimited dimension? */ |
1891 | 37.2k | if (dim->extended) |
1892 | 0 | { |
1893 | 0 | NC_VAR_INFO_T *v1 = NULL; |
1894 | |
|
1895 | 0 | assert(dim->unlimited); |
1896 | | |
1897 | | /* If this is a dimension with an associated coordinate var, |
1898 | | * then update the length of that coord var. */ |
1899 | 0 | v1 = dim->coord_var; |
1900 | 0 | if (v1) |
1901 | 0 | { |
1902 | 0 | NC_HDF5_VAR_INFO_T *hdf5_v1; |
1903 | 0 | hsize_t *new_size; |
1904 | 0 | int d1; |
1905 | |
|
1906 | 0 | hdf5_v1 = (NC_HDF5_VAR_INFO_T *)v1->format_var_info; |
1907 | | |
1908 | | /* Extend the dimension scale dataset to reflect the new |
1909 | | * length of the dimension. */ |
1910 | 0 | if (!(new_size = malloc(v1->ndims * sizeof(hsize_t)))) |
1911 | 0 | BAIL(NC_ENOMEM); |
1912 | 0 | for (d1 = 0; d1 < v1->ndims; d1++) |
1913 | 0 | { |
1914 | 0 | assert(v1->dim[d1] && v1->dim[d1]->hdr.id == v1->dimids[d1]); |
1915 | 0 | new_size[d1] = v1->dim[d1]->len; |
1916 | 0 | } |
1917 | 0 | if (H5Dset_extent(hdf5_v1->hdf_datasetid, new_size) < 0) |
1918 | 0 | BAIL(NC_EHDFERR); |
1919 | 0 | free(new_size); |
1920 | 0 | } |
1921 | 0 | } |
1922 | | |
1923 | | /* If desired, write the secret dimid. This will be used instead of |
1924 | | * the dimid that the dimension would otherwise receive based on |
1925 | | * creation order. This can be necessary when dims and their |
1926 | | * coordinate variables were created in different order. */ |
1927 | 37.2k | if (write_dimid && hdf5_dim->hdf_dimscaleid) |
1928 | 11.8k | if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id))) |
1929 | 0 | BAIL(retval); |
1930 | | |
1931 | 37.2k | exit: |
1932 | | |
1933 | 37.2k | return retval; |
1934 | 37.2k | } |
1935 | | |
1936 | | /** |
1937 | | * @internal Recursively write all the metadata in a group. Groups and |
1938 | | * types have all already been written. Propagate bad cooordinate |
1939 | | * order to subgroups, if detected. |
1940 | | * |
1941 | | * @param grp Pointer to group info struct. |
1942 | | * @param bad_coord_order 1 if there is a bad coordinate order. |
1943 | | * |
1944 | | * @returns NC_NOERR No error. |
1945 | | * @returns NC_EHDFERR HDF5 returned an error. |
1946 | | * @author Ed Hartnett |
1947 | | */ |
1948 | | int |
1949 | | nc4_rec_write_metadata(NC_GRP_INFO_T *grp, nc_bool_t bad_coord_order) |
1950 | 7.89k | { |
1951 | 7.89k | NC_DIM_INFO_T *dim = NULL; |
1952 | 7.89k | NC_VAR_INFO_T *var = NULL; |
1953 | 7.89k | NC_GRP_INFO_T *child_grp = NULL; |
1954 | 7.89k | int coord_varid = -1; |
1955 | 7.89k | int var_index = 0; |
1956 | 7.89k | int dim_index = 0; |
1957 | 7.89k | int retval; |
1958 | 7.89k | int i; |
1959 | | |
1960 | 7.89k | assert(grp && grp->hdr.name && |
1961 | 7.89k | ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid); |
1962 | 7.89k | LOG((3, "%s: grp->hdr.name %s, bad_coord_order %d", __func__, grp->hdr.name, |
1963 | 7.89k | bad_coord_order)); |
1964 | | |
1965 | | /* Write global attributes for this group. */ |
1966 | 7.89k | if ((retval = write_attlist(grp->att, NC_GLOBAL, grp))) |
1967 | 0 | return retval; |
1968 | | |
1969 | | /* Set the pointers to the beginning of the list of dims & vars in this |
1970 | | * group. */ |
1971 | 7.89k | dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, dim_index); |
1972 | 7.89k | var = (NC_VAR_INFO_T *)ncindexith(grp->vars, var_index); |
1973 | | |
1974 | | /* Because of HDF5 ordering the dims and vars have to be stored in |
1975 | | * this way to ensure that the dims and coordinate vars come out in |
1976 | | * the correct order. */ |
1977 | 17.6k | while (dim || var) |
1978 | 9.76k | { |
1979 | 9.76k | nc_bool_t found_coord, wrote_coord; |
1980 | | |
1981 | | /* Write non-coord dims in order, stopping at the first one that |
1982 | | * has an associated coord var. */ |
1983 | 49.3k | for (found_coord = NC_FALSE; dim && !found_coord; ) |
1984 | 39.5k | { |
1985 | 39.5k | if (!dim->coord_var) |
1986 | 37.2k | { |
1987 | 37.2k | if ((retval = write_dim(dim, grp, bad_coord_order))) |
1988 | 0 | return retval; |
1989 | 37.2k | } |
1990 | 2.36k | else |
1991 | 2.36k | { |
1992 | 2.36k | coord_varid = dim->coord_var->hdr.id; |
1993 | 2.36k | found_coord = NC_TRUE; |
1994 | 2.36k | } |
1995 | 39.5k | dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, ++dim_index); |
1996 | 39.5k | } |
1997 | | |
1998 | | /* Write each var. When we get to the coord var we are waiting |
1999 | | * for (if any), then we break after writing it. */ |
2000 | 93.4k | for (wrote_coord = NC_FALSE; var && !wrote_coord; ) |
2001 | 83.6k | { |
2002 | 83.6k | if ((retval = write_var(var, grp, bad_coord_order))) |
2003 | 0 | return retval; |
2004 | 83.6k | if (found_coord && var->hdr.id == coord_varid) |
2005 | 2.15k | wrote_coord = NC_TRUE; |
2006 | 83.6k | var = (NC_VAR_INFO_T *)ncindexith(grp->vars, ++var_index); |
2007 | 83.6k | } |
2008 | 9.76k | } /* end while */ |
2009 | | |
2010 | | /* Attach dimscales to vars in this group. */ |
2011 | 7.89k | if ((retval = attach_dimscales(grp))) |
2012 | 0 | return retval; |
2013 | | |
2014 | | /* If there are any child groups, write their metadata. */ |
2015 | 7.89k | for (i = 0; i < ncindexsize(grp->children); i++) |
2016 | 0 | { |
2017 | 0 | child_grp = (NC_GRP_INFO_T *)ncindexith(grp->children, i); |
2018 | 0 | assert(child_grp); |
2019 | 0 | if ((retval = nc4_rec_write_metadata(child_grp, bad_coord_order))) |
2020 | 0 | return retval; |
2021 | 0 | } |
2022 | 7.89k | return NC_NOERR; |
2023 | 7.89k | } |
2024 | | |
2025 | | /** |
2026 | | * @internal Recursively write all groups and types. |
2027 | | * |
2028 | | * @param grp Pointer to group info struct. |
2029 | | * |
2030 | | * @returns NC_NOERR No error. |
2031 | | * @returns NC_EHDFERR HDF5 returned an error. |
2032 | | * @author Ed Hartnett |
2033 | | */ |
2034 | | int |
2035 | | nc4_rec_write_groups_types(NC_GRP_INFO_T *grp) |
2036 | 7.89k | { |
2037 | 7.89k | NC_GRP_INFO_T *child_grp; |
2038 | 7.89k | NC_HDF5_GRP_INFO_T *hdf5_grp; |
2039 | 7.89k | NC_TYPE_INFO_T *type; |
2040 | 7.89k | int retval; |
2041 | 7.89k | int i; |
2042 | | |
2043 | 7.89k | assert(grp && grp->hdr.name && grp->format_grp_info); |
2044 | 7.89k | LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name)); |
2045 | | |
2046 | | /* Get HDF5-specific group info. */ |
2047 | 7.89k | hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; |
2048 | | |
2049 | | /* Create the group in the HDF5 file if it doesn't exist. */ |
2050 | 7.89k | if (!hdf5_grp->hdf_grpid) |
2051 | 0 | if ((retval = create_group(grp))) |
2052 | 0 | return retval; |
2053 | | |
2054 | | /* If this is the root group of a file with strict NC3 rules, write |
2055 | | * an attribute. But don't leave the attribute open. */ |
2056 | 7.89k | if (!grp->parent && (grp->nc4_info->cmode & NC_CLASSIC_MODEL)) |
2057 | 2.52k | if ((retval = write_nc3_strict_att(hdf5_grp->hdf_grpid))) |
2058 | 0 | return retval; |
2059 | | |
2060 | | /* If there are any user-defined types, write them now. */ |
2061 | 7.89k | for(i=0;i<ncindexsize(grp->type);i++) { |
2062 | 0 | type = (NC_TYPE_INFO_T *)ncindexith(grp->type, i); |
2063 | 0 | assert(type); |
2064 | 0 | if ((retval = commit_type(grp, type))) |
2065 | 0 | return retval; |
2066 | 0 | } |
2067 | | |
2068 | | /* If there are any child groups, write their groups and types. */ |
2069 | 7.89k | for(i=0;i<ncindexsize(grp->children);i++) { |
2070 | 0 | if((child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children,i)) == NULL) continue; |
2071 | 0 | if ((retval = nc4_rec_write_groups_types(child_grp))) |
2072 | 0 | return retval; |
2073 | 0 | } |
2074 | 7.89k | return NC_NOERR; |
2075 | 7.89k | } |
2076 | | |
2077 | | /** |
2078 | | * @internal In our first pass through the data, we may have |
2079 | | * encountered variables before encountering their dimscales, so go |
2080 | | * through the vars in this file and make sure we've got a dimid for |
2081 | | * each. |
2082 | | * |
2083 | | * @param grp Pointer to group info struct. |
2084 | | * |
2085 | | * @returns NC_NOERR No error. |
2086 | | * @returns NC_EHDFERR HDF5 returned an error. |
2087 | | * @returns NC_ENOMEM Out of memory. |
2088 | | * @author Ed Hartnett |
2089 | | */ |
2090 | | int |
2091 | | nc4_rec_match_dimscales(NC_GRP_INFO_T *grp) |
2092 | 0 | { |
2093 | 0 | NC_GRP_INFO_T *g; |
2094 | 0 | NC_VAR_INFO_T *var; |
2095 | 0 | NC_DIM_INFO_T *dim; |
2096 | 0 | int retval = NC_NOERR; |
2097 | 0 | int i; |
2098 | |
|
2099 | 0 | assert(grp && grp->hdr.name); |
2100 | 0 | LOG((4, "%s: grp->hdr.name %s", __func__, grp->hdr.name)); |
2101 | | |
2102 | | /* Perform var dimscale match for child groups. */ |
2103 | 0 | for (i = 0; i < ncindexsize(grp->children); i++) |
2104 | 0 | { |
2105 | 0 | g = (NC_GRP_INFO_T*)ncindexith(grp->children, i); |
2106 | 0 | assert(g); |
2107 | 0 | if ((retval = nc4_rec_match_dimscales(g))) |
2108 | 0 | return retval; |
2109 | 0 | } |
2110 | | |
2111 | | /* Check all the vars in this group. If they have dimscale info, |
2112 | | * try and find a dimension for them. */ |
2113 | 0 | for (i = 0; i < ncindexsize(grp->vars); i++) |
2114 | 0 | { |
2115 | 0 | NC_HDF5_VAR_INFO_T *hdf5_var; |
2116 | 0 | int ndims; |
2117 | 0 | int d; |
2118 | | |
2119 | | /* Get pointer to var and to the HDF5-specific var info. */ |
2120 | 0 | var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i); |
2121 | 0 | assert(var && var->format_var_info); |
2122 | 0 | hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; |
2123 | | |
2124 | | /* Check all vars and see if dim[i] != NULL if dimids[i] valid. */ |
2125 | | /* This loop is very odd. Under normal circumstances, var->dimid[d] is zero |
2126 | | (from the initial calloc) which is a legitimate dimid. The code does not |
2127 | | distinquish this case from the dimscale case where the id might actually |
2128 | | be defined. |
2129 | | The original nc4_find_dim searched up the group tree looking for the given |
2130 | | dimid in one of the dim lists associated with each ancestor group. |
2131 | | I changed nc4_fnd_dim to use the dimid directly using h5->alldims. |
2132 | | However, here that is incorrect because it will find the dimid 0 always |
2133 | | (if any dimensions were defined). Except that when dimscale dimids have |
2134 | | been defined, one or more of the values in var->dimids will have a |
2135 | | legitimate value. |
2136 | | The solution I choose is to modify nc4_var_list_add to initialize dimids to |
2137 | | illegal values (-1). This is another example of the problems with dimscales. |
2138 | | */ |
2139 | 0 | ndims = var->ndims; |
2140 | 0 | for (d = 0; d < ndims; d++) |
2141 | 0 | { |
2142 | 0 | if (var->dim[d] == NULL) { |
2143 | 0 | nc4_find_dim(grp, var->dimids[d], &var->dim[d], NULL); |
2144 | 0 | } |
2145 | | /* assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d]); */ |
2146 | 0 | } |
2147 | | |
2148 | | /* Skip dimension scale variables */ |
2149 | 0 | if (!var->dimscale) |
2150 | 0 | { |
2151 | 0 | int d; |
2152 | 0 | int j; |
2153 | | |
2154 | | /* Are there dimscales for this variable? */ |
2155 | 0 | if (hdf5_var->dimscale_hdf5_objids) |
2156 | 0 | { |
2157 | 0 | for (d = 0; d < var->ndims; d++) |
2158 | 0 | { |
2159 | 0 | nc_bool_t finished = NC_FALSE; |
2160 | 0 | LOG((5, "%s: var %s has dimscale info...", __func__, var->hdr.name)); |
2161 | | |
2162 | | /* Check this and parent groups. */ |
2163 | 0 | for (g = grp; g && !finished; g = g->parent) |
2164 | 0 | { |
2165 | | /* Check all dims in this group. */ |
2166 | 0 | for (j = 0; j < ncindexsize(g->dim); j++) |
2167 | 0 | { |
2168 | | /* Get the HDF5 specific dim info. */ |
2169 | 0 | NC_HDF5_DIM_INFO_T *hdf5_dim; |
2170 | 0 | dim = (NC_DIM_INFO_T *)ncindexith(g->dim, j); |
2171 | 0 | assert(dim && dim->format_dim_info); |
2172 | 0 | hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; |
2173 | | |
2174 | | /* Check for exact match of fileno/objid arrays |
2175 | | * to find identical objects in HDF5 file. */ |
2176 | 0 | if (hdf5_var->dimscale_hdf5_objids[d].fileno[0] == hdf5_dim->hdf5_objid.fileno[0] && |
2177 | 0 | hdf5_var->dimscale_hdf5_objids[d].objno[0] == hdf5_dim->hdf5_objid.objno[0] && |
2178 | 0 | hdf5_var->dimscale_hdf5_objids[d].fileno[1] == hdf5_dim->hdf5_objid.fileno[1] && |
2179 | 0 | hdf5_var->dimscale_hdf5_objids[d].objno[1] == hdf5_dim->hdf5_objid.objno[1]) |
2180 | 0 | { |
2181 | 0 | LOG((4, "%s: for dimension %d, found dim %s", __func__, |
2182 | 0 | d, dim->hdr.name)); |
2183 | 0 | var->dimids[d] = dim->hdr.id; |
2184 | 0 | var->dim[d] = dim; |
2185 | 0 | finished = NC_TRUE; |
2186 | 0 | break; |
2187 | 0 | } |
2188 | 0 | } /* next dim */ |
2189 | 0 | } /* next grp */ |
2190 | 0 | LOG((5, "%s: dimid for this dimscale is %d", __func__, |
2191 | 0 | var->type_info->hdr.id)); |
2192 | 0 | } /* next var->dim */ |
2193 | 0 | } |
2194 | | /* No dimscales for this var! Invent phony dimensions. */ |
2195 | 0 | else |
2196 | 0 | { |
2197 | 0 | hid_t spaceid = 0; |
2198 | 0 | hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL; |
2199 | 0 | int dataset_ndims; |
2200 | | |
2201 | | /* Find the space information for this dimension. */ |
2202 | 0 | if ((spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0) |
2203 | 0 | return NC_EHDFERR; |
2204 | | |
2205 | | /* Get the len of each dim in the space. */ |
2206 | 0 | if (var->ndims) |
2207 | 0 | { |
2208 | 0 | if (!(h5dimlen = malloc(var->ndims * sizeof(hsize_t)))) |
2209 | 0 | return NC_ENOMEM; |
2210 | 0 | if (!(h5dimlenmax = malloc(var->ndims * sizeof(hsize_t)))) |
2211 | 0 | { |
2212 | 0 | free(h5dimlen); |
2213 | 0 | return NC_ENOMEM; |
2214 | 0 | } |
2215 | 0 | if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, h5dimlen, |
2216 | 0 | h5dimlenmax)) < 0) { |
2217 | 0 | free(h5dimlenmax); |
2218 | 0 | free(h5dimlen); |
2219 | 0 | return NC_EHDFERR; |
2220 | 0 | } |
2221 | 0 | if (dataset_ndims != var->ndims) { |
2222 | 0 | free(h5dimlenmax); |
2223 | 0 | free(h5dimlen); |
2224 | 0 | return NC_EHDFERR; |
2225 | 0 | } |
2226 | 0 | } |
2227 | 0 | else |
2228 | 0 | { |
2229 | | /* Make sure it's scalar. */ |
2230 | 0 | if (H5Sget_simple_extent_type(spaceid) != H5S_SCALAR) |
2231 | 0 | return NC_EHDFERR; |
2232 | 0 | } |
2233 | | |
2234 | | /* Release the space object. */ |
2235 | 0 | if (H5Sclose(spaceid) < 0) { |
2236 | 0 | free(h5dimlen); |
2237 | 0 | free(h5dimlenmax); |
2238 | 0 | return NC_EHDFERR; |
2239 | 0 | } |
2240 | | |
2241 | | /* Create a phony dimension for each dimension in the |
2242 | | * dataset, unless there already is one the correct |
2243 | | * size. */ |
2244 | 0 | for (d = 0; d < var->ndims; d++) |
2245 | 0 | { |
2246 | 0 | int k; |
2247 | 0 | int match; |
2248 | | /* Is there already a phony dimension of the correct size? */ |
2249 | 0 | for(match=-1,k=0;k<ncindexsize(grp->dim);k++) { |
2250 | 0 | if((dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,k)) == NULL) continue; |
2251 | 0 | if ((dim->len == h5dimlen[d]) && |
2252 | 0 | ((h5dimlenmax[d] == H5S_UNLIMITED && dim->unlimited) || |
2253 | 0 | (h5dimlenmax[d] != H5S_UNLIMITED && !dim->unlimited))) |
2254 | 0 | {match = k; break;} |
2255 | 0 | } |
2256 | | |
2257 | | /* Didn't find a phony dim? Then create one. */ |
2258 | 0 | if (match < 0) |
2259 | 0 | { |
2260 | 0 | char phony_dim_name[NC_MAX_NAME + 1]; |
2261 | 0 | sprintf(phony_dim_name, "phony_dim_%d", grp->nc4_info->next_dimid); |
2262 | 0 | LOG((3, "%s: creating phony dim for var %s", __func__, var->hdr.name)); |
2263 | 0 | if ((retval = nc4_dim_list_add(grp, phony_dim_name, h5dimlen[d], -1, &dim))) |
2264 | 0 | { |
2265 | 0 | free(h5dimlenmax); |
2266 | 0 | free(h5dimlen); |
2267 | 0 | return retval; |
2268 | 0 | } |
2269 | | /* Create struct for HDF5-specific dim info. */ |
2270 | 0 | if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T)))) |
2271 | 0 | return NC_ENOMEM; |
2272 | 0 | if (h5dimlenmax[d] == H5S_UNLIMITED) |
2273 | 0 | dim->unlimited = NC_TRUE; |
2274 | 0 | } |
2275 | | |
2276 | | /* The variable must remember the dimid. */ |
2277 | 0 | var->dimids[d] = dim->hdr.id; |
2278 | 0 | var->dim[d] = dim; |
2279 | 0 | } /* next dim */ |
2280 | | |
2281 | | /* Free the memory we malloced. */ |
2282 | 0 | free(h5dimlen); |
2283 | 0 | free(h5dimlenmax); |
2284 | 0 | } |
2285 | 0 | } |
2286 | 0 | } |
2287 | | |
2288 | 0 | return retval; |
2289 | 0 | } |
2290 | | |
2291 | | /** |
2292 | | * @internal Get the class of a type |
2293 | | * |
2294 | | * @param h5 Pointer to the HDF5 file info struct. |
2295 | | * @param xtype NetCDF type ID. |
2296 | | * @param type_class Pointer that gets class of type, NC_INT, |
2297 | | * NC_FLOAT, NC_CHAR, or NC_STRING, NC_ENUM, NC_VLEN, NC_COMPOUND, or |
2298 | | * NC_OPAQUE. |
2299 | | * |
2300 | | * @return ::NC_NOERR No error. |
2301 | | * @author Ed Hartnett |
2302 | | */ |
2303 | | int |
2304 | | nc4_get_typeclass(const NC_FILE_INFO_T *h5, nc_type xtype, int *type_class) |
2305 | 54.0k | { |
2306 | 54.0k | int retval = NC_NOERR; |
2307 | | |
2308 | 54.0k | LOG((4, "%s xtype: %d", __func__, xtype)); |
2309 | 54.0k | assert(type_class); |
2310 | | |
2311 | | /* If this is an atomic type, the answer is easy. */ |
2312 | 54.0k | if (xtype <= NC_STRING) |
2313 | 54.0k | { |
2314 | 54.0k | switch (xtype) |
2315 | 54.0k | { |
2316 | 4.69k | case NC_BYTE: |
2317 | 4.69k | case NC_UBYTE: |
2318 | 8.16k | case NC_SHORT: |
2319 | 8.16k | case NC_USHORT: |
2320 | 34.3k | case NC_INT: |
2321 | 34.3k | case NC_UINT: |
2322 | 34.3k | case NC_INT64: |
2323 | 34.3k | case NC_UINT64: |
2324 | | /* NC_INT is class used for all integral types */ |
2325 | 34.3k | *type_class = NC_INT; |
2326 | 34.3k | break; |
2327 | | |
2328 | 0 | case NC_FLOAT: |
2329 | 5.49k | case NC_DOUBLE: |
2330 | | /* NC_FLOAT is class used for all floating-point types */ |
2331 | 5.49k | *type_class = NC_FLOAT; |
2332 | 5.49k | break; |
2333 | | |
2334 | 14.2k | case NC_CHAR: |
2335 | 14.2k | *type_class = NC_CHAR; |
2336 | 14.2k | break; |
2337 | | |
2338 | 0 | case NC_STRING: |
2339 | 0 | *type_class = NC_STRING; |
2340 | 0 | break; |
2341 | | |
2342 | 0 | default: |
2343 | 0 | BAIL(NC_EBADTYPE); |
2344 | 54.0k | } |
2345 | 54.0k | } |
2346 | 0 | else |
2347 | 0 | { |
2348 | 0 | NC_TYPE_INFO_T *type; |
2349 | | |
2350 | | /* See if it's a used-defined type */ |
2351 | 0 | if ((retval = nc4_find_type(h5, xtype, &type))) |
2352 | 0 | BAIL(retval); |
2353 | 0 | if (!type) |
2354 | 0 | BAIL(NC_EBADTYPE); |
2355 | | |
2356 | 0 | *type_class = type->nc_type_class; |
2357 | 0 | } |
2358 | | |
2359 | 54.0k | exit: |
2360 | 54.0k | return retval; |
2361 | 54.0k | } |
2362 | | |
2363 | | /** |
2364 | | * @internal Report information about an open HDF5 object. This is |
2365 | | * called on any still-open objects when a HDF5 file close is |
2366 | | * attempted. |
2367 | | * |
2368 | | * @param uselog If true, send output to LOG not stderr. |
2369 | | * @param id HDF5 ID of open object. |
2370 | | * @param type Type of HDF5 object, file, dataset, etc. |
2371 | | * |
2372 | | * @author Dennis Heimbigner |
2373 | | */ |
2374 | | void |
2375 | | reportobject(int uselog, hid_t id, unsigned int type) |
2376 | 0 | { |
2377 | 0 | char name[NC_HDF5_MAX_NAME]; |
2378 | 0 | ssize_t len; |
2379 | 0 | const char* typename = NULL; |
2380 | 0 | long long printid = (long long)id; |
2381 | |
|
2382 | 0 | len = H5Iget_name(id, name, NC_HDF5_MAX_NAME); |
2383 | 0 | if(len < 0) return; |
2384 | 0 | name[len] = '\0'; |
2385 | |
|
2386 | 0 | switch (type) { |
2387 | 0 | case H5F_OBJ_FILE: typename = "File"; break; |
2388 | 0 | case H5F_OBJ_DATASET: typename = "Dataset"; break; |
2389 | 0 | case H5F_OBJ_GROUP: typename = "Group"; break; |
2390 | 0 | case H5F_OBJ_DATATYPE: typename = "Datatype"; break; |
2391 | 0 | case H5F_OBJ_ATTR: |
2392 | 0 | typename = "Attribute"; |
2393 | 0 | len = H5Aget_name(id, NC_HDF5_MAX_NAME, name); |
2394 | 0 | if(len < 0) len = 0; |
2395 | 0 | name[len] = '\0'; |
2396 | 0 | break; |
2397 | 0 | default: typename = "<unknown>"; break; |
2398 | 0 | } |
2399 | | #ifdef LOGGING |
2400 | | if(uselog) { |
2401 | | LOG((0,"Type = %s(%lld) name='%s'",typename,printid,name)); |
2402 | | } else |
2403 | | #endif |
2404 | 0 | { |
2405 | 0 | fprintf(stderr,"Type = %s(%lld) name='%s'",typename,printid,name); |
2406 | 0 | } |
2407 | |
|
2408 | 0 | } |
2409 | | |
2410 | | /** |
2411 | | * @internal |
2412 | | * |
2413 | | * @param uselog |
2414 | | * @param fid HDF5 ID. |
2415 | | * @param ntypes Number of types. |
2416 | | * @param otypes Pointer that gets number of open types. |
2417 | | * |
2418 | | * @author Dennis Heimbigner |
2419 | | */ |
2420 | | static void |
2421 | | reportopenobjectsT(int uselog, hid_t fid, int ntypes, unsigned int* otypes) |
2422 | 0 | { |
2423 | 0 | int t,i; |
2424 | 0 | ssize_t ocount; |
2425 | 0 | size_t maxobjs = -1; |
2426 | 0 | hid_t* idlist = NULL; |
2427 | | |
2428 | | /* Always report somehow */ |
2429 | | #ifdef LOGGING |
2430 | | if(uselog) |
2431 | | LOG((0,"\nReport: open objects on %lld",(long long)fid)); |
2432 | | else |
2433 | | #endif |
2434 | 0 | fprintf(stdout,"\nReport: open objects on %lld\n",(long long)fid); |
2435 | 0 | maxobjs = H5Fget_obj_count(fid,H5F_OBJ_ALL); |
2436 | 0 | if(idlist != NULL) free(idlist); |
2437 | 0 | idlist = (hid_t*)malloc(sizeof(hid_t)*maxobjs); |
2438 | 0 | for(t=0;t<ntypes;t++) { |
2439 | 0 | unsigned int ot = otypes[t]; |
2440 | 0 | ocount = H5Fget_obj_ids(fid,ot,maxobjs,idlist); |
2441 | 0 | for(i=0;i<ocount;i++) { |
2442 | 0 | hid_t o = idlist[i]; |
2443 | 0 | reportobject(uselog,o,ot); |
2444 | 0 | } |
2445 | 0 | } |
2446 | 0 | if(idlist != NULL) free(idlist); |
2447 | 0 | } |
2448 | | |
2449 | | /** |
2450 | | * @internal Report open objects. |
2451 | | * |
2452 | | * @param uselog |
2453 | | * @param fid HDF5 file ID. |
2454 | | * |
2455 | | * @author Dennit Heimbigner |
2456 | | */ |
2457 | | void |
2458 | | reportopenobjects(int uselog, hid_t fid) |
2459 | 0 | { |
2460 | 0 | unsigned int OTYPES[5] = {H5F_OBJ_FILE, H5F_OBJ_DATASET, H5F_OBJ_GROUP, |
2461 | 0 | H5F_OBJ_DATATYPE, H5F_OBJ_ATTR}; |
2462 | |
|
2463 | 0 | reportopenobjectsT(uselog, fid ,5, OTYPES); |
2464 | 0 | } |
2465 | | |
2466 | | /** |
2467 | | * @internal Report open objects given a pointer to NC_FILE_INFO_T object |
2468 | | * |
2469 | | * @param h5 file object |
2470 | | * |
2471 | | * @author Dennis Heimbigner |
2472 | | */ |
2473 | | void |
2474 | | showopenobjects5(NC_FILE_INFO_T* h5) |
2475 | 0 | { |
2476 | 0 | NC_HDF5_FILE_INFO_T *hdf5_info; |
2477 | |
|
2478 | 0 | assert(h5 && h5->format_file_info); |
2479 | 0 | hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info; |
2480 | |
|
2481 | 0 | fprintf(stderr,"===== begin showopenobjects =====\n"); |
2482 | 0 | reportopenobjects(0,hdf5_info->hdfid); |
2483 | 0 | fprintf(stderr,"===== end showopenobjects =====\n"); |
2484 | 0 | fflush(stderr); |
2485 | 0 | } |
2486 | | |
2487 | | /** |
2488 | | * @internal Report open objects given an ncid |
2489 | | * Defined to support user or gdb level call. |
2490 | | * |
2491 | | * @param ncid file id |
2492 | | * |
2493 | | * @author Dennis Heimbigner |
2494 | | */ |
2495 | | void |
2496 | | showopenobjects(int ncid) |
2497 | 0 | { |
2498 | 0 | NC_FILE_INFO_T* h5 = NULL; |
2499 | | |
2500 | | /* Find our metadata for this file. */ |
2501 | 0 | if (nc4_find_nc_grp_h5(ncid, NULL, NULL, &h5) != NC_NOERR) |
2502 | 0 | fprintf(stderr,"failed\n"); |
2503 | 0 | else |
2504 | 0 | showopenobjects5(h5); |
2505 | 0 | fflush(stderr); |
2506 | 0 | } |
2507 | | |
2508 | | /** |
2509 | | * @internal Get HDF5 library version. |
2510 | | * |
2511 | | * @param major Pointer that gets major version number. |
2512 | | * @param minor Pointer that gets minor version number. |
2513 | | * @param release Pointer that gets release version number. |
2514 | | * |
2515 | | * @returns NC_NOERR No error. |
2516 | | * @returns NC_EHDFERR HDF5 returned error. |
2517 | | * @author Dennis Heimbigner |
2518 | | */ |
2519 | | int |
2520 | | NC4_hdf5get_libversion(unsigned* major,unsigned* minor,unsigned* release) |
2521 | 9 | { |
2522 | 9 | if(H5get_libversion(major,minor,release) < 0) |
2523 | 0 | return NC_EHDFERR; |
2524 | 9 | return NC_NOERR; |
2525 | 9 | } |
2526 | | |
2527 | | /** |
2528 | | * @internal Get HDF5 superblock version. |
2529 | | * |
2530 | | * @param h5 Pointer to HDF5 file info struct. |
2531 | | * @param idp Pointer that gets superblock version. |
2532 | | * |
2533 | | * @returns NC_NOERR No error. |
2534 | | * @returns NC_EHDFERR HDF5 returned error. |
2535 | | * @author Dennis Heimbigner |
2536 | | */ |
2537 | | int |
2538 | | NC4_hdf5get_superblock(struct NC_FILE_INFO* h5, int* idp) |
2539 | 13.1k | { |
2540 | 13.1k | NC_HDF5_FILE_INFO_T *hdf5_info; |
2541 | 13.1k | int stat = NC_NOERR; |
2542 | 13.1k | unsigned super; |
2543 | 13.1k | hid_t plist = -1; |
2544 | | |
2545 | 13.1k | assert(h5 && h5->format_file_info); |
2546 | 13.1k | hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info; |
2547 | | |
2548 | 13.1k | if((plist = H5Fget_create_plist(hdf5_info->hdfid)) < 0) |
2549 | 0 | {stat = NC_EHDFERR; goto done;} |
2550 | 13.1k | if(H5Pget_version(plist, &super, NULL, NULL, NULL) < 0) |
2551 | 0 | {stat = NC_EHDFERR; goto done;} |
2552 | 13.1k | if(idp) *idp = (int)super; |
2553 | 13.1k | done: |
2554 | 13.1k | if(plist >= 0) H5Pclose(plist); |
2555 | 13.1k | return stat; |
2556 | 13.1k | } |
2557 | | |
2558 | | static int NC4_strict_att_exists(NC_FILE_INFO_T*); |
2559 | | static int NC4_walk(hid_t, int*); |
2560 | | |
2561 | | /** |
2562 | | * @internal Determine whether file is netCDF-4. |
2563 | | * |
2564 | | * We define a file as being from netcdf-4 if any of the following |
2565 | | * are true: |
2566 | | * 1. NCPROPS attribute exists in root group |
2567 | | * 2. NC3_STRICT_ATT_NAME exists in root group |
2568 | | * 3. any of NC_ATT_REFERENCE_LIST, NC_ATT_CLASS, |
2569 | | * NC_ATT_DIMENSION_LIST, NC_ATT_NAME, |
2570 | | * NC_ATT_COORDINATES, NC_DIMID_ATT_NAME |
2571 | | * exist anywhere in the file; note that this |
2572 | | * requires walking the file. |
2573 | | |
2574 | | * @note WARNINGS: |
2575 | | * 1. False negatives are possible for a small subset of netcdf-4 |
2576 | | * created files. |
2577 | | * 2. Deliberate falsification in the file can be used to cause |
2578 | | * a false positive. |
2579 | | * |
2580 | | * @param h5 Pointer to HDF5 file info struct. |
2581 | | * |
2582 | | * @returns NC_NOERR No error. |
2583 | | * @author Dennis Heimbigner. |
2584 | | */ |
2585 | | int |
2586 | | NC4_isnetcdf4(struct NC_FILE_INFO* h5) |
2587 | 0 | { |
2588 | 0 | int stat; |
2589 | 0 | int isnc4 = 0; |
2590 | 0 | int exists; |
2591 | 0 | int count; |
2592 | | |
2593 | | /* Look for NC3_STRICT_ATT_NAME */ |
2594 | 0 | exists = NC4_strict_att_exists(h5); |
2595 | 0 | if(exists) |
2596 | 0 | goto done; |
2597 | | /* attribute did not exist */ |
2598 | | /* => last resort: walk the HDF5 file looking for markers */ |
2599 | 0 | count = 0; |
2600 | 0 | stat = NC4_walk(((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid, |
2601 | 0 | &count); |
2602 | 0 | if(stat != NC_NOERR) |
2603 | 0 | isnc4 = 0; |
2604 | 0 | else /* Threshold is at least two matches */ |
2605 | 0 | isnc4 = (count >= 2); |
2606 | |
|
2607 | 0 | done: |
2608 | 0 | return isnc4; |
2609 | 0 | } |
2610 | | |
2611 | | /** |
2612 | | * @internal See if the NC3 strict attribute exists. |
2613 | | * |
2614 | | * @param h5 Pointer to HDF5 file info struct. |
2615 | | * |
2616 | | * @returns 1 if error || exists; 0 otherwise |
2617 | | * @author Dennis Heimbigner. |
2618 | | */ |
2619 | | static int |
2620 | | NC4_strict_att_exists(NC_FILE_INFO_T *h5) |
2621 | 0 | { |
2622 | 0 | hid_t grpid = -1; |
2623 | 0 | htri_t attr_exists; |
2624 | | |
2625 | | /* Get root group ID. */ |
2626 | 0 | grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid; |
2627 | | |
2628 | | /* See if the NC3_STRICT_ATT_NAME attribute exists */ |
2629 | 0 | if ((attr_exists = H5Aexists(grpid, NC3_STRICT_ATT_NAME)) < 0) |
2630 | 0 | return 1; |
2631 | 0 | return (attr_exists?1:0); |
2632 | 0 | } |
2633 | | |
2634 | | /** |
2635 | | * @internal Walk group struct. |
2636 | | * |
2637 | | * @param gid HDF5 ID of starting group. |
2638 | | * @param countp Pointer that gets count. |
2639 | | * |
2640 | | * @returns NC_NOERR No error. |
2641 | | * @author Dennis Heimbigner |
2642 | | */ |
2643 | | static int |
2644 | | NC4_walk(hid_t gid, int* countp) |
2645 | 0 | { |
2646 | 0 | int ncstat = NC_NOERR; |
2647 | 0 | int i,j,na; |
2648 | 0 | ssize_t len; |
2649 | 0 | hsize_t nobj; |
2650 | 0 | herr_t err; |
2651 | 0 | int otype; |
2652 | 0 | hid_t grpid, dsid; |
2653 | 0 | char name[NC_HDF5_MAX_NAME]; |
2654 | | |
2655 | | /* walk group members of interest */ |
2656 | 0 | err = H5Gget_num_objs(gid, &nobj); |
2657 | 0 | if(err < 0) return err; |
2658 | | |
2659 | 0 | for(i = 0; i < nobj; i++) { |
2660 | | /* Get name & kind of object in the group */ |
2661 | 0 | len = H5Gget_objname_by_idx(gid,(hsize_t)i,name,(size_t)NC_HDF5_MAX_NAME); |
2662 | 0 | if(len < 0) return len; |
2663 | | |
2664 | 0 | otype = H5Gget_objtype_by_idx(gid,(size_t)i); |
2665 | 0 | switch(otype) { |
2666 | 0 | case H5G_GROUP: |
2667 | 0 | grpid = H5Gopen(gid,name); |
2668 | 0 | NC4_walk(grpid,countp); |
2669 | 0 | H5Gclose(grpid); |
2670 | 0 | break; |
2671 | 0 | case H5G_DATASET: /* variables */ |
2672 | | /* Check for phony_dim */ |
2673 | 0 | if(strcmp(name,"phony_dim")==0) |
2674 | 0 | *countp = *countp + 1; |
2675 | 0 | dsid = H5Dopen(gid,name); |
2676 | 0 | na = H5Aget_num_attrs(dsid); |
2677 | 0 | for(j = 0; j < na; j++) { |
2678 | 0 | hid_t aid = H5Aopen_idx(dsid,(unsigned int) j); |
2679 | 0 | if(aid >= 0) { |
2680 | 0 | const NC_reservedatt* ra; |
2681 | 0 | ssize_t len = H5Aget_name(aid, NC_HDF5_MAX_NAME, name); |
2682 | 0 | if(len < 0) return len; |
2683 | | /* Is this a netcdf-4 marker attribute */ |
2684 | | /* Is this a netcdf-4 marker attribute */ |
2685 | 0 | ra = NC_findreserved(name); |
2686 | 0 | if(ra != NULL) |
2687 | 0 | *countp = *countp + 1; |
2688 | 0 | } |
2689 | 0 | H5Aclose(aid); |
2690 | 0 | } |
2691 | 0 | H5Dclose(dsid); |
2692 | 0 | break; |
2693 | 0 | default:/* ignore */ |
2694 | 0 | break; |
2695 | 0 | } |
2696 | 0 | } |
2697 | 0 | return ncstat; |
2698 | 0 | } |
2699 | | |
2700 | | int |
2701 | | NC4_hdf5_remove_filter(NC_VAR_INFO_T* var, unsigned int filterid) |
2702 | 0 | { |
2703 | 0 | int stat = NC_NOERR; |
2704 | 0 | NC_HDF5_VAR_INFO_T *hdf5_var; |
2705 | 0 | hid_t propid = -1; |
2706 | 0 | herr_t herr = -1; |
2707 | 0 | H5Z_filter_t hft; |
2708 | |
|
2709 | 0 | hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; |
2710 | 0 | if ((propid = H5Dget_create_plist(hdf5_var->hdf_datasetid)) < 0) |
2711 | 0 | {stat = NC_EHDFERR; goto done;} |
2712 | | |
2713 | 0 | hft = filterid; |
2714 | 0 | if((herr = H5Premove_filter(propid,hft)) < 0) |
2715 | 0 | {stat = NC_EHDFERR; goto done;} |
2716 | 0 | done: |
2717 | 0 | return stat; |
2718 | 0 | } |