/src/gdal/netcdf-c-4.7.4/libhdf5/hdf5internal.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright 2003-2018, University Corporation for Atmospheric |
2 | | * Research. See the COPYRIGHT file for copying and redistribution |
3 | | * conditions. |
4 | | */ |
5 | | /** |
6 | | * @file @internal Internal netcdf-4 functions. |
7 | | * |
8 | | * This file contains functions internal to the netcdf4 library. None of |
9 | | * the functions in this file are exposed in the exetnal API. These |
10 | | * functions all relate to the manipulation of netcdf-4's in-memory |
11 | | * buffer of metadata information, i.e. the linked list of NC |
12 | | * structs. |
13 | | * |
14 | | * @author Ed Hartnett |
15 | | */ |
16 | | |
17 | | #include "config.h" |
18 | | #include "hdf5internal.h" |
19 | | |
20 | | #undef DEBUGH5 |
21 | | |
22 | | #ifdef DEBUGH5 |
23 | | /** |
24 | | * @internal Provide a catchable error reporting function |
25 | | * |
26 | | * @param ignored Ignored. |
27 | | * |
28 | | * @return 0 for success. |
29 | | */ |
30 | | static herr_t |
31 | | h5catch(void* ignored) |
32 | | { |
33 | | H5Eprint(NULL); |
34 | | return 0; |
35 | | } |
36 | | #endif |
37 | | |
38 | | /* These are the default chunk cache sizes for HDF5 files created or |
39 | | * opened with netCDF-4. */ |
40 | | extern size_t nc4_chunk_cache_size; |
41 | | extern size_t nc4_chunk_cache_nelems; |
42 | | extern float nc4_chunk_cache_preemption; |
43 | | |
44 | | #ifdef LOGGING |
45 | | /* This is the severity level of messages which will be logged. Use |
46 | | severity 0 for errors, 1 for important log messages, 2 for less |
47 | | important, etc. */ |
48 | | extern int nc_log_level; |
49 | | |
50 | | #endif /* LOGGING */ |
51 | | |
52 | | int nc4_hdf5_initialized = 0; /**< True if initialization has happened. */ |
53 | | |
54 | | /** |
55 | | * @internal Provide a wrapper for H5Eset_auto |
56 | | * @param func Pointer to func. |
57 | | * @param client_data Client data. |
58 | | * |
59 | | * @return 0 for success |
60 | | */ |
61 | | static herr_t |
62 | | set_auto(void* func, void *client_data) |
63 | 1 | { |
64 | | #ifdef DEBUGH5 |
65 | | return H5Eset_auto2(H5E_DEFAULT,(H5E_auto2_t)h5catch,client_data); |
66 | | #else |
67 | 1 | return H5Eset_auto2(H5E_DEFAULT,(H5E_auto2_t)func,client_data); |
68 | 1 | #endif |
69 | 1 | } |
70 | | |
71 | | /** |
72 | | * @internal Provide a function to do any necessary initialization of |
73 | | * the HDF5 library. |
74 | | */ |
75 | | void |
76 | | nc4_hdf5_initialize(void) |
77 | 1 | { |
78 | 1 | if (set_auto(NULL, NULL) < 0) |
79 | 0 | LOG((0, "Couldn't turn off HDF5 error messages!")); |
80 | 1 | LOG((1, "HDF5 error messages have been turned off.")); |
81 | 1 | nc4_hdf5_initialized = 1; |
82 | 1 | } |
83 | | |
84 | | /** |
85 | | * @internal Provide a function to do any necessary finalization of |
86 | | * the HDF5 library. |
87 | | */ |
88 | | void |
89 | | nc4_hdf5_finalize(void) |
90 | 0 | { |
91 | | /* Reclaim global resources */ |
92 | 0 | NC4_provenance_finalize(); |
93 | 0 | nc4_hdf5_initialized = 0; |
94 | 0 | } |
95 | | |
96 | | /** |
97 | | * @internal Given a varid, return the maximum length of a dimension |
98 | | * using dimid. |
99 | | * |
100 | | * @param grp Pointer to group info struct. |
101 | | * @param varid Variable ID. |
102 | | * @param dimid Dimension ID. |
103 | | * @param maxlen Pointer that gets the max length. |
104 | | * |
105 | | * @return ::NC_NOERR No error. |
106 | | * @author Ed Hartnett |
107 | | */ |
108 | | static int |
109 | | find_var_dim_max_length(NC_GRP_INFO_T *grp, int varid, int dimid, |
110 | | size_t *maxlen) |
111 | 254k | { |
112 | 254k | hid_t datasetid = 0, spaceid = 0; |
113 | 254k | NC_VAR_INFO_T *var; |
114 | 254k | hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL; |
115 | 254k | int d, dataset_ndims = 0; |
116 | 254k | int retval = NC_NOERR; |
117 | | |
118 | 254k | *maxlen = 0; |
119 | | |
120 | | /* Find this var. */ |
121 | 254k | var = (NC_VAR_INFO_T*)ncindexith(grp->vars,varid); |
122 | 254k | if (!var) return NC_ENOTVAR; |
123 | 254k | assert(var->hdr.id == varid); |
124 | | |
125 | | /* If the var hasn't been created yet, its size is 0. */ |
126 | 254k | if (!var->created) |
127 | 0 | { |
128 | 0 | *maxlen = 0; |
129 | 0 | } |
130 | 254k | else |
131 | 254k | { |
132 | | /* Get the number of records in the dataset. */ |
133 | 254k | if ((retval = nc4_open_var_grp2(grp, var->hdr.id, &datasetid))) |
134 | 0 | BAIL(retval); |
135 | 254k | if ((spaceid = H5Dget_space(datasetid)) < 0) |
136 | 0 | BAIL(NC_EHDFERR); |
137 | | |
138 | | /* If it's a scalar dataset, it has length one. */ |
139 | 254k | if (H5Sget_simple_extent_type(spaceid) == H5S_SCALAR) |
140 | 150k | { |
141 | 150k | *maxlen = (var->dimids && var->dimids[0] == dimid) ? 1 : 0; |
142 | 150k | } |
143 | 104k | else |
144 | 104k | { |
145 | | /* Check to make sure ndims is right, then get the len of each |
146 | | dim in the space. */ |
147 | 104k | if ((dataset_ndims = H5Sget_simple_extent_ndims(spaceid)) < 0) |
148 | 0 | BAIL(NC_EHDFERR); |
149 | 104k | if (dataset_ndims != var->ndims) |
150 | 859 | BAIL(NC_EHDFERR); |
151 | 103k | if (!(h5dimlen = malloc(dataset_ndims * sizeof(hsize_t)))) |
152 | 0 | BAIL(NC_ENOMEM); |
153 | 103k | if (!(h5dimlenmax = malloc(dataset_ndims * sizeof(hsize_t)))) |
154 | 0 | BAIL(NC_ENOMEM); |
155 | 103k | if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, |
156 | 103k | h5dimlen, h5dimlenmax)) < 0) |
157 | 0 | BAIL(NC_EHDFERR); |
158 | 103k | LOG((5, "find_var_dim_max_length: varid %d len %d max: %d", |
159 | 103k | varid, (int)h5dimlen[0], (int)h5dimlenmax[0])); |
160 | 278k | for (d=0; d<dataset_ndims; d++) { |
161 | 174k | if (var->dimids[d] == dimid) { |
162 | 87.5k | *maxlen = *maxlen > h5dimlen[d] ? *maxlen : h5dimlen[d]; |
163 | 87.5k | } |
164 | 174k | } |
165 | 103k | } |
166 | 254k | } |
167 | | |
168 | 254k | exit: |
169 | 254k | if (spaceid > 0 && H5Sclose(spaceid) < 0) |
170 | 0 | BAIL2(NC_EHDFERR); |
171 | 254k | if (h5dimlen) free(h5dimlen); |
172 | 254k | if (h5dimlenmax) free(h5dimlenmax); |
173 | 254k | return retval; |
174 | 254k | } |
175 | | |
176 | | /** |
177 | | * @internal Search for type with a given HDF type id. |
178 | | * |
179 | | * @param h5 File |
180 | | * @param target_hdf_typeid HDF5 type ID to find. |
181 | | * |
182 | | * @return Pointer to type info struct, or NULL if not found. |
183 | | * @author Ed Hartnett |
184 | | */ |
185 | | NC_TYPE_INFO_T * |
186 | | nc4_rec_find_hdf_type(NC_FILE_INFO_T *h5, hid_t target_hdf_typeid) |
187 | 0 | { |
188 | 0 | NC_TYPE_INFO_T *type; |
189 | 0 | htri_t equal; |
190 | 0 | int i; |
191 | |
|
192 | 0 | assert(h5); |
193 | | |
194 | 0 | for (i = 0; i < nclistlength(h5->alltypes); i++) |
195 | 0 | { |
196 | 0 | NC_HDF5_TYPE_INFO_T *hdf5_type; |
197 | 0 | hid_t hdf_typeid; |
198 | |
|
199 | 0 | type = (NC_TYPE_INFO_T*)nclistget(h5->alltypes, i); |
200 | 0 | if(type == NULL) continue; |
201 | | |
202 | | /* Get HDF5-specific type info. */ |
203 | 0 | hdf5_type = (NC_HDF5_TYPE_INFO_T *)type->format_type_info; |
204 | | |
205 | | /* Select the HDF5 typeid to use. */ |
206 | 0 | hdf_typeid = hdf5_type->native_hdf_typeid ? |
207 | 0 | hdf5_type->native_hdf_typeid : hdf5_type->hdf_typeid; |
208 | | |
209 | | /* Is this the type we are searching for? */ |
210 | 0 | if ((equal = H5Tequal(hdf_typeid, target_hdf_typeid)) < 0) |
211 | 0 | return NULL; |
212 | 0 | if (equal) |
213 | 0 | return type; |
214 | 0 | } |
215 | | /* Can't find it. Fate, why do you mock me? */ |
216 | 0 | return NULL; |
217 | 0 | } |
218 | | |
219 | | /** |
220 | | * @internal Find the actual length of a dim by checking the length of |
221 | | * that dim in all variables that use it, in grp or children. **len |
222 | | * must be initialized to zero before this function is called. |
223 | | * |
224 | | * @param grp Pointer to group info struct. |
225 | | * @param dimid Dimension ID. |
226 | | * @param len Pointer to pointer that gets length. |
227 | | * |
228 | | * @return ::NC_NOERR No error. |
229 | | * @author Ed Hartnett |
230 | | */ |
231 | | int |
232 | | nc4_find_dim_len(NC_GRP_INFO_T *grp, int dimid, size_t **len) |
233 | 6.27k | { |
234 | 6.27k | NC_VAR_INFO_T *var; |
235 | 6.27k | int retval; |
236 | 6.27k | int i; |
237 | | |
238 | 6.27k | assert(grp && len); |
239 | 6.27k | LOG((3, "%s: grp->name %s dimid %d", __func__, grp->hdr.name, dimid)); |
240 | | |
241 | | /* If there are any groups, call this function recursively on |
242 | | * them. */ |
243 | 6.27k | for (i = 0; i < ncindexsize(grp->children); i++) |
244 | 0 | if ((retval = nc4_find_dim_len((NC_GRP_INFO_T*)ncindexith(grp->children, i), |
245 | 0 | dimid, len))) |
246 | 0 | return retval; |
247 | | |
248 | | /* For all variables in this group, find the ones that use this |
249 | | * dimension, and remember the max length. */ |
250 | 260k | for (i = 0; i < ncindexsize(grp->vars); i++) |
251 | 254k | { |
252 | 254k | size_t mylen; |
253 | 254k | var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i); |
254 | 254k | assert(var); |
255 | | |
256 | | /* Find max length of dim in this variable... */ |
257 | 254k | if ((retval = find_var_dim_max_length(grp, var->hdr.id, dimid, &mylen))) |
258 | 859 | return retval; |
259 | | |
260 | 254k | **len = **len > mylen ? **len : mylen; |
261 | 254k | } |
262 | | |
263 | 5.41k | return NC_NOERR; |
264 | 6.27k | } |
265 | | |
266 | | /** |
267 | | * @internal Break a coordinate variable to separate the dimension and |
268 | | * the variable. |
269 | | * |
270 | | * This is called from nc_rename_dim() and nc_rename_var(). In some |
271 | | * renames, the coord variable must stay, but it is no longer a coord |
272 | | * variable. This function changes a coord var into an ordinary |
273 | | * variable. |
274 | | * |
275 | | * @param grp Pointer to group info struct. |
276 | | * @param coord_var Pointer to variable info struct. |
277 | | * @param dim Pointer to dimension info struct. |
278 | | * |
279 | | * @return ::NC_NOERR No error. |
280 | | * @return ::NC_ENOMEM Out of memory. |
281 | | * @author Quincey Koziol, Ed Hartnett |
282 | | */ |
283 | | int |
284 | | nc4_break_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *coord_var, |
285 | | NC_DIM_INFO_T *dim) |
286 | 0 | { |
287 | 0 | int retval; |
288 | | |
289 | | /* Sanity checks */ |
290 | 0 | assert(grp && coord_var && dim && dim->coord_var == coord_var && |
291 | 0 | coord_var->dim[0] == dim && coord_var->dimids[0] == dim->hdr.id && |
292 | 0 | !((NC_HDF5_DIM_INFO_T *)(dim->format_dim_info))->hdf_dimscaleid); |
293 | 0 | LOG((3, "%s dim %s was associated with var %s, but now has different name", |
294 | 0 | __func__, dim->hdr.name, coord_var->hdr.name)); |
295 | | |
296 | | /* If we're replacing an existing dimscale dataset, go to |
297 | | * every var in the file and detach this dimension scale. */ |
298 | 0 | if ((retval = rec_detach_scales(grp->nc4_info->root_grp, |
299 | 0 | dim->hdr.id, |
300 | 0 | ((NC_HDF5_VAR_INFO_T *)(coord_var->format_var_info))->hdf_datasetid))) |
301 | 0 | return retval; |
302 | | |
303 | | /* Allow attached dimscales to be tracked on the [former] |
304 | | * coordinate variable */ |
305 | 0 | if (coord_var->ndims) |
306 | 0 | { |
307 | | /* Coordinate variables shouldn't have dimscales attached. */ |
308 | 0 | assert(!coord_var->dimscale_attached); |
309 | | |
310 | | /* Allocate space for tracking them */ |
311 | 0 | if (!(coord_var->dimscale_attached = calloc(coord_var->ndims, |
312 | 0 | sizeof(nc_bool_t)))) |
313 | 0 | return NC_ENOMEM; |
314 | 0 | } |
315 | | |
316 | | /* Detach dimension from variable */ |
317 | 0 | coord_var->dimscale = NC_FALSE; |
318 | 0 | dim->coord_var = NULL; |
319 | | |
320 | | /* Set state transition indicators */ |
321 | 0 | coord_var->was_coord_var = NC_TRUE; |
322 | 0 | coord_var->became_coord_var = NC_FALSE; |
323 | |
|
324 | 0 | return NC_NOERR; |
325 | 0 | } |
326 | | |
327 | | /** |
328 | | * @internal Delete an existing dimscale-only dataset. |
329 | | * |
330 | | * A dimscale-only HDF5 dataset is created when a dim is defined |
331 | | * without an accompanying coordinate variable. |
332 | | * |
333 | | * Sometimes, during renames, or late creation of variables, an |
334 | | * existing, dimscale-only dataset must be removed. This means |
335 | | * detaching all variables that use the dataset, then closing and |
336 | | * unlinking it. |
337 | | * |
338 | | * @param grp The grp of the dimscale-only dataset to be deleted, or a |
339 | | * higher group in the hierarchy (ex. root group). |
340 | | * @param dimid id of the dimension |
341 | | * @param dim Pointer to the dim with the dimscale-only dataset to be |
342 | | * deleted. |
343 | | * |
344 | | * @return ::NC_NOERR No error. |
345 | | * @return ::NC_EHDFERR HDF5 error. |
346 | | * @author Ed Hartnett |
347 | | */ |
348 | | int |
349 | | delete_dimscale_dataset(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T *dim) |
350 | 0 | { |
351 | 0 | NC_HDF5_DIM_INFO_T *hdf5_dim; |
352 | 0 | NC_HDF5_GRP_INFO_T *hdf5_grp; |
353 | 0 | int retval; |
354 | |
|
355 | 0 | assert(grp && grp->format_grp_info && dim && dim->format_dim_info); |
356 | 0 | LOG((2, "%s: deleting dimscale dataset %s dimid %d", __func__, dim->hdr.name, |
357 | 0 | dimid)); |
358 | | |
359 | | /* Get HDF5 specific grp and dim info. */ |
360 | 0 | hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; |
361 | 0 | hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; |
362 | | |
363 | | /* Detach dimscale from any variables using it */ |
364 | 0 | if ((retval = rec_detach_scales(grp, dimid, hdf5_dim->hdf_dimscaleid)) < 0) |
365 | 0 | return retval; |
366 | | |
367 | | /* Close the HDF5 dataset */ |
368 | 0 | if (H5Dclose(hdf5_dim->hdf_dimscaleid) < 0) |
369 | 0 | return NC_EHDFERR; |
370 | 0 | hdf5_dim->hdf_dimscaleid = 0; |
371 | | |
372 | | /* Now delete the dataset. */ |
373 | 0 | if (H5Gunlink(hdf5_grp->hdf_grpid, dim->hdr.name) < 0) |
374 | 0 | return NC_EHDFERR; |
375 | | |
376 | 0 | return NC_NOERR; |
377 | 0 | } |
378 | | |
379 | | /** |
380 | | * @internal Reform a coordinate variable from a dimension and a |
381 | | * variable. |
382 | | * |
383 | | * @param grp Pointer to group info struct. |
384 | | * @param var Pointer to variable info struct. |
385 | | * @param dim Pointer to dimension info struct. |
386 | | * |
387 | | * @return ::NC_NOERR No error. |
388 | | * @author Quincey Koziol, Ed Hartnett |
389 | | */ |
390 | | int |
391 | | nc4_reform_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, NC_DIM_INFO_T *dim) |
392 | 0 | { |
393 | 0 | NC_HDF5_DIM_INFO_T *hdf5_dim; |
394 | 0 | NC_HDF5_GRP_INFO_T *hdf5_grp; |
395 | 0 | NC_HDF5_VAR_INFO_T *hdf5_var; |
396 | 0 | int need_to_reattach_scales = 0; |
397 | 0 | int retval = NC_NOERR; |
398 | |
|
399 | 0 | assert(grp && grp->format_grp_info && var && var->format_var_info && |
400 | 0 | dim && dim->format_dim_info); |
401 | 0 | LOG((3, "%s: dim->hdr.name %s var->hdr.name %s", __func__, dim->hdr.name, |
402 | 0 | var->hdr.name)); |
403 | | |
404 | | /* Get HDF5-specific dim, group, and var info. */ |
405 | 0 | hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; |
406 | 0 | hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; |
407 | 0 | hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; |
408 | | |
409 | | /* Detach dimscales from the [new] coordinate variable. */ |
410 | 0 | if (var->dimscale_attached) |
411 | 0 | { |
412 | 0 | int dims_detached = 0; |
413 | 0 | int finished = 0; |
414 | 0 | int d; |
415 | | |
416 | | /* Loop over all dimensions for variable. */ |
417 | 0 | for (d = 0; d < var->ndims && !finished; d++) |
418 | 0 | { |
419 | | /* Is there a dimscale attached to this axis? */ |
420 | 0 | if (var->dimscale_attached[d]) |
421 | 0 | { |
422 | 0 | NC_GRP_INFO_T *g; |
423 | 0 | int k; |
424 | |
|
425 | 0 | for (g = grp; g && !finished; g = g->parent) |
426 | 0 | { |
427 | 0 | NC_DIM_INFO_T *dim1; |
428 | 0 | NC_HDF5_DIM_INFO_T *hdf5_dim1; |
429 | |
|
430 | 0 | for (k = 0; k < ncindexsize(g->dim); k++) |
431 | 0 | { |
432 | 0 | dim1 = (NC_DIM_INFO_T *)ncindexith(g->dim, k); |
433 | 0 | assert(dim1 && dim1->format_dim_info); |
434 | 0 | hdf5_dim1 = (NC_HDF5_DIM_INFO_T *)dim1->format_dim_info; |
435 | |
|
436 | 0 | if (var->dimids[d] == dim1->hdr.id) |
437 | 0 | { |
438 | 0 | hid_t dim_datasetid; /* Dataset ID for dimension */ |
439 | | |
440 | | /* Find dataset ID for dimension */ |
441 | 0 | if (dim1->coord_var) |
442 | 0 | dim_datasetid = ((NC_HDF5_VAR_INFO_T *) |
443 | 0 | (dim1->coord_var->format_var_info))->hdf_datasetid; |
444 | 0 | else |
445 | 0 | dim_datasetid = hdf5_dim1->hdf_dimscaleid; |
446 | | |
447 | | /* dim_datasetid may be 0 in some cases when |
448 | | * renames of dims and vars are happening. In |
449 | | * this case, the scale has already been |
450 | | * detached. */ |
451 | 0 | if (dim_datasetid > 0) |
452 | 0 | { |
453 | 0 | LOG((3, "detaching scale from %s", var->hdr.name)); |
454 | 0 | if (H5DSdetach_scale(hdf5_var->hdf_datasetid, |
455 | 0 | dim_datasetid, d) < 0) |
456 | 0 | BAIL(NC_EHDFERR); |
457 | 0 | } |
458 | 0 | var->dimscale_attached[d] = NC_FALSE; |
459 | 0 | if (dims_detached++ == var->ndims) |
460 | 0 | finished++; |
461 | 0 | } |
462 | 0 | } |
463 | 0 | } |
464 | 0 | } |
465 | 0 | } /* next variable dimension */ |
466 | | |
467 | | /* Release & reset the array tracking attached dimscales. */ |
468 | 0 | free(var->dimscale_attached); |
469 | 0 | var->dimscale_attached = NULL; |
470 | 0 | need_to_reattach_scales++; |
471 | 0 | } |
472 | | |
473 | | /* Use variable's dataset ID for the dimscale ID. */ |
474 | 0 | if (hdf5_dim->hdf_dimscaleid && grp != NULL) |
475 | 0 | { |
476 | 0 | LOG((3, "closing and unlinking dimscale dataset %s", dim->hdr.name)); |
477 | 0 | if (H5Dclose(hdf5_dim->hdf_dimscaleid) < 0) |
478 | 0 | BAIL(NC_EHDFERR); |
479 | 0 | hdf5_dim->hdf_dimscaleid = 0; |
480 | | |
481 | | /* Now delete the dimscale's dataset (it will be recreated |
482 | | later, if necessary). */ |
483 | 0 | if (H5Gunlink(hdf5_grp->hdf_grpid, dim->hdr.name) < 0) |
484 | 0 | return NC_EDIMMETA; |
485 | 0 | } |
486 | | |
487 | | /* Attach variable to dimension. */ |
488 | 0 | var->dimscale = NC_TRUE; |
489 | 0 | dim->coord_var = var; |
490 | | |
491 | | /* Check if this variable used to be a coord. var */ |
492 | 0 | if (need_to_reattach_scales || (var->was_coord_var && grp != NULL)) |
493 | 0 | { |
494 | | /* Reattach the scale everywhere it is used. (Recall that netCDF |
495 | | * dimscales are always 1-D) */ |
496 | 0 | if ((retval = rec_reattach_scales(grp->nc4_info->root_grp, |
497 | 0 | var->dimids[0], hdf5_var->hdf_datasetid))) |
498 | 0 | return retval; |
499 | | |
500 | | /* Set state transition indicator (cancels earlier |
501 | | * transition). */ |
502 | 0 | var->was_coord_var = NC_FALSE; |
503 | 0 | } |
504 | | |
505 | | /* Set state transition indicator */ |
506 | 0 | var->became_coord_var = NC_TRUE; |
507 | |
|
508 | 0 | exit: |
509 | 0 | return retval; |
510 | 0 | } |
511 | | |
512 | | /** |
513 | | * @internal Close HDF5 resources for global atts in a group. |
514 | | * |
515 | | * @param grp Pointer to group info struct. |
516 | | * |
517 | | * @return ::NC_NOERR No error. |
518 | | * @return ::NC_EHDFERR HDF5 error. |
519 | | * @author Ed Hartnett |
520 | | */ |
521 | | static int |
522 | | close_gatts(NC_GRP_INFO_T *grp) |
523 | 1.35k | { |
524 | 1.35k | NC_ATT_INFO_T *att; |
525 | 1.35k | int a; |
526 | | |
527 | 3.72k | for (a = 0; a < ncindexsize(grp->att); a++) |
528 | 2.36k | { |
529 | 2.36k | NC_HDF5_ATT_INFO_T *hdf5_att; |
530 | | |
531 | 2.36k | att = (NC_ATT_INFO_T *)ncindexith(grp->att, a); |
532 | 2.36k | assert(att && att->format_att_info); |
533 | 2.36k | hdf5_att = (NC_HDF5_ATT_INFO_T *)att->format_att_info; |
534 | | |
535 | | /* Close the HDF5 typeid. */ |
536 | 2.36k | if (hdf5_att->native_hdf_typeid && |
537 | 2.36k | H5Tclose(hdf5_att->native_hdf_typeid) < 0) |
538 | 0 | return NC_EHDFERR; |
539 | 2.36k | } |
540 | 1.35k | return NC_NOERR; |
541 | 1.35k | } |
542 | | |
543 | | /** |
544 | | * @internal Close HDF5 resources for vars in a group. |
545 | | * |
546 | | * @param grp Pointer to group info struct. |
547 | | * |
548 | | * @return ::NC_NOERR No error. |
549 | | * @return ::NC_EHDFERR HDF5 error. |
550 | | * @author Ed Hartnett |
551 | | */ |
552 | | static int |
553 | | close_vars(NC_GRP_INFO_T *grp) |
554 | 1.35k | { |
555 | 1.35k | NC_VAR_INFO_T *var; |
556 | 1.35k | NC_HDF5_VAR_INFO_T *hdf5_var; |
557 | 1.35k | NC_ATT_INFO_T *att; |
558 | 1.35k | int a, i; |
559 | | |
560 | 13.5k | for (i = 0; i < ncindexsize(grp->vars); i++) |
561 | 12.1k | { |
562 | 12.1k | var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i); |
563 | 12.1k | assert(var && var->format_var_info); |
564 | 12.1k | hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info; |
565 | | |
566 | | /* Close the HDF5 dataset associated with this var. */ |
567 | 12.1k | if (hdf5_var->hdf_datasetid) |
568 | 12.1k | { |
569 | 12.1k | LOG((3, "closing HDF5 dataset %lld", hdf5_var->hdf_datasetid)); |
570 | 12.1k | if (H5Dclose(hdf5_var->hdf_datasetid) < 0) |
571 | 0 | return NC_EHDFERR; |
572 | | |
573 | 12.1k | if (var->fill_value) |
574 | 6.12k | { |
575 | 6.12k | if (var->type_info) |
576 | 6.12k | { |
577 | 6.12k | if (var->type_info->nc_type_class == NC_VLEN) |
578 | 0 | nc_free_vlen((nc_vlen_t *)var->fill_value); |
579 | 6.12k | else if (var->type_info->nc_type_class == NC_STRING && *(char **)var->fill_value) |
580 | 0 | free(*(char **)var->fill_value); |
581 | 6.12k | } |
582 | 6.12k | } |
583 | 12.1k | } |
584 | | |
585 | | /* Free the HDF5 typeids. */ |
586 | 12.1k | if (var->type_info->rc == 1) |
587 | 12.1k | { |
588 | 12.1k | if (((NC_HDF5_TYPE_INFO_T *)(var->type_info->format_type_info))->hdf_typeid && |
589 | 12.1k | H5Tclose(((NC_HDF5_TYPE_INFO_T *)(var->type_info->format_type_info))->hdf_typeid) < 0) |
590 | 0 | return NC_EHDFERR; |
591 | 12.1k | if (((NC_HDF5_TYPE_INFO_T *)(var->type_info->format_type_info))->native_hdf_typeid && |
592 | 12.1k | H5Tclose(((NC_HDF5_TYPE_INFO_T *)(var->type_info->format_type_info))->native_hdf_typeid) < 0) |
593 | 0 | return NC_EHDFERR; |
594 | 12.1k | } |
595 | | |
596 | | /* Delete any HDF5 dimscale objid information. */ |
597 | 12.1k | if (hdf5_var->dimscale_hdf5_objids) |
598 | 1.76k | free(hdf5_var->dimscale_hdf5_objids); |
599 | | |
600 | 16.5k | for (a = 0; a < ncindexsize(var->att); a++) |
601 | 4.39k | { |
602 | 4.39k | NC_HDF5_ATT_INFO_T *hdf5_att; |
603 | 4.39k | att = (NC_ATT_INFO_T *)ncindexith(var->att, a); |
604 | 4.39k | assert(att && att->format_att_info); |
605 | 4.39k | hdf5_att = (NC_HDF5_ATT_INFO_T *)att->format_att_info; |
606 | | |
607 | | /* Close the HDF5 typeid if one is open. */ |
608 | 4.39k | if (hdf5_att->native_hdf_typeid && |
609 | 4.39k | H5Tclose(hdf5_att->native_hdf_typeid) < 0) |
610 | 0 | return NC_EHDFERR; |
611 | 4.39k | } |
612 | 12.1k | } |
613 | | |
614 | 1.35k | return NC_NOERR; |
615 | 1.35k | } |
616 | | |
617 | | /** |
618 | | * @internal Close HDF5 resources for dims in a group. |
619 | | * |
620 | | * @param grp Pointer to group info struct. |
621 | | * |
622 | | * @return ::NC_NOERR No error. |
623 | | * @return ::NC_EHDFERR HDF5 error. |
624 | | * @author Ed Hartnett |
625 | | */ |
626 | | static int |
627 | | close_dims(NC_GRP_INFO_T *grp) |
628 | 1.35k | { |
629 | 1.35k | NC_DIM_INFO_T *dim; |
630 | 1.35k | int i; |
631 | | |
632 | 8.96k | for (i = 0; i < ncindexsize(grp->dim); i++) |
633 | 7.60k | { |
634 | 7.60k | NC_HDF5_DIM_INFO_T *hdf5_dim; |
635 | | |
636 | 7.60k | dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, i); |
637 | 7.60k | assert(dim && dim->format_dim_info); |
638 | 7.60k | hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info; |
639 | | |
640 | | /* If this is a dim without a coordinate variable, then close |
641 | | * the HDF5 DIM_WITHOUT_VARIABLE dataset associated with this |
642 | | * dim. */ |
643 | 7.60k | if (hdf5_dim->hdf_dimscaleid && H5Dclose(hdf5_dim->hdf_dimscaleid) < 0) |
644 | 0 | return NC_EHDFERR; |
645 | 7.60k | } |
646 | | |
647 | 1.35k | return NC_NOERR; |
648 | 1.35k | } |
649 | | |
650 | | /** |
651 | | * @internal Close HDF5 resources for types in a group. Set values to |
652 | | * 0 after closing types. Because of type reference counters, these |
653 | | * closes can be called multiple times. |
654 | | * |
655 | | * @param grp Pointer to group info struct. |
656 | | * |
657 | | * @return ::NC_NOERR No error. |
658 | | * @return ::NC_EHDFERR HDF5 error. |
659 | | * @author Ed Hartnett, Dennis Heimbigner |
660 | | */ |
661 | | static int |
662 | | close_types(NC_GRP_INFO_T *grp) |
663 | 1.35k | { |
664 | 1.35k | int i; |
665 | | |
666 | 1.35k | for (i = 0; i < ncindexsize(grp->type); i++) |
667 | 0 | { |
668 | 0 | NC_TYPE_INFO_T *type; |
669 | 0 | NC_HDF5_TYPE_INFO_T *hdf5_type; |
670 | |
|
671 | 0 | type = (NC_TYPE_INFO_T *)ncindexith(grp->type, i); |
672 | 0 | assert(type && type->format_type_info); |
673 | | |
674 | | /* Get HDF5-specific type info. */ |
675 | 0 | hdf5_type = (NC_HDF5_TYPE_INFO_T *)type->format_type_info; |
676 | | |
677 | | /* Close any open user-defined HDF5 typeids. */ |
678 | 0 | if (hdf5_type->hdf_typeid && H5Tclose(hdf5_type->hdf_typeid) < 0) |
679 | 0 | return NC_EHDFERR; |
680 | 0 | hdf5_type->hdf_typeid = 0; |
681 | 0 | if (hdf5_type->native_hdf_typeid && |
682 | 0 | H5Tclose(hdf5_type->native_hdf_typeid) < 0) |
683 | 0 | return NC_EHDFERR; |
684 | 0 | hdf5_type->native_hdf_typeid = 0; |
685 | 0 | } |
686 | | |
687 | 1.35k | return NC_NOERR; |
688 | 1.35k | } |
689 | | |
690 | | /** |
691 | | * @internal Recursively free HDF5 objects for a group (and everything |
692 | | * it contains). |
693 | | * |
694 | | * @param grp Pointer to group info struct. |
695 | | * |
696 | | * @return ::NC_NOERR No error. |
697 | | * @return ::NC_EHDFERR HDF5 error. |
698 | | * @author Ed Hartnett |
699 | | */ |
700 | | int |
701 | | nc4_rec_grp_HDF5_del(NC_GRP_INFO_T *grp) |
702 | 1.35k | { |
703 | 1.35k | NC_HDF5_GRP_INFO_T *hdf5_grp; |
704 | 1.35k | int i; |
705 | 1.35k | int retval; |
706 | | |
707 | 1.35k | assert(grp && grp->format_grp_info); |
708 | 1.35k | LOG((3, "%s: grp->name %s", __func__, grp->hdr.name)); |
709 | | |
710 | 1.35k | hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info; |
711 | | |
712 | | /* Recursively call this function for each child, if any, stopping |
713 | | * if there is an error. */ |
714 | 1.35k | for (i = 0; i < ncindexsize(grp->children); i++) |
715 | 0 | if ((retval = nc4_rec_grp_HDF5_del((NC_GRP_INFO_T *)ncindexith(grp->children, |
716 | 0 | i)))) |
717 | 0 | return retval; |
718 | | |
719 | | /* Close HDF5 resources associated with global attributes. */ |
720 | 1.35k | if ((retval = close_gatts(grp))) |
721 | 0 | return retval; |
722 | | |
723 | | /* Close HDF5 resources associated with vars. */ |
724 | 1.35k | if ((retval = close_vars(grp))) |
725 | 0 | return retval; |
726 | | |
727 | | /* Close HDF5 resources associated with dims. */ |
728 | 1.35k | if ((retval = close_dims(grp))) |
729 | 0 | return retval; |
730 | | |
731 | | /* Close HDF5 resources associated with types. */ |
732 | 1.35k | if ((retval = close_types(grp))) |
733 | 0 | return retval; |
734 | | |
735 | | /* Close the HDF5 group. */ |
736 | 1.35k | LOG((4, "%s: closing group %s", __func__, grp->hdr.name)); |
737 | 1.35k | if (hdf5_grp->hdf_grpid && H5Gclose(hdf5_grp->hdf_grpid) < 0) |
738 | 0 | return NC_EHDFERR; |
739 | | |
740 | 1.35k | return NC_NOERR; |
741 | 1.35k | } |
742 | | |
743 | | /** |
744 | | * @internal Given an ncid and varid, get pointers to the group and var |
745 | | * metadata. Lazy var metadata reads are done as needed. |
746 | | * |
747 | | * @param ncid File ID. |
748 | | * @param varid Variable ID. |
749 | | * @param h5 Pointer that gets pointer to the NC_FILE_INFO_T struct |
750 | | * for this file. Ignored if NULL. |
751 | | * @param grp Pointer that gets pointer to group info. Ignored if |
752 | | * NULL. |
753 | | * @param var Pointer that gets pointer to var info. Ignored if NULL. |
754 | | * |
755 | | * @return ::NC_NOERR No error. |
756 | | * @return ::NC_ENOTVAR Variable not found. |
757 | | * @author Ed Hartnett |
758 | | */ |
759 | | int |
760 | | nc4_hdf5_find_grp_h5_var(int ncid, int varid, NC_FILE_INFO_T **h5, |
761 | | NC_GRP_INFO_T **grp, NC_VAR_INFO_T **var) |
762 | 48.7k | { |
763 | 48.7k | NC_FILE_INFO_T *my_h5; |
764 | 48.7k | NC_GRP_INFO_T *my_grp; |
765 | 48.7k | NC_VAR_INFO_T *my_var; |
766 | 48.7k | int retval; |
767 | | |
768 | | /* Look up file and group metadata. */ |
769 | 48.7k | if ((retval = nc4_find_grp_h5(ncid, &my_grp, &my_h5))) |
770 | 0 | return retval; |
771 | 48.7k | assert(my_grp && my_h5); |
772 | | |
773 | | /* Find the var. */ |
774 | 48.7k | if (!(my_var = (NC_VAR_INFO_T *)ncindexith(my_grp->vars, varid))) |
775 | 0 | return NC_ENOTVAR; |
776 | 48.7k | assert(my_var && my_var->hdr.id == varid); |
777 | | |
778 | | /* Do we need to read var metadata? */ |
779 | 48.7k | if (!my_var->meta_read && my_var->created) |
780 | 0 | if ((retval = nc4_get_var_meta(my_var))) |
781 | 0 | return retval; |
782 | | |
783 | | /* Return pointers that caller wants. */ |
784 | 48.7k | if (h5) |
785 | 48.7k | *h5 = my_h5; |
786 | 48.7k | if (grp) |
787 | 48.7k | *grp = my_grp; |
788 | 48.7k | if (var) |
789 | 48.7k | *var = my_var; |
790 | | |
791 | 48.7k | return NC_NOERR; |
792 | 48.7k | } |
793 | | |
794 | | /** |
795 | | * @internal Given an ncid, varid, and attribute name, return |
796 | | * normalized name and pointers to the file, group, var, and att info |
797 | | * structs. Lazy reads of attributes and variable metadata are done as |
798 | | * needed. |
799 | | * |
800 | | * @param ncid File/group ID. |
801 | | * @param varid Variable ID. |
802 | | * @param name Name to of attribute. |
803 | | * @param attnum Number of attribute. |
804 | | * @param use_name If true, use the name to get the |
805 | | * attribute. Otherwise use the attnum. |
806 | | * @param norm_name Pointer to storage of size NC_MAX_NAME + 1, |
807 | | * which will get the normalized name, if use_name is true. Ignored if |
808 | | * NULL. |
809 | | * @param h5 Pointer to pointer that gets file info struct. Ignored if |
810 | | * NULL. |
811 | | * @param grp Pointer to pointer that gets group info struct. Ignored |
812 | | * if NULL. |
813 | | * @param h5 Pointer to pointer that gets variable info |
814 | | * struct. Ignored if NULL. |
815 | | * @param att Pointer to pointer that gets attribute info |
816 | | * struct. Ignored if NULL. |
817 | | * |
818 | | * @return ::NC_NOERR No error. |
819 | | * @return ::NC_EBADID Bad ncid. |
820 | | * @return ::NC_ENOTVAR Variable not found. |
821 | | * @return ::NC_ENOTATT Attribute not found. |
822 | | * @author Ed Hartnett |
823 | | */ |
824 | | int |
825 | | nc4_hdf5_find_grp_var_att(int ncid, int varid, const char *name, int attnum, |
826 | | int use_name, char *norm_name, NC_FILE_INFO_T **h5, |
827 | | NC_GRP_INFO_T **grp, NC_VAR_INFO_T **var, |
828 | | NC_ATT_INFO_T **att) |
829 | 271k | { |
830 | 271k | NC_FILE_INFO_T *my_h5; |
831 | 271k | NC_GRP_INFO_T *my_grp; |
832 | 271k | NC_VAR_INFO_T *my_var = NULL; |
833 | 271k | NC_ATT_INFO_T *my_att; |
834 | 271k | char my_norm_name[NC_MAX_NAME + 1] = ""; |
835 | 271k | NCindex *attlist = NULL; |
836 | 271k | int retval; |
837 | | |
838 | 271k | LOG((4, "%s: ncid %d varid %d attnum %d use_name %d", __func__, ncid, varid, |
839 | 271k | attnum, use_name)); |
840 | | |
841 | | /* Don't need to provide name unless getting att pointer and using |
842 | | * use_name. */ |
843 | 271k | assert(!att || ((use_name && name) || !use_name)); |
844 | | |
845 | | /* Find info for this file, group, and h5 info. */ |
846 | 271k | if ((retval = nc4_find_nc_grp_h5(ncid, NULL, &my_grp, &my_h5))) |
847 | 0 | return retval; |
848 | 271k | assert(my_grp && my_h5); |
849 | | |
850 | | /* Get either the global or a variable attribute list. */ |
851 | 271k | if (varid == NC_GLOBAL) |
852 | 5.68k | { |
853 | | /* Do we need to read the atts? */ |
854 | 5.68k | if (!my_grp->atts_read) |
855 | 0 | if ((retval = nc4_read_atts(my_grp, NULL))) |
856 | 0 | return retval; |
857 | | |
858 | 5.68k | attlist = my_grp->att; |
859 | 5.68k | } |
860 | 265k | else |
861 | 265k | { |
862 | 265k | if (!(my_var = (NC_VAR_INFO_T *)ncindexith(my_grp->vars, varid))) |
863 | 0 | return NC_ENOTVAR; |
864 | | |
865 | | /* Do we need to read the var attributes? */ |
866 | 265k | if (!my_var->atts_read) |
867 | 6.08k | if ((retval = nc4_read_atts(my_grp, my_var))) |
868 | 0 | return retval; |
869 | | |
870 | | /* Do we need to read var metadata? */ |
871 | 265k | if (!my_var->meta_read && my_var->created) |
872 | 6.08k | if ((retval = nc4_get_var_meta(my_var))) |
873 | 0 | return retval; |
874 | | |
875 | 265k | attlist = my_var->att; |
876 | 265k | } |
877 | 271k | assert(attlist); |
878 | | |
879 | | /* Need a name if use_name is true. */ |
880 | 271k | if (use_name && !name) |
881 | 0 | return NC_EBADNAME; |
882 | | |
883 | | /* Normalize the name. */ |
884 | 271k | if (use_name) |
885 | 79.7k | if ((retval = nc4_normalize_name(name, my_norm_name))) |
886 | 0 | return retval; |
887 | | |
888 | | /* Now find the attribute by name or number. */ |
889 | 271k | if (att) |
890 | 5.01k | { |
891 | 5.01k | my_att = use_name ? (NC_ATT_INFO_T *)ncindexlookup(attlist, my_norm_name) : |
892 | 5.01k | (NC_ATT_INFO_T *)ncindexith(attlist, attnum); |
893 | 5.01k | if (!my_att) |
894 | 0 | return NC_ENOTATT; |
895 | 5.01k | } |
896 | | |
897 | | /* Give the people what they want. */ |
898 | 271k | if (norm_name) |
899 | 79.7k | { |
900 | 79.7k | strncpy(norm_name, my_norm_name, NC_MAX_NAME); |
901 | 79.7k | norm_name[NC_MAX_NAME] = 0; |
902 | 79.7k | } |
903 | 271k | if (h5) |
904 | 266k | *h5 = my_h5; |
905 | 271k | if (grp) |
906 | 266k | *grp = my_grp; |
907 | 271k | if (var) |
908 | 266k | *var = my_var; |
909 | 271k | if (att) |
910 | 5.01k | *att = my_att; |
911 | | |
912 | 271k | return NC_NOERR; |
913 | 271k | } |
914 | | |
915 | | #ifdef LOGGING |
916 | | /* We will need to check against nc log level from nc4internal.c. */ |
917 | | extern int nc_log_level; |
918 | | |
919 | | /** |
920 | | * @internal This is like nc_set_log_level(), but will also turn on |
921 | | * HDF5 internal logging, in addition to netCDF logging. This should |
922 | | * never be called by the user. It is called in open/create when the |
923 | | * nc logging level has changed. |
924 | | * |
925 | | * @return ::NC_NOERR No error. |
926 | | * @author Ed Hartnett |
927 | | */ |
928 | | int |
929 | | hdf5_set_log_level() |
930 | | { |
931 | | /* If the user wants to completely turn off logging, turn off HDF5 |
932 | | logging too. Now I truly can't think of what to do if this |
933 | | fails, so just ignore the return code. */ |
934 | | if (nc_log_level == NC_TURN_OFF_LOGGING) |
935 | | { |
936 | | set_auto(NULL, NULL); |
937 | | LOG((1, "HDF5 error messages turned off!")); |
938 | | } |
939 | | else |
940 | | { |
941 | | if (set_auto((H5E_auto_t)&H5Eprint, stderr) < 0) |
942 | | LOG((0, "H5Eset_auto failed!")); |
943 | | LOG((1, "HDF5 error messages turned on.")); |
944 | | } |
945 | | |
946 | | return NC_NOERR; |
947 | | } |
948 | | #endif /* LOGGING */ |