/src/netcdf-c/libsrc4/nc4internal.c
Line | Count | Source |
1 | | /* Copyright 2003-2018, University Corporation for Atmospheric |
2 | | * Research. See the COPYRIGHT file for copying and redistribution |
3 | | * conditions. |
4 | | */ |
5 | | /** |
6 | | * @file |
7 | | * @internal |
8 | | * Internal netcdf-4 functions. |
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 all relate to the manipulation of netcdf-4's in-memory |
13 | | * buffer of metadata information, i.e. the linked list of NC |
14 | | * structs. |
15 | | * |
16 | | * @author Ed Hartnett, Dennis Heimbigner, Ward Fisher |
17 | | */ |
18 | | #include "config.h" |
19 | | #include <stdarg.h> |
20 | | #include <stddef.h> |
21 | | #include "netcdf.h" |
22 | | #include "netcdf_filter.h" |
23 | | #include "netcdf_meta.h" |
24 | | #include "nc4internal.h" |
25 | | #include "nc.h" /* from libsrc */ |
26 | | #include "ncdispatch.h" /* from libdispatch */ |
27 | | #include "ncutf8.h" |
28 | | #include "ncrc.h" |
29 | | #include "nc4internal.h" |
30 | | |
31 | | /** @internal Number of reserved attributes. These attributes are |
32 | | * hidden from the netcdf user, but exist in the implementation |
33 | | * datasets to help netcdf read the dataset. |
34 | | * Moved here from hdf5file.c. |
35 | | * These tables need to capture all reserved attributes |
36 | | * across all possible dispatchers |
37 | | */ |
38 | | |
39 | | /** @internal List of reserved attributes. |
40 | | WARNING: This list will be sorted in (strcmp) sorted order for binary search. |
41 | | So order here does not matter; the table will be modified by sorting. |
42 | | */ |
43 | | static NC_reservedatt NC_reserved[] = { |
44 | | {NC_ATT_CLASS, READONLYFLAG|HIDDENATTRFLAG}, /*CLASS*/ |
45 | | {NC_ATT_DIMENSION_LIST, READONLYFLAG|HIDDENATTRFLAG}, /*DIMENSION_LIST*/ |
46 | | {NC_ATT_NAME, READONLYFLAG|HIDDENATTRFLAG}, /*NAME*/ |
47 | | {NC_ATT_REFERENCE_LIST, READONLYFLAG|HIDDENATTRFLAG}, /*REFERENCE_LIST*/ |
48 | | {NC_XARRAY_DIMS, READONLYFLAG|HIDDENATTRFLAG}, /*_ARRAY_DIMENSIONS*/ |
49 | | {NC_ATT_CODECS, VARFLAG|READONLYFLAG|NAMEONLYFLAG}, /*_Codecs*/ |
50 | | {NC_ATT_FORMAT, READONLYFLAG}, /*_Format*/ |
51 | | {ISNETCDF4ATT, READONLYFLAG|NAMEONLYFLAG|VIRTUALFLAG}, /*_IsNetcdf4*/ |
52 | | {NCPROPS,READONLYFLAG|NAMEONLYFLAG|HIDDENATTRFLAG}, /*_NCProperties*/ |
53 | | {NC_ATT_COORDINATES, READONLYFLAG|HIDDENATTRFLAG}, /*_Netcdf4Coordinates*/ |
54 | | {NC_ATT_DIMID_NAME, READONLYFLAG|HIDDENATTRFLAG}, /*_Netcdf4Dimid*/ |
55 | | {SUPERBLOCKATT, READONLYFLAG|NAMEONLYFLAG|VIRTUALFLAG}, /*_SuperblockVersion*/ |
56 | | {NC_ATT_NC3_STRICT_NAME, READONLYFLAG}, /*_nc3_strict*/ |
57 | | {NC_NCZARR_ATTR, READONLYFLAG|HIDDENATTRFLAG}, /*_nczarr_attr */ |
58 | | {NC_NCZARR_GROUP, READONLYFLAG|HIDDENATTRFLAG}, /*_nczarr_group */ |
59 | | {NC_NCZARR_ARRAY, READONLYFLAG|HIDDENATTRFLAG}, /*_nczarr_array */ |
60 | | {NC_NCZARR_SUPERBLOCK, READONLYFLAG|HIDDENATTRFLAG}, /*_nczarr_superblock */ |
61 | | }; |
62 | 1 | #define NRESERVED (sizeof(NC_reserved) / sizeof(NC_reservedatt)) /*|NC_reservedatt*/ |
63 | | |
64 | | /*Forward */ |
65 | | static int NC4_move_in_NCList(NC* nc, int new_id); |
66 | | static int bincmp(const void* arg1, const void* arg2); |
67 | | static int sortcmp(const void* arg1, const void* arg2); |
68 | | |
69 | | #if LOGGING |
70 | | /* This is the severity level of messages which will be logged. Use |
71 | | severity 0 for errors, 1 for important log messages, 2 for less |
72 | | important, etc. */ |
73 | | int nc_log_level = NC_TURN_OFF_LOGGING; |
74 | | |
75 | | |
76 | | #if NC_HAS_PARALLEL4 |
77 | | /* File pointer for the parallel I/O log file. */ |
78 | | FILE *LOG_FILE = NULL; |
79 | | #endif /* NC_HAS_PARALLEL4 */ |
80 | | |
81 | | /* This function prints out a message, if the severity of |
82 | | * the message is lower than the global nc_log_level. To use it, do |
83 | | * something like this: |
84 | | * |
85 | | * nc_log(0, "this computer will explode in %d seconds", i); |
86 | | * |
87 | | * After the first arg (the severity), use the rest like a normal |
88 | | * printf statement. Output will appear on stderr for sequential |
89 | | * builds, and in a file nc4_log_R.log for each process for a parallel |
90 | | * build, where R is the rank of the process. |
91 | | * |
92 | | * Ed Hartnett |
93 | | */ |
94 | | void |
95 | | nc_log(int severity, const char *fmt, ...) |
96 | | { |
97 | | va_list argp; |
98 | | int t; |
99 | | FILE *f = stderr; |
100 | | |
101 | | const char* envv = NULL; |
102 | | envv = getenv("NC4LOGGING"); |
103 | | if(envv != NULL) { |
104 | | nc_log_level = atoi(envv); |
105 | | } |
106 | | |
107 | | /* If the severity is greater than the log level, we don't print |
108 | | * this message. */ |
109 | | if (severity > nc_log_level) |
110 | | return; |
111 | | |
112 | | #if NC_HAS_PARALLEL4 |
113 | | /* For parallel I/O build, if MPI has been initialized, instead of |
114 | | * printing logging output to stderr, it goes to a file for each |
115 | | * process. */ |
116 | | { |
117 | | int mpi_initialized; |
118 | | int mpierr; |
119 | | |
120 | | /* Check to see if MPI has been initialized. */ |
121 | | if ((mpierr = MPI_Initialized(&mpi_initialized))) |
122 | | return; |
123 | | |
124 | | /* If MPI has been initialized use a log file. */ |
125 | | assert(LOG_FILE); |
126 | | if (mpi_initialized) |
127 | | f = LOG_FILE; |
128 | | } |
129 | | #endif /* NC_HAS_PARALLEL4 */ |
130 | | |
131 | | /* If the severity is zero, this is an error. Otherwise insert that |
132 | | many tabs before the message. */ |
133 | | if (!severity) |
134 | | fprintf(f, "ERROR: "); |
135 | | for (t = 0; t < severity; t++) |
136 | | fprintf(f, "\t"); |
137 | | |
138 | | /* Print out the variable list of args with vprintf. */ |
139 | | va_start(argp, fmt); |
140 | | vfprintf(f, fmt, argp); |
141 | | va_end(argp); |
142 | | |
143 | | /* Put on a final linefeed. */ |
144 | | fprintf(f, "\n"); |
145 | | fflush(f); |
146 | | } |
147 | | #endif /* LOGGING */ |
148 | | |
149 | | /** |
150 | | * @internal Check and normalize and name. |
151 | | * |
152 | | * @param name Name to normalize. |
153 | | * @param norm_name The normalized name. |
154 | | * |
155 | | * @return ::NC_NOERR No error. |
156 | | * @return ::NC_EMAXNAME Name too long. |
157 | | * @return ::NC_EINVAL NULL given for name. |
158 | | * @return ::NC_ENOMEM Out of memory. |
159 | | * @author Dennis Heimbigner |
160 | | */ |
161 | | int |
162 | | nc4_check_name(const char *name, char *norm_name) |
163 | 0 | { |
164 | 0 | char *temp; |
165 | 0 | int retval; |
166 | |
|
167 | 0 | assert(norm_name); |
168 | | |
169 | | /* Check for NULL. */ |
170 | 0 | if (!name) |
171 | 0 | return NC_EINVAL; |
172 | | |
173 | | /* Make sure this is a valid netcdf name. This should be done |
174 | | * before the name is normalized, because it gives better error |
175 | | * codes for bad utf8 strings. */ |
176 | 0 | if ((retval = NC_check_name(name))) |
177 | 0 | return retval; |
178 | | |
179 | | /* Normalize the name. */ |
180 | 0 | if ((retval = nc_utf8_normalize((const unsigned char *)name, |
181 | 0 | (unsigned char **)&temp))) |
182 | 0 | return retval; |
183 | | |
184 | | /* Check length of normalized name. */ |
185 | 0 | if (strlen(temp) > NC_MAX_NAME) |
186 | 0 | { |
187 | 0 | free(temp); |
188 | 0 | return NC_EMAXNAME; |
189 | 0 | } |
190 | | |
191 | | /* Copy the normalized name. */ |
192 | 0 | strcpy(norm_name, temp); |
193 | 0 | free(temp); |
194 | |
|
195 | 0 | return NC_NOERR; |
196 | 0 | } |
197 | | |
198 | | /** |
199 | | * @internal Add a file to the list of libsrc4 open files. This is |
200 | | * used by dispatch layers that wish to use the libsrc4 metadata |
201 | | * model, but don't know about struct NC. This is the same as |
202 | | * nc4_nc4f_list_add(), except it takes an ncid instead of an NC *, |
203 | | * and also passes back the dispatchdata pointer. |
204 | | * |
205 | | * @param ncid The (already-assigned) ncid of the file (aka ext_ncid). |
206 | | * @param path The file name of the new file. |
207 | | * @param mode The mode flag. |
208 | | * @param dispatchdata Void * that gets pointer to dispatch data, |
209 | | * which is the NC_FILE_INFO_T struct allocated for this file and its |
210 | | * metadata. Ignored if NULL. (This is passed as a void to allow |
211 | | * external user-defined formats to use this function.) |
212 | | * |
213 | | * @return ::NC_NOERR No error. |
214 | | * @return ::NC_EBADID No NC struct with this ext_ncid. |
215 | | * @return ::NC_ENOMEM Out of memory. |
216 | | * @author Ed Hartnett |
217 | | */ |
218 | | int |
219 | | nc4_file_list_add(int ncid, const char *path, int mode, void **dispatchdata) |
220 | 0 | { |
221 | 0 | NC *nc; |
222 | 0 | int ret; |
223 | | |
224 | | /* Find NC pointer for this file. */ |
225 | 0 | if ((ret = NC_check_id(ncid, &nc))) |
226 | 0 | return ret; |
227 | | |
228 | | /* Add necessary structs to hold netcdf-4 file data. This is where |
229 | | * the NC_FILE_INFO_T struct is allocated for the file. */ |
230 | 0 | if ((ret = nc4_nc4f_list_add(nc, path, mode))) |
231 | 0 | return ret; |
232 | | |
233 | | /* If the user wants a pointer to the NC_FILE_INFO_T, then provide |
234 | | * it. */ |
235 | 0 | if (dispatchdata) |
236 | 0 | *dispatchdata = nc->dispatchdata; |
237 | |
|
238 | 0 | return NC_NOERR; |
239 | 0 | } |
240 | | |
241 | | /** |
242 | | * @internal Change the ncid of an open file. This is needed for PIO |
243 | | * integration. |
244 | | * |
245 | | * @param ncid The ncid of the file (aka ext_ncid). |
246 | | * @param new_ncid_index The new ncid index to use (i.e. the first two bytes |
247 | | * of the ncid). |
248 | | * |
249 | | * @return ::NC_NOERR No error. |
250 | | * @return ::NC_EBADID No NC struct with this ext_ncid. |
251 | | * @return ::NC_ENOMEM Out of memory. |
252 | | * @author Ed Hartnett |
253 | | */ |
254 | | int |
255 | | nc4_file_change_ncid(int ncid, unsigned short new_ncid_index) |
256 | 0 | { |
257 | 0 | NC *nc; |
258 | 0 | int ret; |
259 | |
|
260 | 0 | LOG((2, "%s: ncid %d new_ncid_index %d", __func__, ncid, new_ncid_index)); |
261 | | |
262 | | /* Find NC pointer for this file. */ |
263 | 0 | if ((ret = NC_check_id(ncid, &nc))) |
264 | 0 | return ret; |
265 | | |
266 | | /* Move it in the list. It will fail if list spot is already |
267 | | * occupied. */ |
268 | 0 | LOG((3, "moving nc->ext_ncid %d nc->ext_ncid >> ID_SHIFT %d", |
269 | 0 | nc->ext_ncid, nc->ext_ncid >> ID_SHIFT)); |
270 | 0 | if (NC4_move_in_NCList(nc, new_ncid_index)) |
271 | 0 | return NC_EIO; |
272 | 0 | LOG((3, "moved to new_ncid_index %d new nc->ext_ncid %d", new_ncid_index, |
273 | 0 | nc->ext_ncid)); |
274 | |
|
275 | 0 | return NC_NOERR; |
276 | 0 | } |
277 | | |
278 | | /** |
279 | | * @internal Get info about a file on the list of libsrc4 open |
280 | | * files. This is used by dispatch layers that wish to use the libsrc4 |
281 | | * metadata model, but don't know about struct NC. |
282 | | * |
283 | | * @param ncid The ncid of the file (aka ext_ncid). |
284 | | * @param path A pointer that gets file name (< NC_MAX_NAME). Ignored |
285 | | * if NULL. |
286 | | * @param mode A pointer that gets the mode flag. Ignored if NULL. |
287 | | * @param dispatchdata Void * that gets pointer to dispatch data, |
288 | | * which is the NC_FILE_INFO_T struct allocated for this file and its |
289 | | * metadata. Ignored if NULL. (This is passed as a void to allow |
290 | | * external user-defined formats to use this function.) |
291 | | * |
292 | | * @return ::NC_NOERR No error. |
293 | | * @return ::NC_EBADID No NC struct with this ext_ncid. |
294 | | * @return ::NC_ENOMEM Out of memory. |
295 | | * @author Ed Hartnett |
296 | | */ |
297 | | int |
298 | | nc4_file_list_get(int ncid, char **path, int *mode, void **dispatchdata) |
299 | 0 | { |
300 | 0 | NC *nc; |
301 | 0 | int ret; |
302 | | |
303 | | /* Find NC pointer for this file. */ |
304 | 0 | if ((ret = NC_check_id(ncid, &nc))) |
305 | 0 | return ret; |
306 | | |
307 | | /* If the user wants path, give it. */ |
308 | 0 | if (path) |
309 | 0 | strncpy(*path, nc->path, NC_MAX_NAME); |
310 | | |
311 | | /* If the user wants mode, give it. */ |
312 | 0 | if (mode) |
313 | 0 | *mode = nc->mode; |
314 | | |
315 | | /* If the user wants dispatchdata, give it. */ |
316 | 0 | if (dispatchdata) |
317 | 0 | *dispatchdata = nc->dispatchdata; |
318 | |
|
319 | 0 | return NC_NOERR; |
320 | 0 | } |
321 | | |
322 | | /** |
323 | | * @internal Given an NC pointer, add the necessary stuff for a |
324 | | * netcdf-4 file. This allocates the NC_FILE_INFO_T struct for the |
325 | | * file, which is used by libhdf5 and libhdf4 (and perhaps other |
326 | | * future dispatch layers) to hold the metadata for the file. |
327 | | * |
328 | | * @param nc Pointer to file's NC struct. |
329 | | * @param path The file name of the new file. |
330 | | * @param mode The mode flag. |
331 | | * |
332 | | * @return ::NC_NOERR No error. |
333 | | * @return ::NC_ENOMEM Out of memory. |
334 | | * @author Ed Hartnett, Dennis Heimbigner |
335 | | */ |
336 | | int |
337 | | nc4_nc4f_list_add(NC *nc, const char *path, int mode) |
338 | 0 | { |
339 | 0 | NC_FILE_INFO_T *h5; |
340 | 0 | int retval; |
341 | |
|
342 | 0 | assert(nc && !NC4_DATA(nc) && path); |
343 | | |
344 | | /* We need to malloc and initialize the substructure |
345 | | NC_FILE_INFO_T. */ |
346 | 0 | if (!(h5 = calloc(1, sizeof(NC_FILE_INFO_T)))) |
347 | 0 | return NC_ENOMEM; |
348 | 0 | nc->dispatchdata = h5; |
349 | 0 | h5->controller = nc; |
350 | |
|
351 | 0 | h5->hdr.sort = NCFIL; |
352 | 0 | h5->hdr.name = strdup(path); |
353 | 0 | h5->hdr.id = nc->ext_ncid; |
354 | | |
355 | | /* Hang on to cmode, and note that we're in define mode. */ |
356 | 0 | h5->cmode = mode | NC_INDEF; |
357 | | |
358 | | /* The next_typeid needs to be set beyond the end of our atomic |
359 | | * types. */ |
360 | 0 | h5->next_typeid = NC_FIRSTUSERTYPEID; |
361 | | |
362 | | /* Initialize lists for dimensions, types, and groups. */ |
363 | 0 | h5->alldims = nclistnew(); |
364 | 0 | h5->alltypes = nclistnew(); |
365 | 0 | h5->allgroups = nclistnew(); |
366 | | |
367 | | /* There's always at least one open group - the root |
368 | | * group. Allocate space for one group's worth of information. Set |
369 | | * its grp id, name, and allocate associated empty lists. */ |
370 | 0 | if ((retval = nc4_grp_list_add(h5, NULL, NC_GROUP_NAME, &h5->root_grp))) |
371 | 0 | { |
372 | | /* Free everything allocated above before returning, otherwise |
373 | | * the NC_hashmap inside each NCindex leaks (issue #2665). */ |
374 | 0 | free(h5->hdr.name); |
375 | 0 | nclistfree(h5->alldims); |
376 | 0 | nclistfree(h5->alltypes); |
377 | 0 | nclistfree(h5->allgroups); |
378 | 0 | free(h5); |
379 | 0 | nc->dispatchdata = NULL; |
380 | 0 | return retval; |
381 | 0 | } |
382 | | |
383 | 0 | return NC_NOERR; |
384 | 0 | } |
385 | | |
386 | | /** |
387 | | * @internal Given an ncid, find the relevant group and return a |
388 | | * pointer to it. |
389 | | * |
390 | | * @param ncid File and group ID. |
391 | | * @param grp Pointer that gets pointer to group info struct. Ignored |
392 | | * if NULL. |
393 | | * |
394 | | * @return ::NC_NOERR No error. |
395 | | * @return ::NC_ENOTNC4 Not a netCDF-4 file. |
396 | | * @author Ed Hartnett |
397 | | */ |
398 | | int |
399 | | nc4_find_nc4_grp(int ncid, NC_GRP_INFO_T **grp) |
400 | 0 | { |
401 | 0 | return nc4_find_nc_grp_h5(ncid, NULL, grp, NULL); |
402 | 0 | } |
403 | | |
404 | | /** |
405 | | * @internal Given an ncid, find the relevant group and return a |
406 | | * pointer to it, also set a pointer to the nc4_info struct of the |
407 | | * related file. |
408 | | * |
409 | | * @param ncid File and group ID. |
410 | | * @param grp Pointer that gets pointer to group info struct. Ignored |
411 | | * if NULL. |
412 | | * @param h5 Pointer that gets pointer to file info struct. Ignored if |
413 | | * NULL. |
414 | | * |
415 | | * @return ::NC_NOERR No error. |
416 | | * @return ::NC_EBADID Bad ncid. |
417 | | * @author Ed Hartnett |
418 | | */ |
419 | | int |
420 | | nc4_find_grp_h5(int ncid, NC_GRP_INFO_T **grp, NC_FILE_INFO_T **h5) |
421 | 0 | { |
422 | 0 | return nc4_find_nc_grp_h5(ncid, NULL, grp, h5); |
423 | 0 | } |
424 | | |
425 | | /** |
426 | | * @internal Find info for this file and group, and set pointers. |
427 | | * |
428 | | * @param ncid File and group ID. |
429 | | * @param nc Pointer that gets a pointer to the file's NC |
430 | | * struct. Ignored if NULL. |
431 | | * @param grp Pointer that gets a pointer to the group |
432 | | * struct. Ignored if NULL. |
433 | | * @param h5 Pointer that gets HDF5 file struct. Ignored if NULL. |
434 | | * |
435 | | * @return ::NC_NOERR No error. |
436 | | * @return ::NC_EBADID Bad ncid. |
437 | | * @author Ed Hartnett, Dennis Heimbigner |
438 | | */ |
439 | | int |
440 | | nc4_find_nc_grp_h5(int ncid, NC **nc, NC_GRP_INFO_T **grp, NC_FILE_INFO_T **h5) |
441 | 0 | { |
442 | 0 | NC_GRP_INFO_T *my_grp = NULL; |
443 | 0 | NC_FILE_INFO_T *my_h5 = NULL; |
444 | 0 | NC *my_nc; |
445 | 0 | int retval; |
446 | 0 | size_t index; |
447 | | |
448 | | /* Look up file metadata. */ |
449 | 0 | if ((retval = NC_check_id(ncid, &my_nc))) |
450 | 0 | return retval; |
451 | 0 | my_h5 = my_nc->dispatchdata; |
452 | 0 | assert(my_h5 && my_h5->root_grp); |
453 | | |
454 | | /* If we can't find it, the grp id part of ncid is bad. */ |
455 | 0 | index = (ncid & GRP_ID_MASK); |
456 | 0 | if (!(my_grp = nclistget(my_h5->allgroups,index))) |
457 | 0 | return NC_EBADID; |
458 | | |
459 | | /* Return pointers to caller, if desired. */ |
460 | 0 | if (nc) |
461 | 0 | *nc = my_nc; |
462 | 0 | if (h5) |
463 | 0 | *h5 = my_h5; |
464 | 0 | if (grp) |
465 | 0 | *grp = my_grp; |
466 | |
|
467 | 0 | return NC_NOERR; |
468 | 0 | } |
469 | | |
470 | | /** |
471 | | * @internal Given an ncid and varid, get pointers to the group and var |
472 | | * metadata. |
473 | | * |
474 | | * @param ncid File ID. |
475 | | * @param varid Variable ID. |
476 | | * @param h5 Pointer that gets pointer to the NC_FILE_INFO_T struct |
477 | | * for this file. Ignored if NULL. |
478 | | * @param grp Pointer that gets pointer to group info. Ignored if |
479 | | * NULL. |
480 | | * @param var Pointer that gets pointer to var info. Ignored if NULL. |
481 | | * |
482 | | * @return ::NC_NOERR No error. |
483 | | * @author Ed Hartnett |
484 | | */ |
485 | | int |
486 | | nc4_find_grp_h5_var(int ncid, int varid, NC_FILE_INFO_T **h5, NC_GRP_INFO_T **grp, |
487 | | NC_VAR_INFO_T **var) |
488 | 0 | { |
489 | 0 | NC_FILE_INFO_T *my_h5; |
490 | 0 | NC_GRP_INFO_T *my_grp; |
491 | 0 | NC_VAR_INFO_T *my_var; |
492 | 0 | int retval; |
493 | | |
494 | | /* Look up file and group metadata. */ |
495 | 0 | if ((retval = nc4_find_grp_h5(ncid, &my_grp, &my_h5))) |
496 | 0 | return retval; |
497 | 0 | assert(my_grp && my_h5); |
498 | | |
499 | | /* Find the var. */ |
500 | 0 | if (!(my_var = (NC_VAR_INFO_T *)ncindexith(my_grp->vars, (size_t)varid))) |
501 | 0 | return NC_ENOTVAR; |
502 | 0 | assert(my_var && my_var->hdr.id == varid); |
503 | | |
504 | | /* Return pointers that caller wants. */ |
505 | 0 | if (h5) |
506 | 0 | *h5 = my_h5; |
507 | 0 | if (grp) |
508 | 0 | *grp = my_grp; |
509 | 0 | if (var) |
510 | 0 | *var = my_var; |
511 | |
|
512 | 0 | return NC_NOERR; |
513 | 0 | } |
514 | | |
515 | | /** |
516 | | * @internal Find a dim in the file. |
517 | | * |
518 | | * @param grp Pointer to group info struct. |
519 | | * @param dimid Dimension ID to find. |
520 | | * @param dim Pointer that gets pointer to dim info if found. |
521 | | * @param dim_grp Pointer that gets pointer to group info of group |
522 | | * that contains dimension. Ignored if NULL. |
523 | | * |
524 | | * @return ::NC_NOERR No error. |
525 | | * @return ::NC_EBADDIM Dimension not found. |
526 | | * @author Ed Hartnett, Dennis Heimbigner |
527 | | */ |
528 | | int |
529 | | nc4_find_dim(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T **dim, |
530 | | NC_GRP_INFO_T **dim_grp) |
531 | 0 | { |
532 | 0 | assert(grp && grp->nc4_info && dim); |
533 | 0 | LOG((4, "%s: dimid %d", __func__, dimid)); |
534 | | |
535 | | /* Find the dim info. */ |
536 | 0 | if (!((*dim) = nclistget(grp->nc4_info->alldims, (size_t)dimid))) |
537 | 0 | return NC_EBADDIM; |
538 | | |
539 | | /* Give the caller the group the dimension is in. */ |
540 | 0 | if (dim_grp) |
541 | 0 | *dim_grp = (*dim)->container; |
542 | |
|
543 | 0 | return NC_NOERR; |
544 | 0 | } |
545 | | |
546 | | /** |
547 | | * @internal Find a var (by name) in a grp. |
548 | | * |
549 | | * @param grp Pointer to group info. |
550 | | * @param name Name of var to find. |
551 | | * @param var Pointer that gets pointer to var info struct, if found. |
552 | | * |
553 | | * @return ::NC_NOERR No error. |
554 | | * @author Ed Hartnett |
555 | | */ |
556 | | int |
557 | | nc4_find_var(NC_GRP_INFO_T *grp, const char *name, NC_VAR_INFO_T **var) |
558 | 0 | { |
559 | 0 | assert(grp && var && name); |
560 | | |
561 | | /* Find the var info. */ |
562 | 0 | *var = (NC_VAR_INFO_T*)ncindexlookup(grp->vars,name); |
563 | 0 | return NC_NOERR; |
564 | 0 | } |
565 | | |
566 | | /** |
567 | | * @internal Locate netCDF type by name. |
568 | | * |
569 | | * @param start_grp Pointer to starting group info. |
570 | | * @param name Name of type to find. |
571 | | * |
572 | | * @return Pointer to type info, or NULL if not found. |
573 | | * @author Ed Hartnett, Dennis Heimbigner |
574 | | */ |
575 | | NC_TYPE_INFO_T * |
576 | | nc4_rec_find_named_type(NC_GRP_INFO_T *start_grp, char *name) |
577 | 0 | { |
578 | 0 | NC_GRP_INFO_T *g; |
579 | 0 | NC_TYPE_INFO_T *type, *res; |
580 | |
|
581 | 0 | assert(start_grp); |
582 | | |
583 | | /* Does this group have the type we are searching for? */ |
584 | 0 | type = (NC_TYPE_INFO_T*)ncindexlookup(start_grp->type,name); |
585 | 0 | if(type != NULL) |
586 | 0 | return type; |
587 | | |
588 | | /* Search subgroups. */ |
589 | 0 | for(size_t i=0;i<ncindexsize(start_grp->children);i++) { |
590 | 0 | g = (NC_GRP_INFO_T*)ncindexith(start_grp->children,i); |
591 | 0 | if(g == NULL) continue; |
592 | 0 | if ((res = nc4_rec_find_named_type(g, name))) |
593 | 0 | return res; |
594 | 0 | } |
595 | | /* Can't find it. Oh, woe is me! */ |
596 | 0 | return NULL; |
597 | 0 | } |
598 | | |
599 | | /** |
600 | | * @internal Use a netCDF typeid to find a type in a type_list. |
601 | | * |
602 | | * @param h5 Pointer to HDF5 file info struct. |
603 | | * @param typeid The netCDF type ID. |
604 | | * @param type Pointer to pointer to the list of type info structs. |
605 | | * |
606 | | * @return ::NC_NOERR No error. |
607 | | * @return ::NC_EINVAL Invalid input. |
608 | | * @author Ed Hartnett |
609 | | */ |
610 | | int |
611 | | nc4_find_type(const NC_FILE_INFO_T *h5, nc_type typeid, NC_TYPE_INFO_T **type) |
612 | 0 | { |
613 | | /* Check inputs. */ |
614 | 0 | assert(h5); |
615 | 0 | if (typeid < 0 || !type) |
616 | 0 | return NC_EINVAL; |
617 | 0 | *type = NULL; |
618 | | |
619 | | /* Atomic types don't have associated NC_TYPE_INFO_T struct, just |
620 | | * return NOERR. */ |
621 | 0 | if (typeid <= NC_STRING) |
622 | 0 | return NC_NOERR; |
623 | | |
624 | | /* Find the type. */ |
625 | 0 | if (!(*type = nclistget(h5->alltypes, (size_t)typeid))) |
626 | 0 | return NC_EBADTYPID; |
627 | | |
628 | 0 | return NC_NOERR; |
629 | 0 | } |
630 | | |
631 | | /** |
632 | | * @internal Given a group, find an att. If name is provided, use that, |
633 | | * otherwise use the attnum. |
634 | | * |
635 | | * @param grp Pointer to group info struct. |
636 | | * @param varid Variable ID. |
637 | | * @param name Name to of attribute. |
638 | | * @param attnum Number of attribute. |
639 | | * @param att Pointer to pointer that gets attribute info struct. |
640 | | * |
641 | | * @return ::NC_NOERR No error. |
642 | | * @return ::NC_ENOTVAR Variable not found. |
643 | | * @return ::NC_ENOTATT Attribute not found. |
644 | | * @author Ed Hartnett |
645 | | */ |
646 | | int |
647 | | nc4_find_grp_att(NC_GRP_INFO_T *grp, int varid, const char *name, int attnum, |
648 | | NC_ATT_INFO_T **att) |
649 | 0 | { |
650 | 0 | NC_VAR_INFO_T *var; |
651 | 0 | NC_ATT_INFO_T *my_att; |
652 | 0 | NCindex *attlist = NULL; |
653 | |
|
654 | 0 | assert(grp && grp->hdr.name && att); |
655 | |
|
656 | 0 | LOG((4, "%s: grp->name %s varid %d attnum %d", __func__, grp->hdr.name, |
657 | 0 | varid, attnum)); |
658 | | |
659 | | /* Get either the global or a variable attribute list. */ |
660 | 0 | if (varid == NC_GLOBAL) |
661 | 0 | { |
662 | 0 | attlist = grp->att; |
663 | 0 | } |
664 | 0 | else |
665 | 0 | { |
666 | 0 | var = (NC_VAR_INFO_T*)ncindexith(grp->vars,(size_t)varid); |
667 | 0 | if (!var) return NC_ENOTVAR; |
668 | | |
669 | 0 | attlist = var->att; |
670 | 0 | } |
671 | 0 | assert(attlist); |
672 | | |
673 | | /* Now find the attribute by name or number. If a name is provided, |
674 | | * ignore the attnum. */ |
675 | 0 | if (name) |
676 | 0 | my_att = (NC_ATT_INFO_T *)ncindexlookup(attlist, name); |
677 | 0 | else |
678 | 0 | my_att = (NC_ATT_INFO_T *)ncindexith(attlist, (size_t)attnum); |
679 | |
|
680 | 0 | if (!my_att) |
681 | 0 | return NC_ENOTATT; |
682 | | |
683 | 0 | *att = my_att; |
684 | 0 | return NC_NOERR; |
685 | 0 | } |
686 | | |
687 | | /** |
688 | | * @internal Given an ncid, varid, and name or attnum, find and return |
689 | | * pointer to NC_ATT_INFO_T metadata. |
690 | | * |
691 | | * @param ncid File and group ID. |
692 | | * @param varid Variable ID. |
693 | | * @param name Name to of attribute. |
694 | | * @param attnum Number of attribute. |
695 | | * @param att Pointer to pointer that gets attribute info struct. |
696 | | |
697 | | * |
698 | | * @return ::NC_NOERR No error. |
699 | | * @return ::NC_ENOTVAR Variable not found. |
700 | | * @return ::NC_ENOTATT Attribute not found. |
701 | | * @author Ed Hartnett |
702 | | */ |
703 | | int |
704 | | nc4_find_nc_att(int ncid, int varid, const char *name, int attnum, |
705 | | NC_ATT_INFO_T **att) |
706 | 0 | { |
707 | 0 | NC_GRP_INFO_T *grp; |
708 | 0 | int retval; |
709 | |
|
710 | 0 | LOG((4, "nc4_find_nc_att: ncid 0x%x varid %d name %s attnum %d", |
711 | 0 | ncid, varid, name, attnum)); |
712 | | |
713 | | /* Find info for this file and group, and set pointer to each. */ |
714 | 0 | if ((retval = nc4_find_grp_h5(ncid, &grp, NULL))) |
715 | 0 | return retval; |
716 | 0 | assert(grp); |
717 | |
|
718 | 0 | return nc4_find_grp_att(grp, varid, name, attnum, att); |
719 | 0 | } |
720 | | |
721 | | /** |
722 | | * @internal Add NC_OBJ to allXXX lists in a file |
723 | | * |
724 | | * @param file Pointer to the containing file |
725 | | * @param obj Pointer to object to add. |
726 | | * |
727 | | * @author Dennis Heimbigner |
728 | | */ |
729 | | static void |
730 | | obj_track(NC_FILE_INFO_T* file, NC_OBJ* obj) |
731 | 0 | { |
732 | 0 | NClist* list = NULL; |
733 | | /* record the object in the file */ |
734 | 0 | switch (obj->sort) { |
735 | 0 | case NCDIM: list = file->alldims; break; |
736 | 0 | case NCTYP: list = file->alltypes; break; |
737 | 0 | case NCGRP: list = file->allgroups; break; |
738 | 0 | default: |
739 | 0 | assert(NC_FALSE); |
740 | 0 | } |
741 | | /* Insert at the appropriate point in the list */ |
742 | 0 | nclistset(list,(size_t)obj->id,obj); |
743 | 0 | } |
744 | | |
745 | | /** |
746 | | * @internal Create a new variable and insert into relevant |
747 | | * lists. Dimensionality info need not be known. |
748 | | * |
749 | | * @param grp the containing group |
750 | | * @param name the name for the new variable |
751 | | * @param var Pointer in which to return a pointer to the new var. |
752 | | * |
753 | | * @return ::NC_NOERR No error. |
754 | | * @return ::NC_ENOMEM Out of memory. |
755 | | * @author Ed Hartnett |
756 | | */ |
757 | | int |
758 | | nc4_var_list_add2(NC_GRP_INFO_T *grp, const char *name, NC_VAR_INFO_T **var) |
759 | 0 | { |
760 | 0 | NC_VAR_INFO_T *new_var = NULL; |
761 | 0 | NCglobalstate* gs = NC_getglobalstate(); |
762 | | |
763 | | /* Allocate storage for new variable. */ |
764 | 0 | if (!(new_var = calloc(1, sizeof(NC_VAR_INFO_T)))) |
765 | 0 | return NC_ENOMEM; |
766 | 0 | new_var->hdr.sort = NCVAR; |
767 | 0 | new_var->container = grp; |
768 | |
|
769 | 0 | memset(&new_var->chunkcache,0,sizeof(struct ChunkCache)); |
770 | | |
771 | | /* These are the HDF5-1.8.4 defaults. */ |
772 | 0 | new_var->chunkcache.size = gs->chunkcache.size; |
773 | 0 | new_var->chunkcache.nelems = gs->chunkcache.nelems; |
774 | 0 | new_var->chunkcache.preemption = gs->chunkcache.preemption; |
775 | | |
776 | | /* Now fill in the values in the var info structure. */ |
777 | 0 | new_var->hdr.id = (int)ncindexsize(grp->vars); |
778 | 0 | if (!(new_var->hdr.name = strdup(name))) { |
779 | 0 | if(new_var) |
780 | 0 | free(new_var); |
781 | 0 | return NC_ENOMEM; |
782 | 0 | } |
783 | | |
784 | | /* Create an indexed list for the attributes. */ |
785 | 0 | new_var->att = ncindexnew(0); |
786 | | |
787 | | /* Officially track it */ |
788 | 0 | ncindexadd(grp->vars, (NC_OBJ *)new_var); |
789 | | |
790 | | /* Set the var pointer, if one was given */ |
791 | 0 | if (var) |
792 | 0 | *var = new_var; |
793 | |
|
794 | 0 | return NC_NOERR; |
795 | 0 | } |
796 | | |
797 | | /** |
798 | | * @internal Set the number of dims in an NC_VAR_INFO_T struct. |
799 | | * |
800 | | * @param var Pointer to the var. |
801 | | * @param ndims Number of dimensions for this var. |
802 | | * |
803 | | * @return ::NC_NOERR No error. |
804 | | * @return ::NC_ENOMEM Out of memory. |
805 | | * @author Ed Hartnett |
806 | | */ |
807 | | int |
808 | | nc4_var_set_ndims(NC_VAR_INFO_T *var, int ndims) |
809 | 0 | { |
810 | 0 | assert(var); |
811 | | |
812 | | /* Remember the number of dimensions. */ |
813 | 0 | var->ndims = (size_t)ndims; |
814 | | |
815 | | /* Allocate space for dimension information. */ |
816 | 0 | if (ndims) |
817 | 0 | { |
818 | 0 | if (!(var->dim = calloc((size_t)ndims, sizeof(NC_DIM_INFO_T *)))) |
819 | 0 | return NC_ENOMEM; |
820 | 0 | if (!(var->dimids = calloc((size_t)ndims, sizeof(int)))) |
821 | 0 | return NC_ENOMEM; |
822 | | |
823 | | /* Initialize dimids to illegal values (-1). See the comment |
824 | | in nc4_rec_match_dimscales(). */ |
825 | 0 | memset(var->dimids, -1, (size_t)ndims * sizeof(int)); |
826 | 0 | } |
827 | | |
828 | 0 | return NC_NOERR; |
829 | 0 | } |
830 | | |
831 | | /** |
832 | | * @internal Create a new variable and insert int relevant list. |
833 | | * |
834 | | * @param grp the containing group |
835 | | * @param name the name for the new variable |
836 | | * @param ndims the rank of the new variable |
837 | | * @param var Pointer in which to return a pointer to the new var. |
838 | | * |
839 | | * @return ::NC_NOERR No error. |
840 | | * @return ::NC_ENOMEM Out of memory. |
841 | | * @author Ed Hartnett |
842 | | */ |
843 | | int |
844 | | nc4_var_list_add(NC_GRP_INFO_T* grp, const char* name, int ndims, |
845 | | NC_VAR_INFO_T **var) |
846 | 0 | { |
847 | 0 | int retval; |
848 | |
|
849 | 0 | if ((retval = nc4_var_list_add2(grp, name, var))) |
850 | 0 | return retval; |
851 | 0 | if ((retval = nc4_var_set_ndims(*var, ndims))) |
852 | 0 | return retval; |
853 | | |
854 | 0 | return NC_NOERR; |
855 | 0 | } |
856 | | |
857 | | /** |
858 | | * @internal Add a dimension to the dimension list for a group. |
859 | | * |
860 | | * @param grp container for the dim |
861 | | * @param name for the dim |
862 | | * @param len for the dim |
863 | | * @param assignedid override dimid if >= 0 |
864 | | * @param dim Pointer to pointer that gets the new dim info struct. |
865 | | * |
866 | | * @return ::NC_NOERR No error. |
867 | | * @return ::NC_ENOMEM Out of memory. |
868 | | * @author Ed Hartnett |
869 | | */ |
870 | | int |
871 | | nc4_dim_list_add(NC_GRP_INFO_T *grp, const char *name, size_t len, |
872 | | int assignedid, NC_DIM_INFO_T **dim) |
873 | 0 | { |
874 | 0 | NC_DIM_INFO_T *new_dim = NULL; |
875 | |
|
876 | 0 | assert(grp && name); |
877 | | |
878 | | /* Allocate memory for dim metadata. */ |
879 | 0 | if (!(new_dim = calloc(1, sizeof(NC_DIM_INFO_T)))) |
880 | 0 | return NC_ENOMEM; |
881 | | |
882 | 0 | new_dim->hdr.sort = NCDIM; |
883 | | |
884 | | /* Assign the dimension ID. */ |
885 | 0 | if (assignedid >= 0) |
886 | 0 | new_dim->hdr.id = assignedid; |
887 | 0 | else |
888 | 0 | new_dim->hdr.id = grp->nc4_info->next_dimid++; |
889 | | |
890 | | /* Remember the name and create a hash. */ |
891 | 0 | if (!(new_dim->hdr.name = strdup(name))) { |
892 | 0 | if(new_dim) |
893 | 0 | free(new_dim); |
894 | |
|
895 | 0 | return NC_ENOMEM; |
896 | 0 | } |
897 | | |
898 | | /* Is dimension unlimited? */ |
899 | 0 | new_dim->len = len; |
900 | 0 | if (len == NC_UNLIMITED) |
901 | 0 | new_dim->unlimited = NC_TRUE; |
902 | | |
903 | | /* Remember the containing group. */ |
904 | 0 | new_dim->container = grp; |
905 | | |
906 | | /* Add object to dimension list for this group. */ |
907 | 0 | ncindexadd(grp->dim, (NC_OBJ *)new_dim); |
908 | 0 | obj_track(grp->nc4_info, (NC_OBJ *)new_dim); |
909 | | |
910 | | /* Set the dim pointer, if one was given */ |
911 | 0 | if (dim) |
912 | 0 | *dim = new_dim; |
913 | |
|
914 | 0 | return NC_NOERR; |
915 | 0 | } |
916 | | |
917 | | /** |
918 | | * @internal Add to an attribute list. |
919 | | * |
920 | | * @param list NCindex of att info structs. |
921 | | * @param name name of the new attribute |
922 | | * @param att Pointer to pointer that gets the new att info |
923 | | * struct. Ignored if NULL. |
924 | | * |
925 | | * @return ::NC_NOERR No error. |
926 | | * @return ::NC_ENOMEM Out of memory. |
927 | | * @author Ed Hartnett |
928 | | */ |
929 | | int |
930 | | nc4_att_list_add(NCindex *list, const char *name, NC_ATT_INFO_T **att) |
931 | 0 | { |
932 | 0 | NC_ATT_INFO_T *new_att = NULL; |
933 | |
|
934 | 0 | LOG((3, "%s: name %s ", __func__, name)); |
935 | |
|
936 | 0 | if (!(new_att = calloc(1, sizeof(NC_ATT_INFO_T)))) |
937 | 0 | return NC_ENOMEM; |
938 | 0 | new_att->hdr.sort = NCATT; |
939 | | |
940 | | /* Fill in the information we know. */ |
941 | 0 | new_att->hdr.id = (int)ncindexsize(list); |
942 | 0 | if (!(new_att->hdr.name = strdup(name))) { |
943 | 0 | if(new_att) |
944 | 0 | free(new_att); |
945 | 0 | return NC_ENOMEM; |
946 | 0 | } |
947 | | |
948 | | /* Add object to list as specified by its number */ |
949 | 0 | ncindexadd(list, (NC_OBJ *)new_att); |
950 | | |
951 | | /* Set the attribute pointer, if one was given */ |
952 | 0 | if (att) |
953 | 0 | *att = new_att; |
954 | |
|
955 | 0 | return NC_NOERR; |
956 | 0 | } |
957 | | |
958 | | /** |
959 | | * @internal Add a group to a group list. |
960 | | * |
961 | | * @param h5 Pointer to the file info. |
962 | | * @param parent Pointer to the parent group. Will be NULL when adding |
963 | | * the root group. |
964 | | * @param name Name of the group. |
965 | | * @param grp Pointer to pointer that gets new group info |
966 | | * struct. Ignored if NULL. |
967 | | * |
968 | | * @return ::NC_NOERR No error. |
969 | | * @return ::NC_ENOMEM Out of memory. |
970 | | * @author Ed Hartnett, Dennis Heimbigner |
971 | | */ |
972 | | int |
973 | | nc4_grp_list_add(NC_FILE_INFO_T *h5, NC_GRP_INFO_T *parent, char *name, |
974 | | NC_GRP_INFO_T **grp) |
975 | 0 | { |
976 | 0 | NC_GRP_INFO_T *new_grp; |
977 | | |
978 | | /* Check inputs. */ |
979 | 0 | assert(h5 && name); |
980 | 0 | LOG((3, "%s: name %s ", __func__, name)); |
981 | | |
982 | | /* Get the memory to store this groups info. */ |
983 | 0 | if (!(new_grp = calloc(1, sizeof(NC_GRP_INFO_T)))) |
984 | 0 | return NC_ENOMEM; |
985 | | |
986 | | /* Fill in this group's information. */ |
987 | 0 | new_grp->hdr.sort = NCGRP; |
988 | 0 | new_grp->nc4_info = h5; |
989 | 0 | new_grp->parent = parent; |
990 | | |
991 | | /* Assign the group ID. The root group will get id 0. */ |
992 | 0 | new_grp->hdr.id = h5->next_nc_grpid++; |
993 | 0 | assert(parent || !new_grp->hdr.id); |
994 | | |
995 | | /* Handle the group name. */ |
996 | 0 | if (!(new_grp->hdr.name = strdup(name))) |
997 | 0 | { |
998 | 0 | free(new_grp); |
999 | 0 | return NC_ENOMEM; |
1000 | 0 | } |
1001 | | |
1002 | | /* Set up new indexed lists for stuff this group can contain. */ |
1003 | 0 | new_grp->children = ncindexnew(0); |
1004 | 0 | new_grp->dim = ncindexnew(0); |
1005 | 0 | new_grp->att = ncindexnew(0); |
1006 | 0 | new_grp->type = ncindexnew(0); |
1007 | 0 | new_grp->vars = ncindexnew(0); |
1008 | | |
1009 | | /* Add object to lists */ |
1010 | 0 | if (parent) |
1011 | 0 | ncindexadd(parent->children, (NC_OBJ *)new_grp); |
1012 | 0 | obj_track(h5, (NC_OBJ *)new_grp); |
1013 | | |
1014 | | /* Set the group pointer, if one was given */ |
1015 | 0 | if (grp) |
1016 | 0 | *grp = new_grp; |
1017 | |
|
1018 | 0 | return NC_NOERR; |
1019 | 0 | } |
1020 | | |
1021 | | /** |
1022 | | * @internal Names for groups, variables, and types must not be the |
1023 | | * same. This function checks that a proposed name is not already in |
1024 | | * use. Normalzation of UTF8 strings should happen before this |
1025 | | * function is called. |
1026 | | * |
1027 | | * @param grp Pointer to group info struct. |
1028 | | * @param name Name to check. |
1029 | | * |
1030 | | * @return ::NC_NOERR No error. |
1031 | | * @return ::NC_ENAMEINUSE Name is in use. |
1032 | | * @author Ed Hartnett, Dennis Heimbigner |
1033 | | */ |
1034 | | int |
1035 | | nc4_check_dup_name(NC_GRP_INFO_T *grp, char *name) |
1036 | 0 | { |
1037 | 0 | NC_TYPE_INFO_T *type; |
1038 | 0 | NC_GRP_INFO_T *g; |
1039 | 0 | NC_VAR_INFO_T *var; |
1040 | | |
1041 | | /* Any types of this name? */ |
1042 | 0 | type = (NC_TYPE_INFO_T*)ncindexlookup(grp->type,name); |
1043 | 0 | if(type != NULL) |
1044 | 0 | return NC_ENAMEINUSE; |
1045 | | |
1046 | | /* Any child groups of this name? */ |
1047 | 0 | g = (NC_GRP_INFO_T*)ncindexlookup(grp->children,name); |
1048 | 0 | if(g != NULL) |
1049 | 0 | return NC_ENAMEINUSE; |
1050 | | |
1051 | | /* Any variables of this name? */ |
1052 | 0 | var = (NC_VAR_INFO_T*)ncindexlookup(grp->vars,name); |
1053 | 0 | if(var != NULL) |
1054 | 0 | return NC_ENAMEINUSE; |
1055 | | |
1056 | 0 | return NC_NOERR; |
1057 | 0 | } |
1058 | | |
1059 | | /** |
1060 | | * @internal Create a type, but do not add to various lists nor |
1061 | | * increment its ref count |
1062 | | * |
1063 | | * @param size Size of type in bytes. |
1064 | | * @param name Name of type. |
1065 | | * @param assignedid if >= 0 then override the default type id. |
1066 | | * @param type Pointer that gets pointer to new type info struct. |
1067 | | * |
1068 | | * @return ::NC_NOERR No error. |
1069 | | * @return ::NC_ENOMEM Out of memory. |
1070 | | * @author Ed Hartnett, Ward Fisher |
1071 | | */ |
1072 | | int |
1073 | | nc4_type_new(size_t size, const char *name, int assignedid, |
1074 | | NC_TYPE_INFO_T **type) |
1075 | 0 | { |
1076 | 0 | NC_TYPE_INFO_T *new_type; |
1077 | |
|
1078 | 0 | LOG((4, "%s: size %d name %s assignedid %d", __func__, size, name, assignedid)); |
1079 | | |
1080 | | /* Check inputs. */ |
1081 | 0 | assert(type); |
1082 | | |
1083 | | /* Allocate memory for the type */ |
1084 | 0 | if (!(new_type = calloc(1, sizeof(NC_TYPE_INFO_T)))) |
1085 | 0 | return NC_ENOMEM; |
1086 | 0 | new_type->hdr.sort = NCTYP; |
1087 | 0 | new_type->hdr.id = assignedid; |
1088 | | |
1089 | | /* Remember info about this type. */ |
1090 | 0 | new_type->size = size; |
1091 | 0 | if (!(new_type->hdr.name = strdup(name))) { |
1092 | 0 | free(new_type); |
1093 | 0 | return NC_ENOMEM; |
1094 | 0 | } |
1095 | | |
1096 | | /* Return a pointer to the new type. */ |
1097 | 0 | *type = new_type; |
1098 | |
|
1099 | 0 | return NC_NOERR; |
1100 | 0 | } |
1101 | | |
1102 | | /** |
1103 | | * @internal Add to the type list. |
1104 | | * |
1105 | | * @param grp Pointer to group info struct. |
1106 | | * @param size Size of type in bytes. |
1107 | | * @param name Name of type. |
1108 | | * @param type Pointer that gets pointer to new type info |
1109 | | * struct. |
1110 | | * |
1111 | | * @return ::NC_NOERR No error. |
1112 | | * @return ::NC_ENOMEM Out of memory. |
1113 | | * @author Ed Hartnett |
1114 | | */ |
1115 | | int |
1116 | | nc4_type_list_add(NC_GRP_INFO_T *grp, size_t size, const char *name, |
1117 | | NC_TYPE_INFO_T **type) |
1118 | 0 | { |
1119 | 0 | NC_TYPE_INFO_T *new_type; |
1120 | 0 | int retval; |
1121 | | |
1122 | | /* Check inputs. */ |
1123 | 0 | assert(grp && name && type); |
1124 | 0 | LOG((4, "%s: size %d name %s", __func__, size, name)); |
1125 | | |
1126 | | /* Create the new TYPE_INFO struct. */ |
1127 | 0 | if ((retval = nc4_type_new(size, name, grp->nc4_info->next_typeid, |
1128 | 0 | &new_type))) |
1129 | 0 | return retval; |
1130 | 0 | grp->nc4_info->next_typeid++; |
1131 | | |
1132 | | /* Increment the ref. count on the type */ |
1133 | 0 | new_type->rc++; |
1134 | | |
1135 | | /* Add object to lists */ |
1136 | 0 | ncindexadd(grp->type, (NC_OBJ *)new_type); |
1137 | 0 | obj_track(grp->nc4_info,(NC_OBJ*)new_type); |
1138 | | |
1139 | | /* back link */ |
1140 | 0 | new_type->container = grp; |
1141 | | |
1142 | | /* Return a pointer to the new type. */ |
1143 | 0 | *type = new_type; |
1144 | |
|
1145 | 0 | return NC_NOERR; |
1146 | 0 | } |
1147 | | |
1148 | | /** |
1149 | | * @internal Add to the compound field list. |
1150 | | * |
1151 | | * @param parent parent type |
1152 | | * @param name Name of the field. |
1153 | | * @param offset Offset in bytes. |
1154 | | * @param xtype The netCDF type of the field. |
1155 | | * @param ndims The number of dimensions of the field. |
1156 | | * @param dim_sizesp An array of dim sizes for the field. |
1157 | | * |
1158 | | * @return ::NC_NOERR No error. |
1159 | | * @author Ed Hartnett, Dennis Heimbigner |
1160 | | */ |
1161 | | int |
1162 | | nc4_field_list_add(NC_TYPE_INFO_T *parent, const char *name, |
1163 | | size_t offset, nc_type xtype, int ndims, |
1164 | | const int *dim_sizesp) |
1165 | 0 | { |
1166 | 0 | NC_FIELD_INFO_T *field; |
1167 | | |
1168 | | /* Name has already been checked and UTF8 normalized. */ |
1169 | 0 | if (!name) |
1170 | 0 | return NC_EINVAL; |
1171 | | |
1172 | | /* Allocate storage for this field information. */ |
1173 | 0 | if (!(field = calloc(1, sizeof(NC_FIELD_INFO_T)))) |
1174 | 0 | return NC_ENOMEM; |
1175 | 0 | field->hdr.sort = NCFLD; |
1176 | | |
1177 | | /* Store the information about this field. */ |
1178 | 0 | if (!(field->hdr.name = strdup(name))) |
1179 | 0 | { |
1180 | 0 | free(field); |
1181 | 0 | return NC_ENOMEM; |
1182 | 0 | } |
1183 | 0 | field->nc_typeid = xtype; |
1184 | 0 | field->offset = offset; |
1185 | 0 | field->ndims = ndims; |
1186 | 0 | if (ndims) |
1187 | 0 | { |
1188 | 0 | int i; |
1189 | 0 | if (!(field->dim_size = malloc((size_t)ndims * sizeof(int)))) |
1190 | 0 | { |
1191 | 0 | free(field->hdr.name); |
1192 | 0 | free(field); |
1193 | 0 | return NC_ENOMEM; |
1194 | 0 | } |
1195 | 0 | for (i = 0; i < ndims; i++) |
1196 | 0 | field->dim_size[i] = dim_sizesp[i]; |
1197 | 0 | } |
1198 | | |
1199 | | /* Add object to lists */ |
1200 | 0 | field->hdr.id = (int)nclistlength(parent->u.c.field); |
1201 | 0 | nclistpush(parent->u.c.field,field); |
1202 | |
|
1203 | 0 | return NC_NOERR; |
1204 | 0 | } |
1205 | | |
1206 | | /** |
1207 | | * @internal Add a member to an enum type. |
1208 | | * |
1209 | | * @param parent Containing NC_TYPE_INFO_T object |
1210 | | * @param size Size in bytes of new member. |
1211 | | * @param name Name of the member. |
1212 | | * @param value Value to associate with member. |
1213 | | * |
1214 | | * @return ::NC_NOERR No error. |
1215 | | * @return ::NC_ENOMEM Out of memory. |
1216 | | * @author Ed Hartnett |
1217 | | */ |
1218 | | int |
1219 | | nc4_enum_member_add(NC_TYPE_INFO_T *parent, size_t size, |
1220 | | const char *name, const void *value) |
1221 | 0 | { |
1222 | 0 | NC_ENUM_MEMBER_INFO_T *member; |
1223 | | |
1224 | | /* Name has already been checked. */ |
1225 | 0 | assert(name && size > 0 && value); |
1226 | 0 | LOG((4, "%s: size %d name %s", __func__, size, name)); |
1227 | | |
1228 | | /* Allocate storage for this field information. */ |
1229 | 0 | if (!(member = calloc(1, sizeof(NC_ENUM_MEMBER_INFO_T)))) |
1230 | 0 | return NC_ENOMEM; |
1231 | 0 | if (!(member->value = malloc(size))) { |
1232 | 0 | free(member); |
1233 | 0 | return NC_ENOMEM; |
1234 | 0 | } |
1235 | 0 | if (!(member->name = strdup(name))) { |
1236 | 0 | free(member->value); |
1237 | 0 | free(member); |
1238 | 0 | return NC_ENOMEM; |
1239 | 0 | } |
1240 | | |
1241 | | /* Store the value for this member. */ |
1242 | 0 | memcpy(member->value, value, size); |
1243 | | |
1244 | | /* Add object to list */ |
1245 | 0 | nclistpush(parent->u.e.enum_member,member); |
1246 | |
|
1247 | 0 | return NC_NOERR; |
1248 | 0 | } |
1249 | | |
1250 | | /** |
1251 | | * @internal Free up a field |
1252 | | * |
1253 | | * @param field Pointer to field info of field to delete. |
1254 | | * |
1255 | | * @author Ed Hartnett |
1256 | | */ |
1257 | | static void |
1258 | | field_free(NC_FIELD_INFO_T *field) |
1259 | 0 | { |
1260 | | /* Free some stuff. */ |
1261 | 0 | if (field->hdr.name) |
1262 | 0 | free(field->hdr.name); |
1263 | 0 | if (field->dim_size) |
1264 | 0 | free(field->dim_size); |
1265 | | |
1266 | | /* Nc_Free the memory. */ |
1267 | 0 | free(field); |
1268 | 0 | } |
1269 | | |
1270 | | /** |
1271 | | * @internal Free allocated space for type information. |
1272 | | * |
1273 | | * @param type Pointer to type info struct. |
1274 | | * |
1275 | | * @return ::NC_NOERR No error. |
1276 | | * @return ::NC_EHDFERR HDF5 error. |
1277 | | * @author Ed Hartnett, Dennis Heimbigner |
1278 | | */ |
1279 | | int |
1280 | | nc4_type_free(NC_TYPE_INFO_T *type) |
1281 | 0 | { |
1282 | 0 | size_t i; |
1283 | |
|
1284 | 0 | assert(type && type->rc && type->hdr.name); |
1285 | | |
1286 | | /* Decrement the ref. count on the type */ |
1287 | 0 | type->rc--; |
1288 | | |
1289 | | /* Release the type, if the ref. count drops to zero */ |
1290 | 0 | if (type->rc == 0) |
1291 | 0 | { |
1292 | 0 | LOG((4, "%s: deleting type %s", __func__, type->hdr.name)); |
1293 | | |
1294 | | /* Free the name. */ |
1295 | 0 | free(type->hdr.name); |
1296 | | |
1297 | | /* Enums and compound types have lists of fields to clean up. */ |
1298 | 0 | switch (type->nc_type_class) |
1299 | 0 | { |
1300 | 0 | case NC_COMPOUND: |
1301 | 0 | { |
1302 | 0 | NC_FIELD_INFO_T *field; |
1303 | | |
1304 | | /* Delete all the fields in this type (there will be some if its a |
1305 | | * compound). */ |
1306 | 0 | for(i=0;i<nclistlength(type->u.c.field);i++) { |
1307 | 0 | field = nclistget(type->u.c.field,i); |
1308 | 0 | field_free(field); |
1309 | 0 | } |
1310 | 0 | nclistfree(type->u.c.field); |
1311 | 0 | } |
1312 | 0 | break; |
1313 | | |
1314 | 0 | case NC_ENUM: |
1315 | 0 | { |
1316 | 0 | NC_ENUM_MEMBER_INFO_T *enum_member; |
1317 | | |
1318 | | /* Delete all the enum_members, if any. */ |
1319 | 0 | for(i=0;i<nclistlength(type->u.e.enum_member);i++) { |
1320 | 0 | enum_member = nclistget(type->u.e.enum_member,i); |
1321 | 0 | free(enum_member->value); |
1322 | 0 | free(enum_member->name); |
1323 | 0 | free(enum_member); |
1324 | 0 | } |
1325 | 0 | nclistfree(type->u.e.enum_member); |
1326 | 0 | } |
1327 | 0 | break; |
1328 | | |
1329 | 0 | default: |
1330 | 0 | break; |
1331 | 0 | } |
1332 | | |
1333 | | /* Release the memory. */ |
1334 | 0 | free(type); |
1335 | 0 | } |
1336 | | |
1337 | 0 | return NC_NOERR; |
1338 | 0 | } |
1339 | | |
1340 | | /** |
1341 | | * @internal Free memory of an attribute object |
1342 | | * |
1343 | | * @param att Pointer to attribute info struct. |
1344 | | * |
1345 | | * @return ::NC_NOERR No error. |
1346 | | * @author Ed Hartnett |
1347 | | */ |
1348 | | int |
1349 | | nc4_att_free(NC_ATT_INFO_T *att) |
1350 | 0 | { |
1351 | 0 | int stat = NC_NOERR; |
1352 | |
|
1353 | 0 | assert(att); |
1354 | 0 | LOG((3, "%s: name %s ", __func__, att->hdr.name)); |
1355 | | |
1356 | | /* Free the name. */ |
1357 | 0 | if (att->hdr.name) |
1358 | 0 | free(att->hdr.name); |
1359 | |
|
1360 | 0 | if (att->data) { |
1361 | 0 | NC_OBJ* parent; |
1362 | 0 | NC_FILE_INFO_T* h5 = NULL; |
1363 | | |
1364 | | /* Locate relevant objects */ |
1365 | 0 | parent = att->container; |
1366 | 0 | if(parent->sort == NCVAR) parent = (NC_OBJ*)(((NC_VAR_INFO_T*)parent)->container); |
1367 | 0 | assert(parent->sort == NCGRP); |
1368 | 0 | h5 = ((NC_GRP_INFO_T*)parent)->nc4_info; |
1369 | | /* Reclaim the attribute data */ |
1370 | 0 | if((stat = NC_reclaim_data(h5->controller,att->nc_typeid,att->data,att->len))) goto done; |
1371 | 0 | free(att->data); /* reclaim top level */ |
1372 | 0 | att->data = NULL; |
1373 | 0 | } |
1374 | | |
1375 | 0 | done: |
1376 | 0 | free(att); |
1377 | 0 | return stat; |
1378 | 0 | } |
1379 | | |
1380 | | /** |
1381 | | * @internal Delete a var, and free the memory. All HDF5 objects for |
1382 | | * the var must be closed before this is called. |
1383 | | * |
1384 | | * @param var Pointer to the var info struct of var to delete. |
1385 | | * |
1386 | | * @return ::NC_NOERR No error. |
1387 | | * @author Ed Hartnett, Dennis Heimbigner |
1388 | | */ |
1389 | | static int |
1390 | | var_free(NC_VAR_INFO_T *var) |
1391 | 0 | { |
1392 | 0 | int retval; |
1393 | |
|
1394 | 0 | assert(var); |
1395 | 0 | LOG((4, "%s: deleting var %s", __func__, var->hdr.name)); |
1396 | | |
1397 | | /* First delete all the attributes attached to this var. */ |
1398 | 0 | for (size_t i = 0; i < ncindexsize(var->att); i++) |
1399 | 0 | if ((retval = nc4_att_free((NC_ATT_INFO_T *)ncindexith(var->att, i)))) |
1400 | 0 | return retval; |
1401 | 0 | ncindexfree(var->att); |
1402 | | |
1403 | | /* Free some things that may be allocated. */ |
1404 | 0 | if (var->chunksizes) |
1405 | 0 | free(var->chunksizes); |
1406 | |
|
1407 | 0 | if (var->alt_name) |
1408 | 0 | free(var->alt_name); |
1409 | |
|
1410 | 0 | if (var->dimids) |
1411 | 0 | free(var->dimids); |
1412 | |
|
1413 | 0 | if (var->dim) |
1414 | 0 | free(var->dim); |
1415 | |
|
1416 | 0 | memset(&var->chunkcache,0,sizeof(struct ChunkCache)); |
1417 | | |
1418 | | /* Delete any fill value allocation. */ |
1419 | 0 | if (var->fill_value) { |
1420 | 0 | int tid = var->type_info->hdr.id; |
1421 | 0 | if((retval = NC_reclaim_data_all(var->container->nc4_info->controller, tid, var->fill_value, 1))) return retval; |
1422 | 0 | var->fill_value = NULL; |
1423 | 0 | } |
1424 | | |
1425 | | /* Release type information */ |
1426 | 0 | if (var->type_info) |
1427 | 0 | if ((retval = nc4_type_free(var->type_info))) |
1428 | 0 | return retval; |
1429 | | |
1430 | | /* Do this last because debugging may need it */ |
1431 | 0 | if (var->hdr.name) |
1432 | 0 | free(var->hdr.name); |
1433 | | |
1434 | | /* Delete the var. */ |
1435 | 0 | free(var); |
1436 | |
|
1437 | 0 | return NC_NOERR; |
1438 | 0 | } |
1439 | | |
1440 | | /** |
1441 | | * @internal Delete a var, and free the memory. |
1442 | | * |
1443 | | * @param grp Pointer to the strct for the containing group. |
1444 | | * @param var Pointer to the var info struct of var to delete. |
1445 | | * |
1446 | | * @return ::NC_NOERR No error. |
1447 | | * @author Ed Hartnett, Dennis Heimbigner |
1448 | | */ |
1449 | | int |
1450 | | nc4_var_list_del(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var) |
1451 | 0 | { |
1452 | 0 | int i; |
1453 | |
|
1454 | 0 | assert(var && grp); |
1455 | | |
1456 | | /* Remove from lists */ |
1457 | 0 | i = ncindexfind(grp->vars, (NC_OBJ *)var); |
1458 | 0 | if (i >= 0) |
1459 | 0 | ncindexidel(grp->vars, (size_t)i); |
1460 | |
|
1461 | 0 | return var_free(var); |
1462 | 0 | } |
1463 | | |
1464 | | /** |
1465 | | * @internal Free a dim |
1466 | | * |
1467 | | * @param dim Pointer to dim info struct of type to delete. |
1468 | | * |
1469 | | * @return ::NC_NOERR No error. |
1470 | | * @author Ed Hartnett, Ward Fisher |
1471 | | */ |
1472 | | static int |
1473 | | dim_free(NC_DIM_INFO_T *dim) |
1474 | 0 | { |
1475 | 0 | assert(dim); |
1476 | 0 | LOG((4, "%s: deleting dim %s", __func__, dim->hdr.name)); |
1477 | | |
1478 | | /* Free memory allocated for names. */ |
1479 | 0 | if (dim->hdr.name) |
1480 | 0 | free(dim->hdr.name); |
1481 | |
|
1482 | 0 | free(dim); |
1483 | 0 | return NC_NOERR; |
1484 | 0 | } |
1485 | | |
1486 | | /** |
1487 | | * @internal Free a dim and unlist it |
1488 | | * |
1489 | | * @param grp Pointer to dim's containing group |
1490 | | * @param dim Pointer to dim info struct of type to delete. |
1491 | | * |
1492 | | * @return ::NC_NOERR No error. |
1493 | | * @author Dennis Heimbigner |
1494 | | */ |
1495 | | int |
1496 | | nc4_dim_list_del(NC_GRP_INFO_T *grp, NC_DIM_INFO_T *dim) |
1497 | 0 | { |
1498 | 0 | if (grp && dim) |
1499 | 0 | { |
1500 | 0 | int pos = ncindexfind(grp->dim, (NC_OBJ *)dim); |
1501 | 0 | if(pos >= 0) |
1502 | 0 | ncindexidel(grp->dim, (size_t)pos); |
1503 | 0 | } |
1504 | |
|
1505 | 0 | return dim_free(dim); |
1506 | 0 | } |
1507 | | |
1508 | | /** |
1509 | | * @internal Recursively delete the data for a group (and everything |
1510 | | * it contains) in our internal metadata store. |
1511 | | * |
1512 | | * @param grp Pointer to group info struct. |
1513 | | * |
1514 | | * @return ::NC_NOERR No error. |
1515 | | * @author Ed Hartnett, Dennis Heimbigner |
1516 | | */ |
1517 | | int |
1518 | | nc4_rec_grp_del(NC_GRP_INFO_T *grp) |
1519 | 0 | { |
1520 | 0 | int retval; |
1521 | |
|
1522 | 0 | assert(grp); |
1523 | 0 | LOG((3, "%s: grp->name %s", __func__, grp->hdr.name)); |
1524 | | |
1525 | | /* Recursively call this function for each child, if any, stopping |
1526 | | * if there is an error. */ |
1527 | 0 | for (size_t i = 0; i < ncindexsize(grp->children); i++) |
1528 | 0 | if ((retval = nc4_rec_grp_del((NC_GRP_INFO_T *)ncindexith(grp->children, |
1529 | 0 | i)))) |
1530 | 0 | return retval; |
1531 | 0 | ncindexfree(grp->children); |
1532 | | |
1533 | | /* Free attributes */ |
1534 | 0 | for (size_t i = 0; i < ncindexsize(grp->att); i++) |
1535 | 0 | if ((retval = nc4_att_free((NC_ATT_INFO_T *)ncindexith(grp->att, i)))) |
1536 | 0 | return retval; |
1537 | 0 | ncindexfree(grp->att); |
1538 | | |
1539 | | /* Delete all vars. */ |
1540 | 0 | for (size_t i = 0; i < ncindexsize(grp->vars); i++) { |
1541 | 0 | NC_VAR_INFO_T* v = (NC_VAR_INFO_T *)ncindexith(grp->vars, i); |
1542 | 0 | if ((retval = var_free(v))) |
1543 | 0 | return retval; |
1544 | 0 | } |
1545 | 0 | ncindexfree(grp->vars); |
1546 | | |
1547 | | /* Delete all dims, and free the list of dims. */ |
1548 | 0 | for (size_t i = 0; i < ncindexsize(grp->dim); i++) |
1549 | 0 | if ((retval = dim_free((NC_DIM_INFO_T *)ncindexith(grp->dim, i)))) |
1550 | 0 | return retval; |
1551 | 0 | ncindexfree(grp->dim); |
1552 | | |
1553 | | /* Delete all types. */ |
1554 | 0 | for (size_t i = 0; i < ncindexsize(grp->type); i++) |
1555 | 0 | if ((retval = nc4_type_free((NC_TYPE_INFO_T *)ncindexith(grp->type, i)))) |
1556 | 0 | return retval; |
1557 | 0 | ncindexfree(grp->type); |
1558 | | |
1559 | | /* Free the name. */ |
1560 | 0 | free(grp->hdr.name); |
1561 | | |
1562 | | /* Free up this group */ |
1563 | 0 | free(grp); |
1564 | |
|
1565 | 0 | return NC_NOERR; |
1566 | 0 | } |
1567 | | |
1568 | | /** |
1569 | | * @internal Recursively delete the data for a group (and everything |
1570 | | * it contains) in our internal metadata store. |
1571 | | * |
1572 | | * @param grp Pointer to group info struct. |
1573 | | * |
1574 | | * @return ::NC_NOERR No error. |
1575 | | * @author Ed Hartnett, Dennis Heimbigner |
1576 | | */ |
1577 | | int |
1578 | | nc4_rec_grp_del_att_data(NC_GRP_INFO_T *grp) |
1579 | 0 | { |
1580 | 0 | int retval; |
1581 | |
|
1582 | 0 | assert(grp); |
1583 | 0 | LOG((3, "%s: grp->name %s", __func__, grp->hdr.name)); |
1584 | | |
1585 | | /* Recursively call this function for each child, if any, stopping |
1586 | | * if there is an error. */ |
1587 | 0 | for (size_t i = 0; i < ncindexsize(grp->children); i++) |
1588 | 0 | if ((retval = nc4_rec_grp_del_att_data((NC_GRP_INFO_T *)ncindexith(grp->children, i)))) |
1589 | 0 | return retval; |
1590 | | |
1591 | | /* Free attribute data in this group */ |
1592 | 0 | for (size_t i = 0; i < ncindexsize(grp->att); i++) { |
1593 | 0 | NC_ATT_INFO_T * att = (NC_ATT_INFO_T*)ncindexith(grp->att, i); |
1594 | 0 | if((retval = NC_reclaim_data_all(grp->nc4_info->controller,att->nc_typeid,att->data,att->len))) |
1595 | 0 | return retval; |
1596 | 0 | att->data = NULL; |
1597 | 0 | att->len = 0; |
1598 | 0 | att->dirty = 0; |
1599 | 0 | } |
1600 | | |
1601 | | /* Delete att data from all contained vars in this group */ |
1602 | 0 | for (size_t i = 0; i < ncindexsize(grp->vars); i++) { |
1603 | 0 | NC_VAR_INFO_T* v = (NC_VAR_INFO_T *)ncindexith(grp->vars, i); |
1604 | 0 | for(size_t j=0;j<ncindexsize(v->att);j++) { |
1605 | 0 | NC_ATT_INFO_T* att = (NC_ATT_INFO_T*)ncindexith(v->att, j); |
1606 | 0 | if((retval = NC_reclaim_data_all(grp->nc4_info->controller,att->nc_typeid,att->data,att->len))) |
1607 | 0 | return retval; |
1608 | 0 | att->data = NULL; |
1609 | 0 | att->len = 0; |
1610 | 0 | att->dirty = 0; |
1611 | 0 | } |
1612 | 0 | } |
1613 | | |
1614 | 0 | return NC_NOERR; |
1615 | 0 | } |
1616 | | |
1617 | | /** |
1618 | | * @internal Remove a NC_ATT_INFO_T from an index. |
1619 | | * This will nc_free the memory too. |
1620 | | * |
1621 | | * @param list Pointer to pointer of list. |
1622 | | * @param att Pointer to attribute info struct. |
1623 | | * |
1624 | | * @return ::NC_NOERR No error. |
1625 | | * @author Dennis Heimbigner |
1626 | | */ |
1627 | | int |
1628 | | nc4_att_list_del(NCindex *list, NC_ATT_INFO_T *att) |
1629 | 0 | { |
1630 | 0 | assert(att && list); |
1631 | 0 | ncindexidel(list, (size_t)((NC_OBJ *)att)->id); |
1632 | 0 | return nc4_att_free(att); |
1633 | 0 | } |
1634 | | |
1635 | | /** |
1636 | | * @internal Free all resources and memory associated with a |
1637 | | * NC_FILE_INFO_T. This is the same as nc4_nc4f_list_del(), except it |
1638 | | * takes ncid. This function allows external dispatch layers, like |
1639 | | * PIO, to manipulate the file list without needing to know about |
1640 | | * internal netcdf structures. |
1641 | | * |
1642 | | * @param ncid The ncid of the file to release. |
1643 | | * |
1644 | | * @return ::NC_NOERR No error. |
1645 | | * @return ::NC_EBADID Bad ncid. |
1646 | | * @author Ed Hartnett |
1647 | | */ |
1648 | | int |
1649 | | nc4_file_list_del(int ncid) |
1650 | 0 | { |
1651 | 0 | NC_FILE_INFO_T *h5; |
1652 | 0 | int retval; |
1653 | | |
1654 | | /* Find our metadata for this file. */ |
1655 | 0 | if ((retval = nc4_find_grp_h5(ncid, NULL, &h5))) |
1656 | 0 | return retval; |
1657 | 0 | assert(h5); |
1658 | | |
1659 | | /* Delete the file resources. */ |
1660 | 0 | if ((retval = nc4_nc4f_list_del(h5))) |
1661 | 0 | return retval; |
1662 | | |
1663 | 0 | return NC_NOERR; |
1664 | 0 | } |
1665 | | |
1666 | | /** |
1667 | | * @internal Free all resources and memory associated with a |
1668 | | * NC_FILE_INFO_T. |
1669 | | * |
1670 | | * @param h5 Pointer to NC_FILE_INFO_T to be freed. |
1671 | | * |
1672 | | * @return ::NC_NOERR No error. |
1673 | | * @author Ed Hartnett |
1674 | | */ |
1675 | | int |
1676 | | nc4_nc4f_list_del(NC_FILE_INFO_T *h5) |
1677 | 0 | { |
1678 | 0 | int retval; |
1679 | |
|
1680 | 0 | assert(h5); |
1681 | | |
1682 | | /* Order is important here. We must delete the attribute contents |
1683 | | before deleting any metadata because nc_reclaim_data depends |
1684 | | on the existence of the type info. |
1685 | | */ |
1686 | | |
1687 | | /* Delete all the attribute data contents in each group and variable. */ |
1688 | 0 | if ((retval = nc4_rec_grp_del_att_data(h5->root_grp))) |
1689 | 0 | return retval; |
1690 | | |
1691 | | /* Delete all the list contents for vars, dims, and atts, in each |
1692 | | * group. */ |
1693 | 0 | if ((retval = nc4_rec_grp_del(h5->root_grp))) |
1694 | 0 | return retval; |
1695 | | |
1696 | | /* Cleanup these (extra) lists of all dims, groups, and types. */ |
1697 | 0 | nclistfree(h5->alldims); |
1698 | 0 | nclistfree(h5->allgroups); |
1699 | 0 | nclistfree(h5->alltypes); |
1700 | | |
1701 | | /* Free the NC_FILE_INFO_T struct. */ |
1702 | 0 | nullfree(h5->hdr.name); |
1703 | 0 | free(h5); |
1704 | |
|
1705 | 0 | return NC_NOERR; |
1706 | 0 | } |
1707 | | |
1708 | | /** |
1709 | | * @internal Normalize a UTF8 name. Put the result in norm_name, which |
1710 | | * can be NC_MAX_NAME + 1 in size. This function makes sure the free() |
1711 | | * gets called on the return from utf8proc_NFC, and also ensures that |
1712 | | * the name is not too long. |
1713 | | * |
1714 | | * @param name Name to normalize. |
1715 | | * @param norm_name The normalized name. |
1716 | | * |
1717 | | * @return ::NC_NOERR No error. |
1718 | | * @return ::NC_EMAXNAME Name too long. |
1719 | | * @author Dennis Heimbigner |
1720 | | */ |
1721 | | int |
1722 | | nc4_normalize_name(const char *name, char *norm_name) |
1723 | 0 | { |
1724 | 0 | char *temp_name; |
1725 | 0 | int stat = nc_utf8_normalize((const unsigned char *)name,(unsigned char **)&temp_name); |
1726 | 0 | if(stat != NC_NOERR) |
1727 | 0 | return stat; |
1728 | 0 | if (strlen(temp_name) > NC_MAX_NAME) |
1729 | 0 | { |
1730 | 0 | free(temp_name); |
1731 | 0 | return NC_EMAXNAME; |
1732 | 0 | } |
1733 | 0 | strcpy(norm_name, temp_name); |
1734 | 0 | free(temp_name); |
1735 | 0 | return NC_NOERR; |
1736 | 0 | } |
1737 | | |
1738 | | #ifdef NETCDF_ENABLE_SET_LOG_LEVEL |
1739 | | |
1740 | | /** |
1741 | | * Initialize parallel I/O logging. For parallel I/O builds, open log |
1742 | | * file, if not opened yet, or increment ref count if already open. |
1743 | | * |
1744 | | * @author Ed Hartnett |
1745 | | */ |
1746 | | int |
1747 | | nc4_init_logging(void) |
1748 | | { |
1749 | | int ret = NC_NOERR; |
1750 | | |
1751 | | #if LOGGING |
1752 | | #if NC_HAS_PARALLEL4 |
1753 | | if (!LOG_FILE && nc_log_level >= 0) |
1754 | | { |
1755 | | char log_filename[NC_MAX_NAME]; |
1756 | | int my_rank = 0; |
1757 | | int mpierr; |
1758 | | int mpi_initialized; |
1759 | | |
1760 | | /* If MPI has been initialized find the rank. */ |
1761 | | if ((mpierr = MPI_Initialized(&mpi_initialized))) |
1762 | | return NC_EMPI; |
1763 | | if (mpi_initialized) |
1764 | | { |
1765 | | if ((mpierr = MPI_Comm_rank(MPI_COMM_WORLD, &my_rank))) |
1766 | | return NC_EMPI; |
1767 | | } |
1768 | | |
1769 | | /* Create a filename with the rank in it. */ |
1770 | | snprintf(log_filename, sizeof(log_filename), "nc4_log_%d.log", my_rank); |
1771 | | |
1772 | | /* Open a file for this rank to log messages. */ |
1773 | | if (!(LOG_FILE = fopen(log_filename, "w"))) |
1774 | | return NC_EINTERNAL; |
1775 | | } |
1776 | | #endif /* NC_HAS_PARALLEL4 */ |
1777 | | #endif /* LOGGING */ |
1778 | | |
1779 | | return ret; |
1780 | | } |
1781 | | |
1782 | | /** |
1783 | | * Finalize logging - close parallel I/O log files, if open. This does |
1784 | | * nothing if logging is not enabled. |
1785 | | * |
1786 | | * @author Ed Hartnett |
1787 | | */ |
1788 | | void |
1789 | | nc4_finalize_logging(void) |
1790 | | { |
1791 | | #if LOGGING |
1792 | | #if NC_HAS_PARALLEL4 |
1793 | | if (LOG_FILE) |
1794 | | { |
1795 | | fclose(LOG_FILE); |
1796 | | LOG_FILE = NULL; |
1797 | | } |
1798 | | #endif /* NC_HAS_PARALLEL4 */ |
1799 | | #endif /* LOGGING */ |
1800 | | } |
1801 | | |
1802 | | /** |
1803 | | * Use this to set the global log level. |
1804 | | * |
1805 | | * Set it to NC_TURN_OFF_LOGGING (-1) to turn off all logging. Set it |
1806 | | * to 0 to show only errors, and to higher numbers to show more and |
1807 | | * more logging details. If logging is not enabled when building |
1808 | | * netCDF, this function will do nothing. |
1809 | | * |
1810 | | * @param new_level The new logging level. |
1811 | | * |
1812 | | * @return ::NC_NOERR No error. |
1813 | | * @author Ed Hartnett |
1814 | | */ |
1815 | | int |
1816 | | nc_set_log_level(int new_level) |
1817 | | { |
1818 | | #if LOGGING |
1819 | | /* Remember the new level. */ |
1820 | | nc_log_level = new_level; |
1821 | | |
1822 | | #if NC_HAS_PARALLEL4 |
1823 | | /* For parallel I/O builds, call the log init/finalize functions |
1824 | | * as needed, to open and close the log files. */ |
1825 | | if (new_level >= 0) |
1826 | | { |
1827 | | if (!LOG_FILE) |
1828 | | nc4_init_logging(); |
1829 | | } |
1830 | | else |
1831 | | nc4_finalize_logging(); |
1832 | | #endif /* NC_HAS_PARALLEL4 */ |
1833 | | |
1834 | | LOG((1, "log_level changed to %d", nc_log_level)); |
1835 | | #endif /* LOGGING */ |
1836 | | |
1837 | | return NC_NOERR; |
1838 | | } |
1839 | | #endif /* NETCDF_ENABLE_SET_LOG_LEVEL */ |
1840 | | |
1841 | | #if LOGGING |
1842 | | #define MAX_NESTS 10 |
1843 | | /** |
1844 | | * @internal Recursively print the metadata of a group. |
1845 | | * |
1846 | | * @param grp Pointer to group info struct. |
1847 | | * @param tab_count Number of tabs. |
1848 | | * |
1849 | | * @return ::NC_NOERR No error. |
1850 | | * @author Ed Hartnett, Dennis Heimbigner |
1851 | | */ |
1852 | | static int |
1853 | | rec_print_metadata(NC_GRP_INFO_T *grp, int tab_count) |
1854 | | { |
1855 | | NC_ATT_INFO_T *att; |
1856 | | NC_VAR_INFO_T *var; |
1857 | | NC_DIM_INFO_T *dim; |
1858 | | NC_TYPE_INFO_T *type; |
1859 | | NC_FIELD_INFO_T *field; |
1860 | | char tabs[MAX_NESTS+1] = ""; |
1861 | | char temp_string[10]; |
1862 | | int t, retval, d, i; |
1863 | | |
1864 | | /* Come up with a number of tabs relative to the group. */ |
1865 | | for (t = 0; t < tab_count && t < MAX_NESTS; t++) |
1866 | | tabs[t] = '\t'; |
1867 | | tabs[t] = '\0'; |
1868 | | |
1869 | | LOG((2, "%s GROUP - %s nc_grpid: %d nvars: %d natts: %d", |
1870 | | tabs, grp->hdr.name, grp->hdr.id, ncindexsize(grp->vars), ncindexsize(grp->att))); |
1871 | | |
1872 | | for (i = 0; i < ncindexsize(grp->att); i++) |
1873 | | { |
1874 | | att = (NC_ATT_INFO_T *)ncindexith(grp->att, i); |
1875 | | assert(att); |
1876 | | LOG((2, "%s GROUP ATTRIBUTE - attnum: %d name: %s type: %d len: %d", |
1877 | | tabs, att->hdr.id, att->hdr.name, att->nc_typeid, att->len)); |
1878 | | } |
1879 | | |
1880 | | for (i = 0; i < ncindexsize(grp->dim); i++) |
1881 | | { |
1882 | | dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, i); |
1883 | | assert(dim); |
1884 | | LOG((2, "%s DIMENSION - dimid: %d name: %s len: %d unlimited: %d", |
1885 | | tabs, dim->hdr.id, dim->hdr.name, dim->len, dim->unlimited)); |
1886 | | } |
1887 | | |
1888 | | for (i = 0; i < ncindexsize(grp->vars); i++) |
1889 | | { |
1890 | | int j; |
1891 | | char storage_str[NC_MAX_NAME] = ""; |
1892 | | char *dims_string = NULL; |
1893 | | |
1894 | | var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i); |
1895 | | assert(var); |
1896 | | if (var->ndims > 0) |
1897 | | { |
1898 | | if (!(dims_string = malloc(sizeof(char) * var->ndims * 4))) |
1899 | | return NC_ENOMEM; |
1900 | | strcpy(dims_string, ""); |
1901 | | for (d = 0; d < var->ndims; d++) |
1902 | | { |
1903 | | snprintf(temp_string, sizeof(temp_string), " %d", var->dimids[d]); |
1904 | | strcat(dims_string, temp_string); |
1905 | | } |
1906 | | } |
1907 | | if (!var->meta_read) |
1908 | | strcat(storage_str, "unknown"); |
1909 | | else if (var->storage == NC_CONTIGUOUS) |
1910 | | strcat(storage_str, "contiguous"); |
1911 | | else if (var->storage == NC_COMPACT) |
1912 | | strcat(storage_str, "compact"); |
1913 | | else if (var->storage == NC_CHUNKED) |
1914 | | strcat(storage_str, "chunked"); |
1915 | | else if (var->storage == NC_VIRTUAL) |
1916 | | strcat(storage_str, "virtual"); |
1917 | | else |
1918 | | strcat(storage_str, "unknown"); |
1919 | | LOG((2, "%s VARIABLE - varid: %d name: %s ndims: %d " |
1920 | | "dimids:%s storage: %s", tabs, var->hdr.id, var->hdr.name, |
1921 | | var->ndims, |
1922 | | (dims_string ? dims_string : " -"), storage_str)); |
1923 | | for (j = 0; j < ncindexsize(var->att); j++) |
1924 | | { |
1925 | | att = (NC_ATT_INFO_T *)ncindexith(var->att, j); |
1926 | | assert(att); |
1927 | | LOG((2, "%s VAR ATTRIBUTE - attnum: %d name: %s type: %d len: %d", |
1928 | | tabs, att->hdr.id, att->hdr.name, att->nc_typeid, att->len)); |
1929 | | } |
1930 | | if (dims_string) |
1931 | | free(dims_string); |
1932 | | } |
1933 | | |
1934 | | for (i = 0; i < ncindexsize(grp->type); i++) |
1935 | | { |
1936 | | type = (NC_TYPE_INFO_T*)ncindexith(grp->type, i); |
1937 | | assert(type); |
1938 | | LOG((2, "%s TYPE - nc_typeid: %d size: %d committed: %d name: %s", |
1939 | | tabs, type->hdr.id, type->size, (int)type->committed, type->hdr.name)); |
1940 | | /* Is this a compound type? */ |
1941 | | if (type->nc_type_class == NC_COMPOUND) |
1942 | | { |
1943 | | int j; |
1944 | | LOG((3, "compound type")); |
1945 | | for (j = 0; j < nclistlength(type->u.c.field); j++) |
1946 | | { |
1947 | | field = (NC_FIELD_INFO_T *)nclistget(type->u.c.field, j); |
1948 | | LOG((4, "field %s offset %d nctype %d ndims %d", field->hdr.name, |
1949 | | field->offset, field->nc_typeid, field->ndims)); |
1950 | | } |
1951 | | } |
1952 | | else if (type->nc_type_class == NC_VLEN) |
1953 | | { |
1954 | | LOG((3, "VLEN type")); |
1955 | | LOG((4, "base_nc_type: %d", type->u.v.base_nc_typeid)); |
1956 | | } |
1957 | | else if (type->nc_type_class == NC_OPAQUE) |
1958 | | LOG((3, "Opaque type")); |
1959 | | else if (type->nc_type_class == NC_ENUM) |
1960 | | { |
1961 | | LOG((3, "Enum type")); |
1962 | | LOG((4, "base_nc_type: %d", type->u.e.base_nc_typeid)); |
1963 | | } |
1964 | | else |
1965 | | { |
1966 | | LOG((0, "Unknown class: %d", type->nc_type_class)); |
1967 | | return NC_EBADTYPE; |
1968 | | } |
1969 | | } |
1970 | | |
1971 | | /* Call self for each child of this group. */ |
1972 | | for (i = 0; i < ncindexsize(grp->children); i++) |
1973 | | if ((retval = rec_print_metadata((NC_GRP_INFO_T *)ncindexith(grp->children, i), |
1974 | | tab_count + 1))) |
1975 | | return retval; |
1976 | | |
1977 | | return NC_NOERR; |
1978 | | } |
1979 | | |
1980 | | /** |
1981 | | * @internal Print out the internal metadata for a file. This is |
1982 | | * useful to check that netCDF is working! Nonetheless, this function |
1983 | | * will print nothing if logging is not set to at least two. |
1984 | | * |
1985 | | * @param Pointer to the file info struct. |
1986 | | * |
1987 | | * @return ::NC_NOERR No error. |
1988 | | * @author Ed Hartnett |
1989 | | */ |
1990 | | int |
1991 | | log_metadata_nc(NC_FILE_INFO_T *h5) |
1992 | | { |
1993 | | LOG((2, "*** NetCDF-4 Internal Metadata: int_ncid 0x%x ext_ncid 0x%x", |
1994 | | h5->root_grp->nc4_info->controller->int_ncid, |
1995 | | h5->root_grp->nc4_info->controller->ext_ncid)); |
1996 | | if (!h5) |
1997 | | { |
1998 | | LOG((2, "This is a netCDF-3 file.")); |
1999 | | return NC_NOERR; |
2000 | | } |
2001 | | LOG((2, "FILE - path: %s cmode: 0x%x parallel: %d redef: %d " |
2002 | | "fill_mode: %d no_write: %d next_nc_grpid: %d", h5->root_grp->nc4_info->controller->path, |
2003 | | h5->cmode, (int)h5->parallel, (int)h5->redef, h5->fill_mode, (int)h5->no_write, |
2004 | | h5->next_nc_grpid)); |
2005 | | if(nc_log_level >= 2) |
2006 | | return rec_print_metadata(h5->root_grp, 0); |
2007 | | return NC_NOERR; |
2008 | | } |
2009 | | |
2010 | | #endif /*LOGGING */ |
2011 | | |
2012 | | /** |
2013 | | * @internal Show the in-memory metadata for a netcdf file. This |
2014 | | * function does nothing unless netCDF was built with |
2015 | | * the configure option --enable-logging. |
2016 | | * |
2017 | | * @param ncid File and group ID. |
2018 | | * |
2019 | | * @return ::NC_NOERR No error. |
2020 | | * @return ::NC_EBADID Bad ncid. |
2021 | | * @author Ed Hartnett |
2022 | | */ |
2023 | | int |
2024 | | NC4_show_metadata(int ncid) |
2025 | 0 | { |
2026 | 0 | int retval = NC_NOERR; |
2027 | | #if LOGGING |
2028 | | NC_FILE_INFO_T *h5; |
2029 | | int old_log_level = nc_log_level; |
2030 | | |
2031 | | /* Find file metadata. */ |
2032 | | if ((retval = nc4_find_grp_h5(ncid, NULL, &h5))) |
2033 | | return retval; |
2034 | | |
2035 | | /* Log level must be 2 to see metadata. */ |
2036 | | nc_log_level = 2; |
2037 | | retval = log_metadata_nc(h5); |
2038 | | nc_log_level = old_log_level; |
2039 | | #endif /*LOGGING*/ |
2040 | 0 | return retval; |
2041 | 0 | } |
2042 | | |
2043 | | /** |
2044 | | * @internal Define a binary searcher for reserved attributes |
2045 | | * @param name for which to search |
2046 | | * @return pointer to the matching NC_reservedatt structure. |
2047 | | * @return NULL if not found. |
2048 | | * @author Dennis Heimbigner |
2049 | | */ |
2050 | | const NC_reservedatt* |
2051 | | NC_findreserved(const char* name) |
2052 | 0 | { |
2053 | | #if 0 |
2054 | | int n = NRESERVED; |
2055 | | int L = 0; |
2056 | | int R = (n - 1); |
2057 | | |
2058 | | for(;;) { |
2059 | | if(L > R) break; |
2060 | | int m = (L + R) / 2; |
2061 | | const NC_reservedatt* p = &NC_reserved[m]; |
2062 | | int cmp = strcmp(p->name,name); |
2063 | | if(cmp == 0) return p; |
2064 | | if(cmp < 0) |
2065 | | L = (m + 1); |
2066 | | else /*cmp > 0*/ |
2067 | | R = (m - 1); |
2068 | | } |
2069 | | return NULL; |
2070 | | #else |
2071 | 0 | return (const NC_reservedatt*)bsearch(name,NC_reserved,NRESERVED,sizeof(NC_reservedatt),bincmp); |
2072 | 0 | #endif |
2073 | 0 | } |
2074 | | |
2075 | | /* Ed Hartness requires this function */ |
2076 | | static int |
2077 | | NC4_move_in_NCList(NC* nc, int new_id) |
2078 | 0 | { |
2079 | 0 | int stat = move_in_NCList(nc,new_id); |
2080 | 0 | if(stat == NC_NOERR) { |
2081 | | /* Synchronize header */ |
2082 | 0 | if(nc->dispatchdata) |
2083 | 0 | ((NC_OBJ*)nc->dispatchdata)->id = nc->ext_ncid; |
2084 | 0 | } |
2085 | 0 | return stat; |
2086 | 0 | } |
2087 | | |
2088 | | static int |
2089 | | sortcmp(const void* arg1, const void* arg2) |
2090 | 35 | { |
2091 | 35 | NC_reservedatt* r1 = (NC_reservedatt*)arg1; |
2092 | 35 | NC_reservedatt* r2 = (NC_reservedatt*)arg2; |
2093 | 35 | return strcmp(r1->name,r2->name); |
2094 | 35 | } |
2095 | | |
2096 | | static int |
2097 | | bincmp(const void* arg1, const void* arg2) |
2098 | 0 | { |
2099 | 0 | const char* name = (const char*)arg1; |
2100 | 0 | NC_reservedatt* ra = (NC_reservedatt*)arg2; |
2101 | 0 | return strcmp(name,ra->name); |
2102 | 0 | } |
2103 | | |
2104 | | void |
2105 | | NC_initialize_reserved(void) |
2106 | 1 | { |
2107 | | /* Guarantee the reserved attribute list is sorted */ |
2108 | 1 | qsort((void*)NC_reserved,NRESERVED,sizeof(NC_reservedatt),sortcmp); |
2109 | 1 | } |