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