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