/src/netcdf-c/libnczarr/zvar.c
Line | Count | Source |
1 | | /* Copyright 2003-2019, University Corporation for Atmospheric |
2 | | * Research. See COPYRIGHT file for copying and redistribution |
3 | | * conditions.*/ |
4 | | |
5 | | /** |
6 | | * @file |
7 | | * @internal This file handles the ZARR variable functions. |
8 | | * |
9 | | * @author Dennis Heimbigner, Ed Hartnett |
10 | | */ |
11 | | |
12 | | #include "zincludes.h" |
13 | | #include <math.h> /* For pow() used below. */ |
14 | | |
15 | | /* Mnemonics */ |
16 | | #define CREATE 0 |
17 | | #define NOCREATE 1 |
18 | | |
19 | | |
20 | | #ifdef LOGGING |
21 | | static void |
22 | | reportchunking(const char* title, NC_VAR_INFO_T* var) |
23 | | { |
24 | | int i; |
25 | | char buf[8192]; |
26 | | |
27 | | buf[0] = '\0'; /* for strlcat */ |
28 | | strlcat(buf,title,sizeof(buf)); |
29 | | strlcat(buf,"chunksizes for var ",sizeof(buf)); |
30 | | strlcat(buf,var->hdr.name,sizeof(buf)); |
31 | | strlcat(buf,"sizes=",sizeof(buf)); |
32 | | for(i=0;i<var->ndims;i++) { |
33 | | char digits[64]; |
34 | | if(i > 0) strlcat(buf,",",sizeof(buf)); |
35 | | snprintf(digits,sizeof(digits),"%ld",(unsigned long)var->chunksizes[i]); |
36 | | strlcat(buf,digits,sizeof(buf)); |
37 | | } |
38 | | LOG((3,"%s",buf)); |
39 | | } |
40 | | #endif |
41 | | |
42 | | /* Mnemonic */ |
43 | 0 | #define READING 1 |
44 | 0 | #define WRITING 0 |
45 | | |
46 | | /** @internal Default size for unlimited dim chunksize. */ |
47 | 0 | #define DEFAULT_1D_UNLIM_SIZE (4096) |
48 | | |
49 | | /** Number of bytes in 64 KB. */ |
50 | | #define SIXTY_FOUR_KB (65536) |
51 | | |
52 | | /** @internal Temp name used when renaming vars to preserve varid |
53 | | * order. */ |
54 | | #define NC_TEMP_NAME "_netcdf4_temporary_variable_name_for_rename" |
55 | | |
56 | | /** |
57 | | * @internal Check a set of chunksizes to see if they specify a chunk |
58 | | * that is too big. |
59 | | * |
60 | | * @param grp Pointer to the group info. |
61 | | * @param var Pointer to the var info. |
62 | | * @param chunksizes Array of chunksizes to check. |
63 | | * |
64 | | * @returns ::NC_NOERR No error. |
65 | | * @returns ::NC_EBADID Bad ncid. |
66 | | * @returns ::NC_ENOTVAR Invalid variable ID. |
67 | | * @returns ::NC_EBADCHUNK Bad chunksize. |
68 | | */ |
69 | | static int |
70 | | check_chunksizes(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, const size_t *chunksizes) |
71 | 0 | { |
72 | 0 | double dprod; |
73 | 0 | size_t type_len; |
74 | 0 | int d; |
75 | 0 | int retval = NC_NOERR; |
76 | |
|
77 | 0 | if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->hdr.id, &type_len))) |
78 | 0 | goto done; |
79 | 0 | if (var->type_info->nc_type_class == NC_VLEN) |
80 | 0 | dprod = (double)sizeof(nc_hvl_t); |
81 | 0 | else |
82 | 0 | dprod = (double)type_len; |
83 | 0 | for (d = 0; d < var->ndims; d++) |
84 | 0 | dprod *= (double)chunksizes[d]; |
85 | |
|
86 | 0 | if (dprod > (double) NC_MAX_UINT) |
87 | 0 | {retval = NC_EBADCHUNK; goto done;} |
88 | 0 | done: |
89 | 0 | return retval; |
90 | 0 | } |
91 | | |
92 | | /** |
93 | | * @internal Determine some default chunksizes for a variable. |
94 | | * |
95 | | * @param grp Pointer to the group info. |
96 | | * @param var Pointer to the var info. |
97 | | * |
98 | | * @returns ::NC_NOERR for success |
99 | | * @returns ::NC_EBADID Bad ncid. |
100 | | * @returns ::NC_ENOTVAR Invalid variable ID. |
101 | | * @author Dennis Heimbigner, Ed Hartnett |
102 | | */ |
103 | | int |
104 | | ncz_find_default_chunksizes2(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var) |
105 | 0 | { |
106 | 0 | int d; |
107 | 0 | size_t type_size; |
108 | 0 | float num_values = 1, num_unlim = 0; |
109 | 0 | int retval; |
110 | 0 | size_t suggested_size; |
111 | | #ifdef LOGGING |
112 | | double total_chunk_size; |
113 | | #endif |
114 | |
|
115 | 0 | type_size = var->type_info->size; |
116 | |
|
117 | | #ifdef LOGGING |
118 | | /* Later this will become the total number of bytes in the default |
119 | | * chunk. */ |
120 | | total_chunk_size = (double) type_size; |
121 | | #endif |
122 | |
|
123 | 0 | if(var->chunksizes == NULL) { |
124 | 0 | if((var->chunksizes = calloc(1,sizeof(size_t)*var->ndims)) == NULL) |
125 | 0 | return NC_ENOMEM; |
126 | 0 | } |
127 | | |
128 | | /* How many values in the variable (or one record, if there are |
129 | | * unlimited dimensions). */ |
130 | 0 | for (d = 0; d < var->ndims; d++) |
131 | 0 | { |
132 | 0 | assert(var->dim[d]); |
133 | 0 | if (! var->dim[d]->unlimited) |
134 | 0 | num_values *= (float)var->dim[d]->len; |
135 | 0 | else { |
136 | 0 | num_unlim++; |
137 | 0 | var->chunksizes[d] = 1; /* overwritten below, if all dims are unlimited */ |
138 | 0 | } |
139 | 0 | } |
140 | | /* Special case to avoid 1D vars with unlim dim taking huge amount |
141 | | of space (DEFAULT_CHUNK_SIZE bytes). Instead we limit to about |
142 | | 4KB */ |
143 | 0 | if (var->ndims == 1 && num_unlim == 1) { |
144 | 0 | if (DEFAULT_CHUNK_SIZE / type_size <= 0) |
145 | 0 | suggested_size = 1; |
146 | 0 | else if (DEFAULT_CHUNK_SIZE / type_size > DEFAULT_1D_UNLIM_SIZE) |
147 | 0 | suggested_size = DEFAULT_1D_UNLIM_SIZE; |
148 | 0 | else |
149 | 0 | suggested_size = DEFAULT_CHUNK_SIZE / type_size; |
150 | 0 | var->chunksizes[0] = suggested_size / type_size; |
151 | 0 | LOG((4, "%s: name %s dim %d DEFAULT_CHUNK_SIZE %d num_values %f type_size %d " |
152 | 0 | "chunksize %ld", __func__, var->hdr.name, d, DEFAULT_CHUNK_SIZE, num_values, type_size, var->chunksizes[0])); |
153 | 0 | } |
154 | 0 | if (var->ndims > 1 && var->ndims == num_unlim) { /* all dims unlimited */ |
155 | 0 | suggested_size = pow((double)DEFAULT_CHUNK_SIZE/type_size, 1.0/(double)(var->ndims)); |
156 | 0 | for (d = 0; d < var->ndims; d++) |
157 | 0 | { |
158 | 0 | var->chunksizes[d] = suggested_size ? suggested_size : 1; |
159 | 0 | LOG((4, "%s: name %s dim %d DEFAULT_CHUNK_SIZE %d num_values %f type_size %d " |
160 | 0 | "chunksize %ld", __func__, var->hdr.name, d, DEFAULT_CHUNK_SIZE, num_values, type_size, var->chunksizes[d])); |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | | /* Pick a chunk length for each dimension, if one has not already |
165 | | * been picked above. */ |
166 | 0 | for (d = 0; d < var->ndims; d++) |
167 | 0 | if (!var->chunksizes[d]) |
168 | 0 | { |
169 | 0 | suggested_size = (pow((double)DEFAULT_CHUNK_SIZE/(num_values * type_size), |
170 | 0 | 1.0/(double)(var->ndims - num_unlim)) * var->dim[d]->len - .5); |
171 | 0 | if (suggested_size > var->dim[d]->len) |
172 | 0 | suggested_size = var->dim[d]->len; |
173 | 0 | var->chunksizes[d] = suggested_size ? suggested_size : 1; |
174 | 0 | LOG((4, "%s: name %s dim %d DEFAULT_CHUNK_SIZE %d num_values %f type_size %d " |
175 | 0 | "chunksize %ld", __func__, var->hdr.name, d, DEFAULT_CHUNK_SIZE, num_values, type_size, var->chunksizes[d])); |
176 | 0 | } |
177 | |
|
178 | | #ifdef LOGGING |
179 | | /* Find total chunk size. */ |
180 | | for (d = 0; d < var->ndims; d++) |
181 | | total_chunk_size *= (double) var->chunksizes[d]; |
182 | | LOG((4, "total_chunk_size %f", total_chunk_size)); |
183 | | #endif |
184 | | |
185 | | /* But did this result in a chunk that is too big? */ |
186 | 0 | retval = check_chunksizes(grp, var, var->chunksizes); |
187 | 0 | if (retval) |
188 | 0 | { |
189 | | /* Other error? */ |
190 | 0 | if (retval != NC_EBADCHUNK) |
191 | 0 | return THROW(retval); |
192 | | |
193 | | /* Chunk is too big! Reduce each dimension by half and try again. */ |
194 | 0 | for ( ; retval == NC_EBADCHUNK; retval = check_chunksizes(grp, var, var->chunksizes)) |
195 | 0 | for (d = 0; d < var->ndims; d++) |
196 | 0 | var->chunksizes[d] = var->chunksizes[d]/2 ? var->chunksizes[d]/2 : 1; |
197 | 0 | } |
198 | | |
199 | | /* Do we have any big data overhangs? They can be dangerous to |
200 | | * babies, the elderly, or confused campers who have had too much |
201 | | * beer. */ |
202 | 0 | for (d = 0; d < var->ndims; d++) |
203 | 0 | { |
204 | 0 | size_t num_chunks; |
205 | 0 | size_t overhang; |
206 | 0 | assert(var->chunksizes[d] > 0); |
207 | 0 | num_chunks = (var->dim[d]->len + var->chunksizes[d] - 1) / var->chunksizes[d]; |
208 | 0 | if(num_chunks > 0) { |
209 | 0 | overhang = (num_chunks * var->chunksizes[d]) - var->dim[d]->len; |
210 | 0 | var->chunksizes[d] -= overhang / num_chunks; |
211 | 0 | } |
212 | 0 | } |
213 | |
|
214 | | #ifdef LOGGING |
215 | | reportchunking("find_default: ",var); |
216 | | #endif |
217 | 0 | return NC_NOERR; |
218 | 0 | } |
219 | | |
220 | | #if 0 |
221 | | /** |
222 | | * @internal Give a var a secret ZARR name. This is needed when a var |
223 | | * is defined with the same name as a dim, but it is not a coord var |
224 | | * of that dim. In that case, the var uses a secret name inside the |
225 | | * ZARR file. |
226 | | * |
227 | | * @param var Pointer to var info. |
228 | | * @param name Name to use for base of secret name. |
229 | | * |
230 | | * @returns ::NC_NOERR No error. |
231 | | * @returns ::NC_EMAXNAME Name too long to fit secret prefix. |
232 | | * @returns ::NC_ENOMEM Out of memory. |
233 | | * @author Dennis Heimbigner, Ed Hartnett |
234 | | */ |
235 | | static int |
236 | | give_var_secret_name(NC_VAR_INFO_T *var, const char *name) |
237 | | { |
238 | | /* Set a different ncz name for this variable to avoid name |
239 | | * clash. */ |
240 | | if (strlen(name) + strlen(NON_COORD_PREPEND) > NC_MAX_NAME) |
241 | | return NC_EMAXNAME; |
242 | | size_t ncz_name_size = (strlen(NON_COORD_PREPEND) + strlen(name) + 1) * |
243 | | sizeof(char); |
244 | | if (!(var->ncz_name = malloc(ncz_name_size))) |
245 | | return NC_ENOMEM; |
246 | | |
247 | | snprintf(var->ncz_name, ncz_name_size, "%s%s", NON_COORD_PREPEND, name); |
248 | | |
249 | | return NC_NOERR; |
250 | | } |
251 | | #endif /*0*/ |
252 | | |
253 | | /** |
254 | | * @internal This is called when a new netCDF-4 variable is defined |
255 | | * with nc_def_var(). |
256 | | * |
257 | | * @param ncid File ID. |
258 | | * @param name Name. |
259 | | * @param xtype Type. |
260 | | * @param ndims Number of dims. ZARR has maximum of 32. |
261 | | * @param dimidsp Array of dim IDs. |
262 | | * @param varidp Gets the var ID. |
263 | | * |
264 | | * @returns ::NC_NOERR No error. |
265 | | * @returns ::NC_EBADID Bad ncid. |
266 | | * @returns ::NC_ENOTVAR Invalid variable ID. |
267 | | * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is |
268 | | * not netCDF-4/NCZ. |
269 | | * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3 |
270 | | * netcdf-4 file. |
271 | | * @returns ::NC_ELATEDEF Too late to change settings for this variable. |
272 | | * @returns ::NC_ENOTINDEFINE Not in define mode. |
273 | | * @returns ::NC_EPERM File is read only. |
274 | | * @returns ::NC_EMAXDIMS Classic model file exceeds ::NC_MAX_VAR_DIMS. |
275 | | * @returns ::NC_ESTRICTNC3 Attempting to create netCDF-4 type var in |
276 | | * classic model file |
277 | | * @returns ::NC_EBADNAME Bad name. |
278 | | * @returns ::NC_EBADTYPE Bad type. |
279 | | * @returns ::NC_ENOMEM Out of memory. |
280 | | * @returns ::NC_EHDFERR Error returned by ZARR layer. |
281 | | * @returns ::NC_EINVAL Invalid input |
282 | | * @author Dennis Heimbigner, Ed Hartnett |
283 | | */ |
284 | | int |
285 | | NCZ_def_var(int ncid, const char *name, nc_type xtype, int ndims, |
286 | | const int *dimidsp, int *varidp) |
287 | 0 | { |
288 | 0 | NC_GRP_INFO_T *grp; |
289 | 0 | NC_VAR_INFO_T *var; |
290 | 0 | NC_DIM_INFO_T *dim; |
291 | 0 | NC_FILE_INFO_T *h5; |
292 | 0 | NC_TYPE_INFO_T *type = NULL; |
293 | 0 | NCZ_VAR_INFO_T* zvar = NULL; |
294 | 0 | char norm_name[NC_MAX_NAME + 1]; |
295 | 0 | int d; |
296 | 0 | int retval; |
297 | 0 | NCglobalstate* gstate = NC_getglobalstate(); |
298 | |
|
299 | 0 | ZTRACE(1,"ncid=%d name=%s xtype=%d ndims=%d dimids=%s",ncid,name,xtype,ndims,nczprint_idvector(ndims,dimidsp)); |
300 | | |
301 | | /* Find info for this file and group, and set pointer to each. */ |
302 | 0 | if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) |
303 | 0 | BAIL(retval); |
304 | 0 | assert(grp && grp->format_grp_info && h5); |
305 | |
|
306 | | #ifdef LOOK |
307 | | /* HDF5 allows maximum of 32 dimensions. */ |
308 | | if (ndims > H5S_MAX_RANK) |
309 | | BAIL(NC_EMAXDIMS); |
310 | | #endif |
311 | | |
312 | | /* If it's not in define mode, strict nc3 files error out, |
313 | | * otherwise switch to define mode. This will also check that the |
314 | | * file is writable. */ |
315 | 0 | if (!(h5->flags & NC_INDEF)) |
316 | 0 | { |
317 | 0 | if (h5->cmode & NC_CLASSIC_MODEL) |
318 | 0 | BAIL(NC_ENOTINDEFINE); |
319 | 0 | if ((retval = NCZ_redef(ncid))) |
320 | 0 | BAIL(retval); |
321 | 0 | } |
322 | 0 | assert(!h5->no_write); |
323 | | |
324 | | /* Check and normalize the name. */ |
325 | 0 | if ((retval = nc4_check_name(name, norm_name))) |
326 | 0 | BAIL(retval); |
327 | | |
328 | | /* Not a Type is, well, not a type.*/ |
329 | 0 | if (xtype == NC_NAT) |
330 | 0 | BAIL(NC_EBADTYPE); |
331 | | |
332 | | /* For classic files, only classic types are allowed. */ |
333 | 0 | if (h5->cmode & NC_CLASSIC_MODEL && xtype > NC_DOUBLE) |
334 | 0 | BAIL(NC_ESTRICTNC3); |
335 | | |
336 | | /* For classic files limit number of dims. */ |
337 | 0 | if (h5->cmode & NC_CLASSIC_MODEL && ndims > NC_MAX_VAR_DIMS) |
338 | 0 | BAIL(NC_EMAXDIMS); |
339 | | |
340 | | /* cast needed for braindead systems with signed size_t */ |
341 | 0 | if ((unsigned long) ndims > X_INT_MAX) /* Backward compat */ |
342 | 0 | BAIL(NC_EINVAL); |
343 | | |
344 | | /* Check that this name is not in use as a var, grp, or type. */ |
345 | 0 | if ((retval = nc4_check_dup_name(grp, norm_name))) |
346 | 0 | BAIL(retval); |
347 | | |
348 | | /* For non-scalar vars, dim IDs must be provided. */ |
349 | 0 | if (ndims && !dimidsp) |
350 | 0 | BAIL(NC_EINVAL); |
351 | | |
352 | | /* Check all the dimids to make sure they exist. */ |
353 | 0 | for (d = 0; d < ndims; d++) |
354 | 0 | if ((retval = nc4_find_dim(grp, dimidsp[d], &dim, NULL))) |
355 | 0 | BAIL(retval); |
356 | | |
357 | | /* These degrubbing messages sure are handy! */ |
358 | 0 | LOG((2, "%s: name %s type %d ndims %d", __func__, norm_name, xtype, ndims)); |
359 | | #ifdef LOGGING |
360 | | { |
361 | | int dd; |
362 | | for (dd = 0; dd < ndims; dd++) |
363 | | LOG((4, "dimid[%d] %d", dd, dimidsp[dd])); |
364 | | } |
365 | | #endif |
366 | | |
367 | | /* If this is a user-defined type, there is a type struct with |
368 | | * all the type information. For atomic types, fake up a type |
369 | | * struct. */ |
370 | 0 | if((retval = ncz_gettype(h5,grp,xtype,&type))) |
371 | 0 | BAIL(retval); |
372 | | |
373 | | /* Create a new var and fill in some cache setting values. */ |
374 | 0 | if ((retval = nc4_var_list_add(grp, norm_name, ndims, &var))) |
375 | 0 | BAIL(retval); |
376 | | |
377 | | /* Add storage for NCZ-specific var info. */ |
378 | 0 | if (!(var->format_var_info = calloc(1, sizeof(NCZ_VAR_INFO_T)))) |
379 | 0 | BAIL(NC_ENOMEM); |
380 | 0 | zvar = var->format_var_info; |
381 | 0 | zvar->common.file = h5; |
382 | 0 | zvar->scalar = (ndims == 0 ? 1 : 0); |
383 | |
|
384 | 0 | zvar->dimension_separator = gstate->zarr.dimension_separator; |
385 | 0 | assert(zvar->dimension_separator != 0); |
386 | | |
387 | | /* Set these state flags for the var. */ |
388 | 0 | var->is_new_var = NC_TRUE; |
389 | 0 | var->meta_read = NC_TRUE; |
390 | 0 | var->atts_read = NC_TRUE; |
391 | |
|
392 | | #ifdef NETCDF_ENABLE_NCZARR_FILTERS |
393 | | /* Set the filter list */ |
394 | | assert(var->filters == NULL); |
395 | | var->filters = (void*)nclistnew(); |
396 | | #endif |
397 | | |
398 | | /* Point to the type, and increment its ref. count */ |
399 | 0 | var->type_info = type; |
400 | | #ifdef LOOK |
401 | | var->type_info->rc++; |
402 | | #endif |
403 | 0 | type = NULL; |
404 | | |
405 | | /* Propagate the endianness */ |
406 | 0 | var->endianness = var->type_info->endianness; |
407 | | |
408 | | /* Set variables no_fill to match the database default unless the |
409 | | * variable type is variable length (NC_STRING or NC_VLEN) or is |
410 | | * user-defined type. */ |
411 | 0 | if (var->type_info->nc_type_class <= NC_STRING) |
412 | 0 | var->no_fill = (h5->fill_mode == NC_NOFILL); |
413 | | |
414 | | /* Assign dimensions to the variable. At the same time, check to |
415 | | * see if this is a coordinate variable. If so, it will have the |
416 | | * same name as one of its dimensions. If it is a coordinate var, |
417 | | * is it a coordinate var in the same group as the dim? Also, check |
418 | | * whether we should use contiguous or chunked storage. */ |
419 | 0 | var->storage = NC_CHUNKED; |
420 | 0 | for (d = 0; d < ndims; d++) |
421 | 0 | { |
422 | 0 | NC_GRP_INFO_T *dim_grp; |
423 | | /* Look up each dimension */ |
424 | 0 | if ((retval = nc4_find_dim(grp, dimidsp[d], &dim, &dim_grp))) |
425 | 0 | BAIL(retval); |
426 | 0 | assert(dim && dim->format_dim_info); |
427 | | /* Check for unlimited dimension and turn off contiguous storage. */ |
428 | 0 | if (dim->unlimited) |
429 | 0 | var->storage = NC_CHUNKED; |
430 | | /* Track dimensions for variable */ |
431 | 0 | var->dimids[d] = dimidsp[d]; |
432 | 0 | var->dim[d] = dim; |
433 | 0 | } |
434 | | |
435 | | /* Determine default chunksizes for this variable. (Even for |
436 | | * variables which may be contiguous.) */ |
437 | 0 | LOG((4, "allocating array of %d size_t to hold chunksizes for var %s", |
438 | 0 | var->ndims, var->hdr.name)); |
439 | 0 | if(!var->chunksizes) { |
440 | 0 | if(var->ndims) { |
441 | 0 | if (!(var->chunksizes = calloc(var->ndims, sizeof(size_t)))) |
442 | 0 | BAIL(NC_ENOMEM); |
443 | 0 | if ((retval = ncz_find_default_chunksizes2(grp, var))) |
444 | 0 | BAIL(retval); |
445 | 0 | } else { |
446 | | /* Pretend that scalars are like var[1] */ |
447 | 0 | if (!(var->chunksizes = calloc(1, sizeof(size_t)))) |
448 | 0 | BAIL(NC_ENOMEM); |
449 | 0 | var->chunksizes[0] = 1; |
450 | 0 | } |
451 | 0 | } |
452 | | |
453 | | /* Compute the chunksize cross product */ |
454 | 0 | zvar->chunkproduct = 1; |
455 | 0 | if(!zvar->scalar) |
456 | 0 | {for(d=0;d<var->ndims;d++) {zvar->chunkproduct *= var->chunksizes[d];}} |
457 | 0 | zvar->chunksize = zvar->chunkproduct * var->type_info->size; |
458 | | |
459 | | /* Set cache defaults */ |
460 | 0 | var->chunkcache = gstate->chunkcache; |
461 | | |
462 | | /* Create the cache */ |
463 | 0 | if((retval=NCZ_create_chunk_cache(var,zvar->chunkproduct*var->type_info->size,zvar->dimension_separator,&zvar->cache))) |
464 | 0 | BAIL(retval); |
465 | | |
466 | | /* Set the per-variable chunkcache defaults */ |
467 | 0 | zvar->cache->params = var->chunkcache; |
468 | | |
469 | | /* Return the varid. */ |
470 | 0 | if (varidp) |
471 | 0 | *varidp = var->hdr.id; |
472 | 0 | LOG((4, "new varid %d", var->hdr.id)); |
473 | |
|
474 | 0 | exit: |
475 | 0 | if (type) |
476 | 0 | if ((retval = nc4_type_free(type))) |
477 | 0 | BAILLOG(retval); |
478 | 0 | return ZUNTRACE(retval); |
479 | 0 | } |
480 | | |
481 | | /** |
482 | | * @internal This functions sets extra stuff about a netCDF-4 variable which |
483 | | * must be set before the enddef but after the def_var. |
484 | | * |
485 | | * @note All pointer parameters may be NULL, in which case they are ignored. |
486 | | * @param ncid File ID. |
487 | | * @param varid Variable ID. |
488 | | * @param shuffle Pointer to shuffle setting. |
489 | | * @param deflate Pointer to deflate setting. |
490 | | * @param deflate_level Pointer to deflate level. |
491 | | * @param fletcher32 Pointer to fletcher32 setting. |
492 | | * @param contiguous Pointer to contiguous setting. |
493 | | * @param chunksizes Array of chunksizes. |
494 | | * @param no_fill Pointer to no_fill setting. |
495 | | * @param fill_value Pointer to fill value. |
496 | | * @param endianness Pointer to endianness setting. |
497 | | * |
498 | | * @returns ::NC_NOERR for success |
499 | | * @returns ::NC_EBADID Bad ncid. |
500 | | * @returns ::NC_ENOTVAR Invalid variable ID. |
501 | | * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is |
502 | | * not netCDF-4/NCZ. |
503 | | * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3 |
504 | | * netcdf-4 file. |
505 | | * @returns ::NC_ELATEDEF Too late to change settings for this variable. |
506 | | * @returns ::NC_ENOTINDEFINE Not in define mode. |
507 | | * @returns ::NC_EPERM File is read only. |
508 | | * @returns ::NC_EINVAL Invalid input |
509 | | * @returns ::NC_EBADCHUNK Bad chunksize. |
510 | | * @author Dennis Heimbigner, Ed Hartnett |
511 | | */ |
512 | | static int |
513 | | ncz_def_var_extra(int ncid, int varid, int *shuffle, int *unused1, |
514 | | int *unused2, int *fletcher32, int *storagep, |
515 | | const size_t *chunksizes, int *no_fill, |
516 | | const void *fill_value, int *endianness, |
517 | | int *quantize_mode, int *nsd) |
518 | 0 | { |
519 | 0 | NC_GRP_INFO_T *grp; |
520 | 0 | NC_FILE_INFO_T *h5; |
521 | 0 | NC_VAR_INFO_T *var; |
522 | 0 | NCZ_VAR_INFO_T *zvar; |
523 | 0 | int d; |
524 | 0 | int retval = NC_NOERR; |
525 | 0 | int storage = NC_CHUNKED; |
526 | 0 | size_t contigchunksizes[NC_MAX_VAR_DIMS]; /* Fake chunksizes if storage is contiguous or compact */ |
527 | |
|
528 | 0 | LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); |
529 | |
|
530 | 0 | ZTRACE(2,"ncid=%d varid=%d shuffle=%d fletcher32=%d no_fill=%d, fill_value=%p endianness=%d quantize_mode=%d nsd=%d", |
531 | 0 | ncid,varid, |
532 | 0 | (shuffle?*shuffle:-1), |
533 | 0 | (fletcher32?*fletcher32:-1), |
534 | 0 | (no_fill?*no_fill:-1), |
535 | 0 | fill_value, |
536 | 0 | (endianness?*endianness:-1), |
537 | 0 | (quantize_mode?*quantize_mode:-1), |
538 | 0 | (nsd?*nsd:-1) |
539 | 0 | ); |
540 | | |
541 | | /* Find info for this file and group, and set pointer to each. */ |
542 | 0 | if ((retval = nc4_find_nc_grp_h5(ncid, NULL, &grp, &h5))) |
543 | 0 | goto done; |
544 | 0 | assert(grp && h5); |
545 | | |
546 | | /* Trying to write to a read-only file? No way, Jose! */ |
547 | 0 | if (h5->no_write) |
548 | 0 | {retval = NC_EPERM; goto done;} |
549 | | |
550 | | /* Find the var. */ |
551 | 0 | if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid))) |
552 | 0 | {retval = NC_ENOTVAR; goto done;} |
553 | 0 | assert(var && var->hdr.id == varid); |
554 | |
|
555 | 0 | zvar = var->format_var_info; |
556 | |
|
557 | 0 | ZTRACEMORE(1,"\tstoragep=%d chunksizes=%s",(storagep?*storagep:-1),(chunksizes?nczprint_sizevector(var->ndims,chunksizes):"null")); |
558 | | |
559 | | /* Can't turn on parallel and deflate/fletcher32/szip/shuffle |
560 | | * before HDF5 1.10.3. */ |
561 | | #ifdef NETCDF_ENABLE_NCZARR_FILTERS |
562 | | #ifndef HDF5_SUPPORTS_PAR_FILTERS |
563 | | if (h5->parallel == NC_TRUE) |
564 | | if (nclistlength(((NClist*)var->filters)) > 0 || fletcher32 || shuffle) |
565 | | {retval = NC_EINVAL; goto done;} |
566 | | #endif |
567 | | #endif |
568 | | |
569 | | /* If the HDF5 dataset has already been created, then it is too |
570 | | * late to set all the extra stuff. */ |
571 | 0 | if (var->created) |
572 | 0 | {retval = NC_ELATEDEF; goto done;} |
573 | | |
574 | | #if 0 |
575 | | /* Check compression options. */ |
576 | | if (deflate && !deflate_level) |
577 | | {retval = NC_EINVAL; goto done;} |
578 | | |
579 | | /* Valid deflate level? */ |
580 | | if (deflate) |
581 | | { |
582 | | if (*deflate) |
583 | | if (*deflate_level < NC_MIN_DEFLATE_LEVEL || |
584 | | *deflate_level > NC_MAX_DEFLATE_LEVEL) |
585 | | {retval = NC_EINVAL; goto done;} |
586 | | |
587 | | /* For scalars, just ignore attempt to deflate. */ |
588 | | if (!var->ndims) |
589 | | goto done; |
590 | | |
591 | | /* If szip is in use, return an error. */ |
592 | | if ((retval = nc_inq_var_szip(ncid, varid, &option_mask, NULL))) |
593 | | goto done; |
594 | | if (option_mask) |
595 | | {retval = NC_EINVAL; goto done;} |
596 | | |
597 | | /* Set the deflate settings. */ |
598 | | var->storage = NC_CONTIGUOUS; |
599 | | var->deflate = *deflate; |
600 | | if (*deflate) |
601 | | var->deflate_level = *deflate_level; |
602 | | LOG((3, "%s: *deflate_level %d", __func__, *deflate_level)); |
603 | | } |
604 | | #endif |
605 | | |
606 | | /* Shuffle filter? */ |
607 | 0 | if (shuffle && *shuffle) { |
608 | 0 | retval = nc_inq_var_filter_info(ncid,varid,H5Z_FILTER_SHUFFLE,NULL,NULL); |
609 | 0 | if(!retval || retval == NC_ENOFILTER) { |
610 | 0 | if((retval = NCZ_def_var_filter(ncid,varid,H5Z_FILTER_SHUFFLE,0,NULL))) return retval; |
611 | 0 | var->storage = NC_CHUNKED; |
612 | 0 | } |
613 | 0 | } |
614 | | |
615 | | /* Fletcher32 checksum error protection? */ |
616 | 0 | if (fletcher32 && fletcher32) { |
617 | 0 | retval = nc_inq_var_filter_info(ncid,varid,H5Z_FILTER_FLETCHER32,NULL,NULL); |
618 | 0 | if(!retval || retval == NC_ENOFILTER) { |
619 | 0 | if((retval = NCZ_def_var_filter(ncid,varid,H5Z_FILTER_FLETCHER32,0,NULL))) return retval; |
620 | 0 | var->storage = NC_CHUNKED; |
621 | 0 | } |
622 | 0 | } |
623 | | |
624 | | /* Handle storage settings. */ |
625 | 0 | if (storagep) |
626 | 0 | { |
627 | 0 | storage = *storagep; |
628 | | /* Does the user want a contiguous or compact dataset? Not so |
629 | | * fast! Make sure that there are no unlimited dimensions, and |
630 | | * no filters in use for this data. */ |
631 | 0 | if (storage != NC_CHUNKED) |
632 | 0 | { |
633 | | #ifdef NCZARR_FILTERS |
634 | | if (nclistlength(((NClist*)var->filters)) > 0) |
635 | | {retval = NC_EINVAL; goto done;} |
636 | | #endif |
637 | 0 | for (d = 0; d < var->ndims; d++) { |
638 | 0 | if (var->dim[d]->unlimited) |
639 | 0 | {retval = NC_EINVAL; goto done;} |
640 | 0 | contigchunksizes[d] = var->dim[d]->len; /* Fake a single big chunk */ |
641 | 0 | } |
642 | 0 | chunksizes = (const size_t*)contigchunksizes; |
643 | 0 | storage = NC_CHUNKED; /*only chunked supported */ |
644 | 0 | } |
645 | | |
646 | 0 | if (storage == NC_CHUNKED && var->ndims == 0) { |
647 | 0 | {retval = NC_EINVAL; goto done;} |
648 | 0 | } else if (storage == NC_CHUNKED && var->ndims > 0) { |
649 | 0 | var->storage = NC_CHUNKED; |
650 | | |
651 | | /* If the user provided chunksizes, check that they are valid |
652 | | * and that their total size of chunk is less than 4 GB. */ |
653 | 0 | if (chunksizes) |
654 | 0 | { |
655 | | /* Check the chunksizes for validity. */ |
656 | 0 | if ((retval = check_chunksizes(grp, var, chunksizes))) |
657 | 0 | goto done; |
658 | | |
659 | | /* Ensure chunksize is smaller than dimension size */ |
660 | 0 | for (d = 0; d < var->ndims; d++) |
661 | 0 | if (!var->dim[d]->unlimited && var->dim[d]->len > 0 && |
662 | 0 | chunksizes[d] > var->dim[d]->len) |
663 | 0 | {retval = NC_EBADCHUNK; goto done;} |
664 | 0 | } |
665 | 0 | } |
666 | | |
667 | | /* Is this a variable with a chunksize greater than the current |
668 | | * cache size? */ |
669 | 0 | if (var->storage == NC_CHUNKED) |
670 | 0 | { |
671 | 0 | int anyzero = 0; /* check for any zero length chunksizes */ |
672 | 0 | zvar = var->format_var_info; |
673 | 0 | assert(zvar->cache != NULL); |
674 | 0 | zvar->cache->valid = 0; |
675 | 0 | if(chunksizes) { |
676 | 0 | for (d = 0; d < var->ndims; d++) { |
677 | 0 | var->chunksizes[d] = chunksizes[d]; |
678 | 0 | if(chunksizes[d] == 0) anyzero = 1; |
679 | 0 | } |
680 | 0 | } |
681 | | /* If chunksizes == NULL or anyzero then use defaults */ |
682 | 0 | if(chunksizes == NULL || anyzero) { /* Use default chunking */ |
683 | 0 | if ((retval = ncz_find_default_chunksizes2(grp, var))) |
684 | 0 | goto done; |
685 | 0 | } |
686 | 0 | assert(var->chunksizes != NULL); |
687 | | /* Set the chunksize product for this variable. */ |
688 | 0 | zvar->chunkproduct = 1; |
689 | 0 | for (d = 0; d < var->ndims; d++) |
690 | 0 | zvar->chunkproduct *= var->chunksizes[d]; |
691 | 0 | zvar->chunksize = zvar->chunkproduct * var->type_info->size; |
692 | 0 | } |
693 | | /* Adjust cache */ |
694 | 0 | if((retval = NCZ_adjust_var_cache(var))) goto done; |
695 | | |
696 | | #ifdef LOGGING |
697 | | { |
698 | | int dfalt = (chunksizes == NULL); |
699 | | reportchunking(dfalt ? "extra: default: " : "extra: user: ", var); |
700 | | } |
701 | | #endif |
702 | 0 | } |
703 | | |
704 | | /* Are we setting a fill modes? */ |
705 | 0 | if (no_fill) |
706 | 0 | { |
707 | 0 | if (*no_fill) |
708 | 0 | { |
709 | | /* NC_STRING types may not turn off fill mode. It's disallowed |
710 | | * by HDF5 and will cause a HDF5 error later. */ |
711 | 0 | if (*no_fill) |
712 | 0 | if (var->type_info->hdr.id == NC_STRING) |
713 | 0 | {retval = NC_EINVAL; goto done;} |
714 | | |
715 | | /* Set the no-fill mode. */ |
716 | 0 | var->no_fill = NC_TRUE; |
717 | 0 | } |
718 | 0 | else |
719 | 0 | var->no_fill = NC_FALSE; |
720 | 0 | } |
721 | | |
722 | | /* Are we setting a fill value? */ |
723 | 0 | if (fill_value && no_fill && !(*no_fill)) |
724 | 0 | { |
725 | | /* Copy the fill_value. */ |
726 | 0 | LOG((4, "Copying fill value into metadata for variable %s", |
727 | 0 | var->hdr.name)); |
728 | | |
729 | | /* If there's a _FillValue attribute, delete it. */ |
730 | 0 | retval = NCZ_del_att(ncid, varid, NC_FillValue); |
731 | 0 | if (retval && retval != NC_ENOTATT) |
732 | 0 | goto done; |
733 | | |
734 | | /* Create a _FillValue attribute; will also fill in var->fill_value */ |
735 | 0 | if ((retval = nc_put_att(ncid, varid, NC_FillValue, var->type_info->hdr.id, |
736 | 0 | 1, fill_value))) |
737 | 0 | goto done; |
738 | | /* Reclaim any existing fill_chunk */ |
739 | 0 | if((retval = NCZ_reclaim_fill_chunk(zvar->cache))) goto done; |
740 | 0 | } else if (var->fill_value && no_fill && (*no_fill)) { /* Turning off fill value? */ |
741 | | /* If there's a _FillValue attribute, delete it. */ |
742 | 0 | retval = NCZ_del_att(ncid, varid, NC_FillValue); |
743 | 0 | if (retval && retval != NC_ENOTATT) return retval; |
744 | 0 | if((retval = NCZ_reclaim_fill_value(var))) return retval; |
745 | 0 | } |
746 | | |
747 | | /* Is the user setting the endianness? */ |
748 | 0 | if (endianness) |
749 | 0 | { |
750 | | /* Setting endianness is only premitted on atomic integer and |
751 | | * atomic float types. */ |
752 | 0 | switch (var->type_info->hdr.id) |
753 | 0 | { |
754 | 0 | case NC_BYTE: |
755 | 0 | case NC_SHORT: |
756 | 0 | case NC_INT: |
757 | 0 | case NC_FLOAT: |
758 | 0 | case NC_DOUBLE: |
759 | 0 | case NC_UBYTE: |
760 | 0 | case NC_USHORT: |
761 | 0 | case NC_UINT: |
762 | 0 | case NC_INT64: |
763 | 0 | case NC_UINT64: |
764 | 0 | break; |
765 | 0 | default: |
766 | 0 | {retval = NC_EINVAL; goto done;} |
767 | 0 | } |
768 | 0 | var->type_info->endianness = *endianness; |
769 | | /* Propagate */ |
770 | 0 | var->endianness = *endianness; |
771 | 0 | } |
772 | | |
773 | | /* Remember quantization settings. They will be used when data are |
774 | | * written. |
775 | | * Code block is identical to one in hdf5var.c---consider functionalizing */ |
776 | 0 | if (quantize_mode) |
777 | 0 | { |
778 | | /* Only four valid mode settings. */ |
779 | 0 | if (*quantize_mode != NC_NOQUANTIZE && |
780 | 0 | *quantize_mode != NC_QUANTIZE_BITGROOM && |
781 | 0 | *quantize_mode != NC_QUANTIZE_GRANULARBR && |
782 | 0 | *quantize_mode != NC_QUANTIZE_BITROUND) |
783 | 0 | return NC_EINVAL; |
784 | | |
785 | 0 | if (*quantize_mode == NC_QUANTIZE_BITGROOM || |
786 | 0 | *quantize_mode == NC_QUANTIZE_GRANULARBR || |
787 | 0 | *quantize_mode == NC_QUANTIZE_BITROUND) |
788 | 0 | { |
789 | | |
790 | | /* Only float and double types can have quantization. */ |
791 | 0 | if (var->type_info->hdr.id != NC_FLOAT && |
792 | 0 | var->type_info->hdr.id != NC_DOUBLE) |
793 | 0 | return NC_EINVAL; |
794 | | |
795 | | /* All quantization codecs require number of significant digits */ |
796 | 0 | if (!nsd) |
797 | 0 | return NC_EINVAL; |
798 | | |
799 | | /* NSD must be in range. */ |
800 | 0 | if (*nsd <= 0) |
801 | 0 | return NC_EINVAL; |
802 | | |
803 | 0 | if (*quantize_mode == NC_QUANTIZE_BITGROOM || |
804 | 0 | *quantize_mode == NC_QUANTIZE_GRANULARBR) |
805 | 0 | { |
806 | 0 | if (var->type_info->hdr.id == NC_FLOAT && |
807 | 0 | *nsd > NC_QUANTIZE_MAX_FLOAT_NSD) |
808 | 0 | return NC_EINVAL; |
809 | 0 | if (var->type_info->hdr.id == NC_DOUBLE && |
810 | 0 | *nsd > NC_QUANTIZE_MAX_DOUBLE_NSD) |
811 | 0 | return NC_EINVAL; |
812 | 0 | } |
813 | 0 | else if (*quantize_mode == NC_QUANTIZE_BITROUND) |
814 | 0 | { |
815 | 0 | if (var->type_info->hdr.id == NC_FLOAT && |
816 | 0 | *nsd > NC_QUANTIZE_MAX_FLOAT_NSB) |
817 | 0 | return NC_EINVAL; |
818 | 0 | if (var->type_info->hdr.id == NC_DOUBLE && |
819 | 0 | *nsd > NC_QUANTIZE_MAX_DOUBLE_NSB) |
820 | 0 | return NC_EINVAL; |
821 | 0 | } |
822 | | |
823 | 0 | var->nsd = *nsd; |
824 | 0 | } |
825 | | |
826 | 0 | var->quantize_mode = *quantize_mode; |
827 | | |
828 | | /* If quantization is turned off, then set nsd to 0. */ |
829 | 0 | if (*quantize_mode == NC_NOQUANTIZE) |
830 | 0 | var->nsd = 0; |
831 | 0 | } |
832 | | |
833 | 0 | done: |
834 | 0 | return ZUNTRACE(retval); |
835 | 0 | } |
836 | | |
837 | | /** |
838 | | * @internal Set compression settings on a variable. This is called by |
839 | | * nc_def_var_deflate(). |
840 | | * |
841 | | * @param ncid File ID. |
842 | | * @param varid Variable ID. |
843 | | * @param shuffle True to turn on the shuffle filter. |
844 | | * @param deflate True to turn on deflation. |
845 | | * @param deflate_level A number between 0 (no compression) and 9 |
846 | | * (maximum compression). |
847 | | * |
848 | | * @returns ::NC_NOERR No error. |
849 | | * @returns ::NC_EBADID Bad ncid. |
850 | | * @returns ::NC_ENOTVAR Invalid variable ID. |
851 | | * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is |
852 | | * not netCDF-4/NCZ. |
853 | | * @returns ::NC_ELATEDEF Too late to change settings for this variable. |
854 | | * @returns ::NC_ENOTINDEFINE Not in define mode. |
855 | | * @returns ::NC_EINVAL Invalid input |
856 | | * @author Dennis Heimbigner, Ed Hartnett |
857 | | */ |
858 | | int |
859 | | NCZ_def_var_deflate(int ncid, int varid, int shuffle, int deflate, |
860 | | int deflate_level) |
861 | 0 | { |
862 | 0 | int stat = NC_NOERR; |
863 | 0 | unsigned int level = (unsigned int)deflate_level; |
864 | | /* Set shuffle first */ |
865 | 0 | if((stat = ncz_def_var_extra(ncid, varid, &shuffle, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))) goto done; |
866 | 0 | if(deflate) |
867 | 0 | stat = nc_def_var_filter(ncid, varid, H5Z_FILTER_DEFLATE,1,&level); |
868 | 0 | if(stat) goto done; |
869 | 0 | done: |
870 | 0 | return stat; |
871 | 0 | } |
872 | | |
873 | | /** |
874 | | * @internal Set checksum on a variable. This is called by |
875 | | * nc_def_var_fletcher32(). |
876 | | * |
877 | | * @param ncid File ID. |
878 | | * @param varid Variable ID. |
879 | | * @param fletcher32 Pointer to fletcher32 setting. |
880 | | * |
881 | | * @returns ::NC_NOERR No error. |
882 | | * @returns ::NC_EBADID Bad ncid. |
883 | | * @returns ::NC_ENOTVAR Invalid variable ID. |
884 | | * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is |
885 | | * not netCDF-4/NCZ. |
886 | | * @returns ::NC_ELATEDEF Too late to change settings for this variable. |
887 | | * @returns ::NC_ENOTINDEFINE Not in define mode. |
888 | | * @returns ::NC_EINVAL Invalid input |
889 | | * @author Dennis Heimbigner, Ed Hartnett |
890 | | */ |
891 | | int |
892 | | NCZ_def_var_fletcher32(int ncid, int varid, int fletcher32) |
893 | 0 | { |
894 | 0 | return ncz_def_var_extra(ncid, varid, NULL, NULL, NULL, &fletcher32, |
895 | 0 | NULL, NULL, NULL, NULL, NULL, NULL, NULL); |
896 | 0 | } |
897 | | |
898 | | /** |
899 | | * @internal Define chunking stuff for a var. This is called by |
900 | | * nc_def_var_chunking(). Chunking is required in any dataset with one |
901 | | * or more unlimited dimensions in NCZ, or any dataset using a |
902 | | * filter. |
903 | | * |
904 | | * @param ncid File ID. |
905 | | * @param varid Variable ID. |
906 | | * @param contiguous Pointer to contiguous setting. |
907 | | * @param chunksizesp Array of chunksizes. |
908 | | * |
909 | | * @returns ::NC_NOERR No error. |
910 | | * @returns ::NC_EBADID Bad ncid. |
911 | | * @returns ::NC_ENOTVAR Invalid variable ID. |
912 | | * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is |
913 | | * not netCDF-4/NCZ. |
914 | | * @returns ::NC_ELATEDEF Too late to change settings for this variable. |
915 | | * @returns ::NC_ENOTINDEFINE Not in define mode. |
916 | | * @returns ::NC_EINVAL Invalid input |
917 | | * @returns ::NC_EBADCHUNK Bad chunksize. |
918 | | * @author Dennis Heimbigner, Ed Hartnett |
919 | | */ |
920 | | int |
921 | | NCZ_def_var_chunking(int ncid, int varid, int contiguous, const size_t *chunksizesp) |
922 | 0 | { |
923 | 0 | return ncz_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, |
924 | 0 | &contiguous, chunksizesp, NULL, NULL, NULL, NULL, NULL); |
925 | 0 | } |
926 | | |
927 | | /** |
928 | | * @internal Define chunking stuff for a var. This is called by |
929 | | * the fortran API. |
930 | | * |
931 | | * @param ncid File ID. |
932 | | * @param varid Variable ID. |
933 | | * @param contiguous Pointer to contiguous setting. |
934 | | * @param chunksizesp Array of chunksizes. |
935 | | * |
936 | | * @returns ::NC_NOERR No error. |
937 | | * @returns ::NC_EBADID Bad ncid. |
938 | | * @returns ::NC_ENOTVAR Invalid variable ID. |
939 | | * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is |
940 | | * not netCDF-4/NCZ. |
941 | | * @returns ::NC_ELATEDEF Too late to change settings for this variable. |
942 | | * @returns ::NC_ENOTINDEFINE Not in define mode. |
943 | | * @returns ::NC_EINVAL Invalid input |
944 | | * @returns ::NC_EBADCHUNK Bad chunksize. |
945 | | * @author Dennis Heimbigner, Ed Hartnett |
946 | | */ |
947 | | int |
948 | | ncz_def_var_chunking_ints(int ncid, int varid, int contiguous, int *chunksizesp) |
949 | 0 | { |
950 | 0 | NC_VAR_INFO_T *var; |
951 | 0 | size_t *cs; |
952 | 0 | int i, retval; |
953 | | |
954 | | /* Get pointer to the var. */ |
955 | 0 | if ((retval = nc4_find_grp_h5_var(ncid, varid, NULL, NULL, &var))) |
956 | 0 | return THROW(retval); |
957 | 0 | assert(var); |
958 | | |
959 | | /* Allocate space for the size_t copy of the chunksizes array. */ |
960 | 0 | if (var->ndims) |
961 | 0 | if (!(cs = malloc(var->ndims * sizeof(size_t)))) |
962 | 0 | return NC_ENOMEM; |
963 | | |
964 | | /* Copy to size_t array. */ |
965 | 0 | for (i = 0; i < var->ndims; i++) |
966 | 0 | cs[i] = chunksizesp[i]; |
967 | |
|
968 | 0 | retval = ncz_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, |
969 | 0 | &contiguous, cs, NULL, NULL, NULL, NULL, NULL); |
970 | |
|
971 | 0 | if (var->ndims) |
972 | 0 | free(cs); |
973 | 0 | return THROW(retval); |
974 | 0 | } |
975 | | |
976 | | /** |
977 | | * @internal This functions sets fill value and no_fill mode for a |
978 | | * netCDF-4 variable. It is called by nc_def_var_fill(). |
979 | | * |
980 | | * @note All pointer parameters may be NULL, in which case they are ignored. |
981 | | * @param ncid File ID. |
982 | | * @param varid Variable ID. |
983 | | * @param no_fill No_fill setting. |
984 | | * @param fill_value Pointer to fill value. |
985 | | * |
986 | | * @returns ::NC_NOERR for success |
987 | | * @returns ::NC_EBADID Bad ncid. |
988 | | * @returns ::NC_ENOTVAR Invalid variable ID. |
989 | | * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is |
990 | | * not netCDF-4/NCZ. |
991 | | * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3 |
992 | | * netcdf-4 file. |
993 | | * @returns ::NC_ELATEDEF Too late to change settings for this variable. |
994 | | * @returns ::NC_ENOTINDEFINE Not in define mode. |
995 | | * @returns ::NC_EPERM File is read only. |
996 | | * @returns ::NC_EINVAL Invalid input |
997 | | * @author Dennis Heimbigner, Ed Hartnett |
998 | | */ |
999 | | int |
1000 | | NCZ_def_var_fill(int ncid, int varid, int no_fill, const void *fill_value) |
1001 | 0 | { |
1002 | 0 | return ncz_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, NULL, |
1003 | 0 | NULL, &no_fill, fill_value, NULL, NULL, NULL); |
1004 | 0 | } |
1005 | | |
1006 | | /** |
1007 | | * @internal This functions sets endianness for a netCDF-4 |
1008 | | * variable. Called by nc_def_var_endian(). |
1009 | | * |
1010 | | * @note All pointer parameters may be NULL, in which case they are ignored. |
1011 | | * @param ncid File ID. |
1012 | | * @param varid Variable ID. |
1013 | | * @param endianness Endianness setting. |
1014 | | * |
1015 | | * @returns ::NC_NOERR for success |
1016 | | * @returns ::NC_EBADID Bad ncid. |
1017 | | * @returns ::NC_ENOTVAR Invalid variable ID. |
1018 | | * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is |
1019 | | * not netCDF-4/NCZ. |
1020 | | * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3 |
1021 | | * netcdf-4 file. |
1022 | | * @returns ::NC_ELATEDEF Too late to change settings for this variable. |
1023 | | * @returns ::NC_ENOTINDEFINE Not in define mode. |
1024 | | * @returns ::NC_EPERM File is read only. |
1025 | | * @returns ::NC_EINVAL Invalid input |
1026 | | * @author Dennis Heimbigner, Ed Hartnett |
1027 | | */ |
1028 | | int |
1029 | | NCZ_def_var_endian(int ncid, int varid, int endianness) |
1030 | 0 | { |
1031 | 0 | return ncz_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, NULL, |
1032 | 0 | NULL, NULL, NULL, &endianness, NULL, NULL); |
1033 | 0 | } |
1034 | | |
1035 | | /** |
1036 | | * @internal Set quantization settings on a variable. This is |
1037 | | * called by nc_def_var_quantize(). |
1038 | | * |
1039 | | * Quantization allows the user to specify a number of significant |
1040 | | * digits for variables of type ::NC_FLOAT or ::NC_DOUBLE. (Attempting |
1041 | | * to set quantize for other types will result in an ::NC_EINVAL |
1042 | | * error.) |
1043 | | * |
1044 | | * When quantize is turned on, and the number of significant digits |
1045 | | * (NSD) has been specified, then the netCDF library will quantize according |
1046 | | * to the selected algorithm. BitGroom interprets NSD as decimal digits |
1047 | | * will apply all zeros or all ones (alternating) to bits which are not |
1048 | | * needed to specify the value to the number of significant decimal digits. |
1049 | | * BitGroom retain the same number of bits for all values of a variable. |
1050 | | * BitRound (BR) interprets NSD as binary digits (i.e., bits) and keeps the |
1051 | | * the user-specified number of significant bits then rounds the result |
1052 | | * to the nearest representable number according to IEEE rounding rules. |
1053 | | * BG and BR both retain a uniform number of significant bits for all |
1054 | | * values of a variable. Granular BitRound interprest NSD as decimal |
1055 | | * digits. GranularBR determines the number of bits to necessary to |
1056 | | * retain the user-specified number of significant digits individually |
1057 | | * for every value of the variable. GranularBR then applies the BR |
1058 | | * quantization algorithm on a granular, value-by-value, rather than |
1059 | | * uniformly for the entire variable. GranularBR quantizes more bits |
1060 | | * than BG, and is thus more compressive and less accurate than BG. |
1061 | | * BR knows bits and makes no guarantees about decimal precision. |
1062 | | * All quantization algorithms change the values of the data, and make |
1063 | | * it more compressible. |
1064 | | * |
1065 | | * Quantizing the data does not reduce the size of the data on disk, |
1066 | | * but combining quantize with compression will allow for better |
1067 | | * compression. Since the data values are changed, the use of quantize |
1068 | | * and compression such as deflate constitute lossy compression. |
1069 | | * |
1070 | | * Producers of large datasets may find that using quantize with |
1071 | | * compression will result in significant improvent in the final data |
1072 | | * size. |
1073 | | * |
1074 | | * Variables which use quantize will have added an attribute with name |
1075 | | * ::NC_QUANTIZE_BITGROOM_ATT_NAME, ::NC_QUANTIZE_GRANULARBR_ATT_NAME, |
1076 | | * or ::NC_QUANTIZE_BITROUND_ATT_NAME that contains the number of |
1077 | | * significant digits. Users should not delete or change this attribute. |
1078 | | * This is the only record that quantize has been applied to the data. |
1079 | | * |
1080 | | * As with the deflate settings, quantize settings may only be |
1081 | | * modified before the first call to nc_enddef(). Once nc_enddef() is |
1082 | | * called for the file, quantize settings for any variable in the file |
1083 | | * may not be changed. |
1084 | | * |
1085 | | * Use of quantization is fully backwards compatible with existing |
1086 | | * versions and packages that can read compressed netCDF data. A |
1087 | | * variable which has been quantized is readable to older versions of |
1088 | | * the netCDF libraries, and to netCDF-Java. |
1089 | | * |
1090 | | * @param ncid File ID. |
1091 | | * @param varid Variable ID. NC_GLOBAL may not be used. |
1092 | | * @param quantize_mode Quantization mode. May be ::NC_NOQUANTIZE or |
1093 | | * ::NC_QUANTIZE_BITGROOM, ::NC_QUANTIZE_BITROUND or ::NC_QUANTIZE_GRANULARBR. |
1094 | | * @param nsd Number of significant digits (either decimal or binary). |
1095 | | * May be any integer from 1 to ::NC_QUANTIZE_MAX_FLOAT_NSD (for variables |
1096 | | * of type ::NC_FLOAT) or ::NC_QUANTIZE_MAX_DOUBLE_NSD (for variables |
1097 | | * of type ::NC_DOUBLE) for mode ::NC_QUANTIZE_BITGROOM and mode |
1098 | | * ::NC_QUANTIZE_GRANULARBR. May be any integer from 1 to |
1099 | | * ::NC_QUANTIZE_MAX_FLOAT_NSB (for variables of type ::NC_FLOAT) or |
1100 | | * ::NC_QUANTIZE_MAX_DOUBLE_NSB (for variables of type ::NC_DOUBLE) |
1101 | | * for mode ::NC_QUANTIZE_BITROUND. |
1102 | | * |
1103 | | * @returns ::NC_NOERR No error. |
1104 | | * @returns ::NC_EBADID Bad ncid. |
1105 | | * @returns ::NC_ENOTVAR Invalid variable ID. |
1106 | | * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is |
1107 | | * not netCDF-4/HDF5. |
1108 | | * @returns ::NC_ELATEDEF Too late to change settings for this variable. |
1109 | | * @returns ::NC_ENOTINDEFINE Not in define mode. |
1110 | | * @returns ::NC_EINVAL Invalid input |
1111 | | * @author Ed Hartnett, Dennis Heimbigner |
1112 | | */ |
1113 | | int |
1114 | | NCZ_def_var_quantize(int ncid, int varid, int quantize_mode, int nsd) |
1115 | 0 | { |
1116 | 0 | return ncz_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, |
1117 | 0 | NULL, NULL, NULL, NULL, NULL, |
1118 | 0 | &quantize_mode, &nsd); |
1119 | 0 | } |
1120 | | |
1121 | | /** |
1122 | | Ensure that the quantize information for a variable is defined. |
1123 | | Keep a flag in the NCZ_VAR_INFO_T struct to indicate if quantize |
1124 | | info is defined, and if not, read the attribute. |
1125 | | */ |
1126 | | int |
1127 | | NCZ_ensure_quantizer(int ncid, NC_VAR_INFO_T* var) |
1128 | 0 | { |
1129 | 0 | int nsd = 0; |
1130 | | |
1131 | | /* Read the attribute */ |
1132 | 0 | if(NCZ_get_att(ncid,var->hdr.id,NC_QUANTIZE_BITGROOM_ATT_NAME,&nsd,NC_INT)==NC_NOERR) { |
1133 | 0 | var->quantize_mode = NC_QUANTIZE_BITGROOM; |
1134 | 0 | var->nsd = nsd; |
1135 | 0 | } else if(NCZ_get_att(ncid,var->hdr.id,NC_QUANTIZE_GRANULARBR_ATT_NAME,&nsd,NC_INT)==NC_NOERR) { |
1136 | 0 | var->quantize_mode = NC_QUANTIZE_GRANULARBR; |
1137 | 0 | var->nsd = nsd; |
1138 | 0 | } else if(NCZ_get_att(ncid,var->hdr.id,NC_QUANTIZE_BITROUND_ATT_NAME,&nsd,NC_INT)==NC_NOERR) { |
1139 | 0 | var->quantize_mode = NC_QUANTIZE_BITROUND; |
1140 | 0 | var->nsd = nsd; |
1141 | 0 | } else { |
1142 | 0 | var->quantize_mode = NC_NOQUANTIZE; |
1143 | 0 | var->nsd = 0; |
1144 | 0 | } |
1145 | 0 | if(var->quantize_mode < 0) var->quantize_mode = 0; |
1146 | 0 | return NC_NOERR; |
1147 | 0 | } |
1148 | | |
1149 | | /** |
1150 | | * @internal Get quantize information about a variable. Pass NULL for |
1151 | | * whatever you don't care about. Note that this can require reading |
1152 | | * all the attributes for the variable. |
1153 | | * |
1154 | | * @param ncid File ID. |
1155 | | * @param varid Variable ID. |
1156 | | * @param quantize_modep Gets quantize mode. |
1157 | | * @param nsdp Gets Number of Significant Digits if quantize is in use. |
1158 | | * |
1159 | | * @returns ::NC_NOERR No error. |
1160 | | * @returns ::NC_EBADID Bad ncid. |
1161 | | * @returns ::NC_ENOTVAR Bad varid. |
1162 | | * @returns ::NC_EINVAL Invalid input. |
1163 | | * @author Ed Hartnett |
1164 | | */ |
1165 | | int |
1166 | | NCZ_inq_var_quantize(int ncid, int varid, int *quantize_modep, |
1167 | | int *nsdp) |
1168 | 0 | { |
1169 | 0 | NC_VAR_INFO_T *var; |
1170 | 0 | int retval; |
1171 | |
|
1172 | 0 | LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); |
1173 | | |
1174 | | /* Find info for this file and group, and set pointer to each. */ |
1175 | | /* Get pointer to the var. */ |
1176 | 0 | if ((retval = nc4_find_grp_h5_var(ncid, varid, NULL, NULL, &var))) |
1177 | 0 | return retval; |
1178 | 0 | if (!var) |
1179 | 0 | return NC_ENOTVAR; |
1180 | 0 | assert(var->hdr.id == varid); |
1181 | 0 | if(var->quantize_mode == -1) |
1182 | 0 | {if((retval = NCZ_ensure_quantizer(ncid, var))) return retval;} |
1183 | | /* Copy the data to the user's data buffers. */ |
1184 | 0 | if (quantize_modep) |
1185 | 0 | *quantize_modep = var->quantize_mode; |
1186 | 0 | if (nsdp) |
1187 | 0 | *nsdp = var->nsd; |
1188 | 0 | return NC_NOERR; |
1189 | 0 | } |
1190 | | |
1191 | | /** |
1192 | | * @internal Rename a var to "bubba," for example. This is called by |
1193 | | * nc_rename_var() for netCDF-4 files. This results in complexities |
1194 | | * when coordinate variables are involved. |
1195 | | |
1196 | | * Whenever a var has the same name as a dim, and also uses that dim |
1197 | | * as its first dimension, then that var is aid to be a coordinate |
1198 | | * variable for that dimensions. Coordinate variables are represented |
1199 | | * in the ZARR by making them dimscales. Dimensions without coordinate |
1200 | | * vars are represented by datasets which are dimscales, but have a |
1201 | | * special attribute marking them as dimscales without associated |
1202 | | * coordinate variables. |
1203 | | * |
1204 | | * When a var is renamed, we must detect whether it has become a |
1205 | | * coordinate var (by being renamed to the same name as a dim that is |
1206 | | * also its first dimension), or whether it is no longer a coordinate |
1207 | | * var. These cause flags to be set in NC_VAR_INFO_T which are used at |
1208 | | * enddef time to make changes in the ZARR file. |
1209 | | * |
1210 | | * @param ncid File ID. |
1211 | | * @param varid Variable ID |
1212 | | * @param name New name of the variable. |
1213 | | * |
1214 | | * @returns ::NC_NOERR No error. |
1215 | | * @returns ::NC_EBADID Bad ncid. |
1216 | | * @returns ::NC_ENOTVAR Invalid variable ID. |
1217 | | * @returns ::NC_EBADNAME Bad name. |
1218 | | * @returns ::NC_EMAXNAME Name is too long. |
1219 | | * @returns ::NC_ENAMEINUSE Name in use. |
1220 | | * @returns ::NC_ENOMEM Out of memory. |
1221 | | * @author Dennis Heimbigner, Ed Hartnett |
1222 | | */ |
1223 | | int |
1224 | | NCZ_rename_var(int ncid, int varid, const char *name) |
1225 | 0 | { |
1226 | 0 | NC_GRP_INFO_T *grp; |
1227 | 0 | NC_FILE_INFO_T *h5; |
1228 | 0 | NC_VAR_INFO_T *var; |
1229 | 0 | int retval = NC_NOERR; |
1230 | |
|
1231 | 0 | if (!name) |
1232 | 0 | return NC_EINVAL; |
1233 | | |
1234 | 0 | LOG((2, "%s: ncid 0x%x varid %d name %s", __func__, ncid, varid, name)); |
1235 | |
|
1236 | 0 | ZTRACE(1,"ncid=%d varid=%d name='%s'",ncid,varid,name); |
1237 | | |
1238 | | /* Find info for this file and group, and set pointer to each. */ |
1239 | 0 | if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) |
1240 | 0 | return THROW(retval); |
1241 | 0 | assert(h5 && grp && grp->format_grp_info); |
1242 | | |
1243 | | /* Is the new name too long? */ |
1244 | 0 | if (strlen(name) > NC_MAX_NAME) |
1245 | 0 | return NC_EMAXNAME; |
1246 | | |
1247 | | /* Trying to write to a read-only file? No way, Jose! */ |
1248 | 0 | if (h5->no_write) |
1249 | 0 | return NC_EPERM; |
1250 | | |
1251 | | /* Check name validity, if strict nc3 rules are in effect for this |
1252 | | * file. */ |
1253 | 0 | if ((retval = NC_check_name(name))) |
1254 | 0 | return THROW(retval); |
1255 | | |
1256 | | /* Get the variable wrt varid */ |
1257 | 0 | if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid))) |
1258 | 0 | return NC_ENOTVAR; |
1259 | | |
1260 | | /* Check if new name is in use; note that renaming to same name is |
1261 | | still an error according to the nc_test/test_write.c |
1262 | | code. Why?*/ |
1263 | 0 | if (ncindexlookup(grp->vars, name)) |
1264 | 0 | return NC_ENAMEINUSE; |
1265 | | |
1266 | | /* If we're not in define mode, new name must be of equal or |
1267 | | less size, if strict nc3 rules are in effect for this . */ |
1268 | 0 | if (!(h5->flags & NC_INDEF) && strlen(name) > strlen(var->hdr.name) && |
1269 | 0 | (h5->cmode & NC_CLASSIC_MODEL)) |
1270 | 0 | return NC_ENOTINDEFINE; |
1271 | | |
1272 | | #ifdef LOOK |
1273 | | /* Is there another dim with this name, for which this var will not |
1274 | | * be a coord var? If so, we have to create a dim without a |
1275 | | * variable for the old name. */ |
1276 | | if ((other_dim = (NC_DIM_INFO_T *)ncindexlookup(grp->dim, name)) && |
1277 | | strcmp(name, var->dim[0]->hdr.name)) |
1278 | | { |
1279 | | /* Create a dim without var dataset for old dim. */ |
1280 | | if ((retval = ncz_create_dim_wo_var(other_dim))) |
1281 | | return THROW(retval); |
1282 | | |
1283 | | /* Give this var a secret ZARR name so it can co-exist in file |
1284 | | * with dim wp var dataset. Base the secret name on the new var |
1285 | | * name. */ |
1286 | | if ((retval = give_var_secret_name(var, name))) |
1287 | | return THROW(retval); |
1288 | | use_secret_name++; |
1289 | | } |
1290 | | |
1291 | | /* Change the ZARR file, if this var has already been created |
1292 | | there. */ |
1293 | | if (var->created) |
1294 | | { |
1295 | | int v; |
1296 | | char *ncz_name; /* Dataset will be renamed to this. */ |
1297 | | ncz_name = use_secret_name ? var->ncz_name: (char *)name; |
1298 | | |
1299 | | /* Do we need to read var metadata? */ |
1300 | | if (!var->meta_read) |
1301 | | if ((retval = ncz_get_var_meta(var))) |
1302 | | return THROW(retval); |
1303 | | |
1304 | | if (var->ndims) |
1305 | | { |
1306 | | NCZ_DIM_INFO_T *ncz_d0; |
1307 | | ncz_d0 = (NCZ_DIM_INFO_T *)var->dim[0]->format_dim_info; |
1308 | | |
1309 | | /* Is there an existing dimscale-only dataset of this name? If |
1310 | | * so, it must be deleted. */ |
1311 | | if (ncz_d0->hdf_dimscaleid) |
1312 | | { |
1313 | | if ((retval = delete_dimscale_dataset(grp, var->dim[0]->hdr.id, |
1314 | | var->dim[0]))) |
1315 | | return THROW(retval); |
1316 | | } |
1317 | | } |
1318 | | |
1319 | | LOG((3, "Moving dataset %s to %s", var->hdr.name, name)); |
1320 | | if (H5Lmove(ncz_grp->hdf_grpid, var->hdr.name, ncz_grp->hdf_grpid, |
1321 | | ncz_name, H5P_DEFAULT, H5P_DEFAULT) < 0) |
1322 | | return NC_EHDFERR; |
1323 | | |
1324 | | /* Rename all the vars in this file with a varid greater than |
1325 | | * this var. Varids are assigned based on dataset creation time, |
1326 | | * and we have just changed that for this var. We must do the |
1327 | | * same for all vars with a > varid, so that the creation order |
1328 | | * will continue to be correct. */ |
1329 | | for (v = var->hdr.id + 1; v < ncindexsize(grp->vars); v++) |
1330 | | { |
1331 | | NC_VAR_INFO_T *my_var; |
1332 | | my_var = (NC_VAR_INFO_T *)ncindexith(grp->vars, v); |
1333 | | assert(my_var); |
1334 | | |
1335 | | LOG((3, "mandatory rename of %s to same name", my_var->hdr.name)); |
1336 | | |
1337 | | /* Rename to temp name. */ |
1338 | | if (H5Lmove(ncz_grp->hdf_grpid, my_var->hdr.name, ncz_grp->hdf_grpid, |
1339 | | NC_TEMP_NAME, H5P_DEFAULT, H5P_DEFAULT) < 0) |
1340 | | return NC_EHDFERR; |
1341 | | |
1342 | | /* Rename to real name. */ |
1343 | | if (H5Lmove(ncz_grp->hdf_grpid, NC_TEMP_NAME, ncz_grp->hdf_grpid, |
1344 | | my_var->hdr.name, H5P_DEFAULT, H5P_DEFAULT) < 0) |
1345 | | return NC_EHDFERR; |
1346 | | } |
1347 | | } |
1348 | | #endif |
1349 | | |
1350 | | /* Now change the name in our metadata. */ |
1351 | 0 | free(var->hdr.name); |
1352 | 0 | if (!(var->hdr.name = strdup(name))) |
1353 | 0 | return NC_ENOMEM; |
1354 | 0 | LOG((3, "var is now %s", var->hdr.name)); |
1355 | | |
1356 | | /* rebuild index. */ |
1357 | 0 | if (!ncindexrebuild(grp->vars)) |
1358 | 0 | return NC_EINTERNAL; |
1359 | | |
1360 | | #ifdef LOOK |
1361 | | /* Check if this was a coordinate variable previously, but names |
1362 | | * are different now */ |
1363 | | if (var->dimscale && strcmp(var->hdr.name, var->dim[0]->hdr.name)) |
1364 | | { |
1365 | | /* Break up the coordinate variable */ |
1366 | | if ((retval = ncz_break_coord_var(grp, var, var->dim[0]))) |
1367 | | return THROW(retval); |
1368 | | } |
1369 | | |
1370 | | /* Check if this should become a coordinate variable. */ |
1371 | | if (!var->dimscale) |
1372 | | { |
1373 | | /* Only variables with >0 dimensions can become coordinate |
1374 | | * variables. */ |
1375 | | if (var->ndims) |
1376 | | { |
1377 | | NC_GRP_INFO_T *dim_grp; |
1378 | | NC_DIM_INFO_T *dim; |
1379 | | |
1380 | | /* Check to see if this is became a coordinate variable. If |
1381 | | * so, it will have the same name as dimension index 0. If it |
1382 | | * is a coordinate var, is it a coordinate var in the same |
1383 | | * group as the dim? */ |
1384 | | if ((retval = ncz_find_dim(grp, var->dimids[0], &dim, &dim_grp))) |
1385 | | return THROW(retval); |
1386 | | if (!strcmp(dim->hdr.name, name) && dim_grp == grp) |
1387 | | { |
1388 | | /* Reform the coordinate variable. */ |
1389 | | if ((retval = ncz_reform_coord_var(grp, var, dim))) |
1390 | | return THROW(retval); |
1391 | | var->became_coord_var = NC_TRUE; |
1392 | | } |
1393 | | } |
1394 | | } |
1395 | | #endif |
1396 | | |
1397 | 0 | return THROW(retval); |
1398 | 0 | } |
1399 | | |
1400 | | /** |
1401 | | * @internal Write an array of data to a variable. This is called by |
1402 | | * nc_put_vara() and other nc_put_vara_* functions, for netCDF-4 |
1403 | | * files. |
1404 | | * |
1405 | | * @param ncid File ID. |
1406 | | * @param varid Variable ID. |
1407 | | * @param startp Array of start indices. |
1408 | | * @param countp Array of counts. |
1409 | | * @param op pointer that gets the data. |
1410 | | * @param memtype The type of these data in memory. |
1411 | | * |
1412 | | * @returns ::NC_NOERR for success |
1413 | | * @author Dennis Heimbigner, Ed Hartnett |
1414 | | */ |
1415 | | int |
1416 | | NCZ_put_vara(int ncid, int varid, const size_t *startp, |
1417 | | const size_t *countp, const void *op, int memtype) |
1418 | 0 | { |
1419 | 0 | return NCZ_put_vars(ncid, varid, startp, countp, NULL, op, memtype); |
1420 | 0 | } |
1421 | | |
1422 | | /** |
1423 | | * @internal Read an array of values. This is called by nc_get_vara() |
1424 | | * for netCDF-4 files, as well as all the other nc_get_vara_* |
1425 | | * functions. |
1426 | | * |
1427 | | * @param ncid File ID. |
1428 | | * @param varid Variable ID. |
1429 | | * @param startp Array of start indices. |
1430 | | * @param countp Array of counts. |
1431 | | * @param ip pointer that gets the data. |
1432 | | * @param memtype The type of these data after it is read into memory. |
1433 | | |
1434 | | * @returns ::NC_NOERR for success |
1435 | | * @author Dennis Heimbigner, Ed Hartnett |
1436 | | */ |
1437 | | int |
1438 | | NCZ_get_vara(int ncid, int varid, const size_t *startp, |
1439 | | const size_t *countp, void *ip, int memtype) |
1440 | 0 | { |
1441 | 0 | return NCZ_get_vars(ncid, varid, startp, countp, NULL, ip, memtype); |
1442 | 0 | } |
1443 | | |
1444 | | /** |
1445 | | * @internal Do some common check for NCZ_put_vars and |
1446 | | * NCZ_get_vars. These checks have to be done when both reading and |
1447 | | * writing data. |
1448 | | * |
1449 | | * @param mem_nc_type Pointer to type of data in memory. |
1450 | | * @param var Pointer to var info struct. |
1451 | | * @param h5 Pointer to ZARR file info struct. |
1452 | | * |
1453 | | * @return ::NC_NOERR No error. |
1454 | | * @author Dennis Heimbigner, Ed Hartnett |
1455 | | */ |
1456 | | static int |
1457 | | check_for_vara(nc_type *mem_nc_type, NC_VAR_INFO_T *var, NC_FILE_INFO_T *h5) |
1458 | 0 | { |
1459 | 0 | int retval; |
1460 | | |
1461 | | /* If mem_nc_type is NC_NAT, it means we want to use the file type |
1462 | | * as the mem type as well. */ |
1463 | 0 | assert(mem_nc_type); |
1464 | 0 | if (*mem_nc_type == NC_NAT) |
1465 | 0 | *mem_nc_type = var->type_info->hdr.id; |
1466 | 0 | assert(*mem_nc_type); |
1467 | | |
1468 | | /* No NC_CHAR conversions, you pervert! */ |
1469 | 0 | if (var->type_info->hdr.id != *mem_nc_type && |
1470 | 0 | (var->type_info->hdr.id == NC_CHAR || *mem_nc_type == NC_CHAR)) |
1471 | 0 | return NC_ECHAR; |
1472 | | |
1473 | | /* If we're in define mode, we can't read or write data. */ |
1474 | 0 | if (h5->flags & NC_INDEF) |
1475 | 0 | { |
1476 | 0 | if (h5->cmode & NC_CLASSIC_MODEL) |
1477 | 0 | return NC_EINDEFINE; |
1478 | 0 | if ((retval = ncz_enddef_netcdf4_file(h5))) |
1479 | 0 | return THROW(retval); |
1480 | 0 | } |
1481 | | |
1482 | 0 | return NC_NOERR; |
1483 | 0 | } |
1484 | | |
1485 | | #ifdef LOGGING |
1486 | | /** |
1487 | | * @intarnal Print some debug info about dimensions to the log. |
1488 | | */ |
1489 | | static void |
1490 | | log_dim_info(NC_VAR_INFO_T *var, size64_t *fdims, size64_t *fmaxdims, |
1491 | | size64_t *start, size64_t *count) |
1492 | | { |
1493 | | int d2; |
1494 | | |
1495 | | /* Print some debugging info... */ |
1496 | | LOG((4, "%s: var name %s ndims %d", __func__, var->hdr.name, var->ndims)); |
1497 | | LOG((4, "File space, and requested:")); |
1498 | | for (d2 = 0; d2 < var->ndims; d2++) |
1499 | | { |
1500 | | LOG((4, "fdims[%d]=%Ld fmaxdims[%d]=%Ld", d2, fdims[d2], d2, |
1501 | | fmaxdims[d2])); |
1502 | | LOG((4, "start[%d]=%Ld count[%d]=%Ld", d2, start[d2], d2, count[d2])); |
1503 | | } |
1504 | | } |
1505 | | #endif /* LOGGING */ |
1506 | | |
1507 | | /** |
1508 | | * @internal Write a strided array of data to a variable. This is |
1509 | | * called by nc_put_vars() and other nc_put_vars_* functions, for |
1510 | | * netCDF-4 files. Also the nc_put_vara() calls end up calling this |
1511 | | * with a NULL stride parameter. |
1512 | | * |
1513 | | * @param ncid File ID. |
1514 | | * @param varid Variable ID. |
1515 | | * @param startp Array of start indices. Must always be provided by |
1516 | | * caller for non-scalar vars. |
1517 | | * @param countp Array of counts. Will default to counts of full |
1518 | | * dimension size if NULL. |
1519 | | * @param stridep Array of strides. Will default to strides of 1 if |
1520 | | * NULL. |
1521 | | * @param data The data to be written. |
1522 | | * @param mem_nc_type The type of the data in memory. |
1523 | | * |
1524 | | * @returns ::NC_NOERR No error. |
1525 | | * @returns ::NC_EBADID Bad ncid. |
1526 | | * @returns ::NC_ENOTVAR Var not found. |
1527 | | * @returns ::NC_EHDFERR ZARR function returned error. |
1528 | | * @returns ::NC_EINVALCOORDS Incorrect start. |
1529 | | * @returns ::NC_EEDGE Incorrect start/count. |
1530 | | * @returns ::NC_ENOMEM Out of memory. |
1531 | | * @returns ::NC_EMPI MPI library error (parallel only) |
1532 | | * @returns ::NC_ECANTEXTEND Can't extend dimension for write. |
1533 | | * @returns ::NC_ERANGE Data conversion error. |
1534 | | * @author Dennis Heimbigner, Ed Hartnett |
1535 | | */ |
1536 | | int |
1537 | | NCZ_put_vars(int ncid, int varid, const size_t *startp, const size_t *countp, |
1538 | | const ptrdiff_t *stridep, const void *data, nc_type mem_nc_type) |
1539 | 0 | { |
1540 | 0 | NC_GRP_INFO_T *grp; |
1541 | 0 | NC_FILE_INFO_T *h5; |
1542 | 0 | NC_VAR_INFO_T *var; |
1543 | 0 | NC_DIM_INFO_T *dim; |
1544 | | #ifdef LOOK |
1545 | | hid_t file_spaceid = 0, mem_spaceid = 0, xfer_plistid = 0; |
1546 | | #endif |
1547 | 0 | size64_t fdims[NC_MAX_VAR_DIMS]; |
1548 | 0 | size64_t start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS]; |
1549 | 0 | size64_t stride[NC_MAX_VAR_DIMS], ones[NC_MAX_VAR_DIMS]; |
1550 | 0 | int retval, range_error = 0, i, d2; |
1551 | 0 | void *bufr = NULL; |
1552 | 0 | int bufrd = 0; /* 1 => we allocated bufr */ |
1553 | 0 | int need_to_convert = 0; |
1554 | 0 | int zero_count = 0; /* true if a count is zero */ |
1555 | 0 | size_t len = 1; |
1556 | 0 | size64_t fmaxdims[NC_MAX_VAR_DIMS]; |
1557 | 0 | NCZ_VAR_INFO_T* zvar; |
1558 | |
|
1559 | 0 | NC_UNUSED(fmaxdims); |
1560 | |
|
1561 | 0 | #ifndef LOOK |
1562 | 0 | NC_UNUSED(fmaxdims); |
1563 | 0 | #endif |
1564 | | |
1565 | | /* Find info for this file, group, and var. */ |
1566 | 0 | if ((retval = nc4_find_grp_h5_var(ncid, varid, &h5, &grp, &var))) |
1567 | 0 | return THROW(retval); |
1568 | 0 | assert(h5 && grp && var && var->hdr.id == varid && var->format_var_info); |
1569 | |
|
1570 | 0 | LOG((3, "%s: var->hdr.name %s mem_nc_type %d", __func__, |
1571 | 0 | var->hdr.name, mem_nc_type)); |
1572 | |
|
1573 | 0 | if(h5->no_write) |
1574 | 0 | return NC_EPERM; |
1575 | | |
1576 | 0 | zvar = (NCZ_VAR_INFO_T*)var->format_var_info; |
1577 | | |
1578 | | /* Cannot convert to user-defined types. */ |
1579 | 0 | if (mem_nc_type >= NC_FIRSTUSERTYPEID) |
1580 | 0 | return THROW(NC_EINVAL); |
1581 | | |
1582 | | /* Check some stuff about the type and the file. If the file must |
1583 | | * be switched from define mode, it happens here. */ |
1584 | 0 | if ((retval = check_for_vara(&mem_nc_type, var, h5))) |
1585 | 0 | return THROW(retval); |
1586 | 0 | assert(!var->ndims || (startp && countp)); |
1587 | | |
1588 | | /* Convert from size_t and ptrdiff_t to size64_t */ |
1589 | | /* Also do sanity checks */ |
1590 | 0 | if(var->ndims == 0) { /* scalar */ |
1591 | 0 | start[0] = 0; |
1592 | 0 | count[0] = 1; |
1593 | 0 | stride[0] = 1; |
1594 | 0 | ones[0] = 1; |
1595 | 0 | } else { |
1596 | 0 | for (i = 0; i < var->ndims; i++) |
1597 | 0 | { |
1598 | | /* Check for non-positive stride. */ |
1599 | 0 | if (stridep && stridep[i] <= 0) |
1600 | 0 | return NC_ESTRIDE; |
1601 | | |
1602 | 0 | fdims[i] = var->dim[i]->len; |
1603 | 0 | start[i] = startp[i]; |
1604 | 0 | count[i] = countp ? countp[i] : fdims[i]; |
1605 | 0 | stride[i] = stridep ? stridep[i] : 1; |
1606 | 0 | ones[i] = 1; |
1607 | | |
1608 | | /* Check to see if any counts are zero. */ |
1609 | 0 | if (!count[i]) |
1610 | 0 | zero_count++; |
1611 | 0 | } |
1612 | 0 | } |
1613 | | |
1614 | | |
1615 | | #ifdef LOGGING |
1616 | | log_dim_info(var, fdims, fdims, start, count); |
1617 | | #endif |
1618 | | |
1619 | | /* Check dimension bounds. Remember that unlimited dimensions can |
1620 | | * put data beyond their current length. */ |
1621 | 0 | for (d2 = 0; d2 < var->ndims; d2++) |
1622 | 0 | { |
1623 | 0 | size64_t endindex = start[d2] + stride[d2] * (count[d2] - 1); /* last index written */ |
1624 | 0 | dim = var->dim[d2]; |
1625 | 0 | assert(dim && dim->hdr.id == var->dimids[d2]); |
1626 | 0 | if (count[d2] == 0) |
1627 | 0 | endindex = start[d2]; /* fixup for zero read count */ |
1628 | 0 | if (!dim->unlimited) |
1629 | 0 | { |
1630 | | /* Allow start to equal dim size if count is zero. */ |
1631 | 0 | if (start[d2] > fdims[d2] || (start[d2] == fdims[d2] && count[d2] > 0)) |
1632 | 0 | BAIL_QUIET(NC_EINVALCOORDS); |
1633 | 0 | if (!zero_count && endindex >= fdims[d2]) |
1634 | 0 | BAIL_QUIET(NC_EEDGE); |
1635 | 0 | } |
1636 | 0 | } |
1637 | | |
1638 | | |
1639 | | #ifdef LOOK |
1640 | | /* Now you would think that no one would be crazy enough to write |
1641 | | a scalar dataspace with one of the array function calls, but you |
1642 | | would be wrong. So let's check to see if the dataset is |
1643 | | scalar. If it is, we won't try to set up a hyperslab. */ |
1644 | | if (H5Sget_simple_extent_type(file_spaceid) == H5S_SCALAR) |
1645 | | { |
1646 | | if ((mem_spaceid = H5Screate(H5S_SCALAR)) < 0) |
1647 | | BAIL(NC_EHDFERR); |
1648 | | } |
1649 | | else |
1650 | | { |
1651 | | if (H5Sselect_hyperslab(file_spaceid, H5S_SELECT_SET, start, stride, |
1652 | | ones, count) < 0) |
1653 | | BAIL(NC_EHDFERR); |
1654 | | |
1655 | | /* Create a space for the memory, just big enough to hold the slab |
1656 | | we want. */ |
1657 | | if ((mem_spaceid = H5Screate_simple(var->ndims, count, NULL)) < 0) |
1658 | | BAIL(NC_EHDFERR); |
1659 | | } |
1660 | | #endif |
1661 | | |
1662 | | |
1663 | | /* Are we going to convert any data? (No converting of compound or |
1664 | | * opaque or vlen types.) We also need to call this code if we are doing |
1665 | | * quantization. */ |
1666 | 0 | if ((mem_nc_type != var->type_info->hdr.id && |
1667 | 0 | mem_nc_type != NC_COMPOUND && mem_nc_type != NC_OPAQUE |
1668 | 0 | && mem_nc_type != NC_VLEN) |
1669 | 0 | || var->quantize_mode > 0) |
1670 | 0 | { |
1671 | 0 | size_t file_type_size; |
1672 | | |
1673 | | /* We must convert - allocate a buffer. */ |
1674 | 0 | need_to_convert++; |
1675 | 0 | if(zvar->scalar) |
1676 | 0 | len = 1; |
1677 | 0 | else for (d2=0; d2<var->ndims; d2++) |
1678 | 0 | len *= countp[d2]; |
1679 | |
|
1680 | 0 | LOG((4, "converting data for var %s type=%d len=%d", var->hdr.name, |
1681 | 0 | var->type_info->hdr.id, len)); |
1682 | | |
1683 | | /* Later on, we will need to know the size of this type in the |
1684 | | * file. */ |
1685 | 0 | assert(var->type_info->size); |
1686 | 0 | file_type_size = var->type_info->size; |
1687 | | |
1688 | | /* If we're reading, we need bufr to have enough memory to store |
1689 | | * the data in the file. If we're writing, we need bufr to be |
1690 | | * big enough to hold all the data in the file's type. */ |
1691 | 0 | if (len > 0) { |
1692 | 0 | assert(bufr == NULL); |
1693 | 0 | if (!(bufr = malloc(len * file_type_size))) |
1694 | 0 | BAIL(NC_ENOMEM); |
1695 | 0 | bufrd = 1; |
1696 | 0 | } |
1697 | 0 | } |
1698 | 0 | else |
1699 | 0 | bufr = (void *)data; |
1700 | | |
1701 | | /* Write this hyperslab from memory to file. Does the dataset have to be |
1702 | | extended? If it's already extended to the required size, it will |
1703 | | do no harm to reextend it to that size. */ |
1704 | 0 | if (var->ndims) |
1705 | 0 | { |
1706 | |
|
1707 | 0 | for (d2 = 0; d2 < var->ndims; d2++) |
1708 | 0 | { |
1709 | 0 | size64_t endindex = start[d2] + stride[d2] * (count[d2] - 1); /* last index written */ |
1710 | 0 | if (count[d2] == 0) |
1711 | 0 | endindex = start[d2]; |
1712 | 0 | dim = var->dim[d2]; |
1713 | 0 | assert(dim && dim->hdr.id == var->dimids[d2]); |
1714 | 0 | if (dim->unlimited) |
1715 | 0 | { |
1716 | 0 | if (!zero_count && endindex >= fdims[d2]) |
1717 | 0 | { |
1718 | 0 | dim->len = (endindex+1); |
1719 | 0 | } |
1720 | 0 | else |
1721 | 0 | dim->len = fdims[d2]; |
1722 | |
|
1723 | 0 | if (!zero_count && endindex >= dim->len) |
1724 | 0 | { |
1725 | 0 | dim->len = endindex+1; |
1726 | 0 | dim->extended = NC_TRUE; |
1727 | 0 | } |
1728 | 0 | } |
1729 | 0 | } |
1730 | | |
1731 | | |
1732 | |
|
1733 | | #ifdef LOOK |
1734 | | /* If we need to extend it, we also need a new file_spaceid |
1735 | | to reflect the new size of the space. */ |
1736 | | if (need_to_extend) |
1737 | | { |
1738 | | LOG((4, "extending dataset")); |
1739 | | /* Convert xtend_size back to hsize_t for use with |
1740 | | * H5Dset_extent. */ |
1741 | | for (d2 = 0; d2 < var->ndims; d2++) |
1742 | | fdims[d2] = (size64_t)xtend_size[d2]; |
1743 | | if (H5Dset_extent(ncz_var->hdf_datasetid, fdims) < 0) |
1744 | | BAIL(NC_EHDFERR); |
1745 | | if (file_spaceid > 0 && H5Sclose(file_spaceid) < 0) |
1746 | | BAIL2(NC_EHDFERR); |
1747 | | if ((file_spaceid = H5Dget_space(ncz_var->hdf_datasetid)) < 0) |
1748 | | BAIL(NC_EHDFERR); |
1749 | | if (H5Sselect_hyperslab(file_spaceid, H5S_SELECT_SET, |
1750 | | start, stride, ones, count) < 0) |
1751 | | BAIL(NC_EHDFERR); |
1752 | | } |
1753 | | #endif |
1754 | 0 | } |
1755 | | |
1756 | | /* Do we need to convert the data? */ |
1757 | 0 | if (need_to_convert) |
1758 | 0 | { |
1759 | 0 | if(var->quantize_mode < 0) {if((retval = NCZ_ensure_quantizer(ncid,var))) BAIL(retval);} |
1760 | 0 | assert(bufr != NULL); |
1761 | 0 | if ((retval = nc4_convert_type(data, bufr, mem_nc_type, var->type_info->hdr.id, |
1762 | 0 | len, &range_error, var->fill_value, |
1763 | 0 | (h5->cmode & NC_CLASSIC_MODEL), |
1764 | 0 | var->quantize_mode, var->nsd))) |
1765 | 0 | BAIL(retval); |
1766 | 0 | } |
1767 | | |
1768 | | #ifdef LOOK |
1769 | | /* Write the data. At last! */ |
1770 | | LOG((4, "about to write datasetid 0x%x mem_spaceid 0x%x " |
1771 | | "file_spaceid 0x%x", ncz_var->hdf_datasetid, mem_spaceid, file_spaceid)); |
1772 | | if (H5Dwrite(ncz_var->hdf_datasetid, |
1773 | | ((NCZ_TYPE_INFO_T *)var->type_info->format_type_info)->hdf_typeid, |
1774 | | mem_spaceid, file_spaceid, xfer_plistid, bufr) < 0) |
1775 | | BAIL(NC_EHDFERR); |
1776 | | #endif /*LOOK*/ |
1777 | | |
1778 | 0 | if((retval = NCZ_transferslice(var, WRITING, start, count, stride, bufr, var->type_info->hdr.id))) |
1779 | 0 | BAIL(retval); |
1780 | | |
1781 | | /* Remember that we have written to this var so that Fill Value |
1782 | | * can't be set for it. */ |
1783 | 0 | if (!var->written_to) |
1784 | 0 | var->written_to = NC_TRUE; |
1785 | | |
1786 | | /* For strict netcdf-3 rules, ignore erange errors between UBYTE |
1787 | | * and BYTE types. */ |
1788 | 0 | if ((h5->cmode & NC_CLASSIC_MODEL) && |
1789 | 0 | (var->type_info->hdr.id == NC_UBYTE || var->type_info->hdr.id == NC_BYTE) && |
1790 | 0 | (mem_nc_type == NC_UBYTE || mem_nc_type == NC_BYTE) && |
1791 | 0 | range_error) |
1792 | 0 | range_error = 0; |
1793 | |
|
1794 | 0 | exit: |
1795 | | #ifdef LOOK |
1796 | | if (file_spaceid > 0 && H5Sclose(file_spaceid) < 0) |
1797 | | BAIL2(NC_EHDFERR); |
1798 | | if (mem_spaceid > 0 && H5Sclose(mem_spaceid) < 0) |
1799 | | BAIL2(NC_EHDFERR); |
1800 | | if (xfer_plistid && (H5Pclose(xfer_plistid) < 0)) |
1801 | | BAIL2(NC_EPARINIT); |
1802 | | #endif |
1803 | 0 | if (bufrd && bufr) free(bufr); |
1804 | | |
1805 | | /* If there was an error return it, otherwise return any potential |
1806 | | range error value. If none, return NC_NOERR as usual.*/ |
1807 | 0 | if (retval) |
1808 | 0 | return THROW(retval); |
1809 | 0 | if (range_error) |
1810 | 0 | return NC_ERANGE; |
1811 | 0 | return NC_NOERR; |
1812 | 0 | } |
1813 | | |
1814 | | /** |
1815 | | * @internal Read a strided array of data from a variable. This is |
1816 | | * called by nc_get_vars() for netCDF-4 files, as well as all the |
1817 | | * other nc_get_vars_* functions. |
1818 | | * |
1819 | | * @param ncid File ID. |
1820 | | * @param varid Variable ID. |
1821 | | * @param startp Array of start indices. Must be provided for |
1822 | | * non-scalar vars. |
1823 | | * @param countp Array of counts. Will default to counts of extent of |
1824 | | * dimension if NULL. |
1825 | | * @param stridep Array of strides. Will default to strides of 1 if |
1826 | | * NULL. |
1827 | | * @param data The data to be written. |
1828 | | * @param mem_nc_type The type of the data in memory. (Convert to this |
1829 | | * type from file type.) |
1830 | | * |
1831 | | * @returns ::NC_NOERR No error. |
1832 | | * @returns ::NC_EBADID Bad ncid. |
1833 | | * @returns ::NC_ENOTVAR Var not found. |
1834 | | * @returns ::NC_EHDFERR ZARR function returned error. |
1835 | | * @returns ::NC_EINVALCOORDS Incorrect start. |
1836 | | * @returns ::NC_EEDGE Incorrect start/count. |
1837 | | * @returns ::NC_ENOMEM Out of memory. |
1838 | | * @returns ::NC_EMPI MPI library error (parallel only) |
1839 | | * @returns ::NC_ECANTEXTEND Can't extend dimension for write. |
1840 | | * @returns ::NC_ERANGE Data conversion error. |
1841 | | * @author Dennis Heimbigner, Ed Hartnett |
1842 | | */ |
1843 | | int |
1844 | | NCZ_get_vars(int ncid, int varid, const size_t *startp, const size_t *countp, |
1845 | | const ptrdiff_t *stridep, void *data, nc_type mem_nc_type) |
1846 | 0 | { |
1847 | 0 | NC_GRP_INFO_T *grp; |
1848 | 0 | NC_FILE_INFO_T *h5; |
1849 | 0 | NC_VAR_INFO_T *var; |
1850 | 0 | NC_DIM_INFO_T *dim; |
1851 | 0 | size_t file_type_size; |
1852 | 0 | size64_t count[NC_MAX_VAR_DIMS]; |
1853 | 0 | size64_t fdims[NC_MAX_VAR_DIMS]; /* size of the dimensions */ |
1854 | 0 | size64_t start[NC_MAX_VAR_DIMS]; |
1855 | 0 | size64_t stride[NC_MAX_VAR_DIMS]; |
1856 | 0 | size64_t ones[NC_MAX_VAR_DIMS]; |
1857 | 0 | int no_read = 0, provide_fill = 0; |
1858 | 0 | int fill_value_size[NC_MAX_VAR_DIMS]; |
1859 | 0 | int retval, range_error = 0, i, d2; |
1860 | 0 | void *bufr = NULL; |
1861 | 0 | int need_to_convert = 0; |
1862 | 0 | size_t len = 1; |
1863 | 0 | NCZ_VAR_INFO_T* zvar = NULL; |
1864 | | |
1865 | | /* Find info for this file, group, and var. */ |
1866 | 0 | if ((retval = nc4_find_grp_h5_var(ncid, varid, &h5, &grp, &var))) |
1867 | 0 | return THROW(retval); |
1868 | 0 | assert(h5 && grp && var && var->hdr.id == varid && var->format_var_info && |
1869 | 0 | var->type_info && var->type_info->size && |
1870 | 0 | var->type_info->format_type_info); |
1871 | |
|
1872 | 0 | LOG((3, "%s: var->hdr.name %s mem_nc_type %d", __func__, |
1873 | 0 | var->hdr.name, mem_nc_type)); |
1874 | |
|
1875 | 0 | zvar = (NCZ_VAR_INFO_T*)var->format_var_info; |
1876 | | |
1877 | | /* Check some stuff about the type and the file. Also end define |
1878 | | * mode, if needed. */ |
1879 | 0 | if ((retval = check_for_vara(&mem_nc_type, var, h5))) |
1880 | 0 | return THROW(retval); |
1881 | 0 | assert((!var->ndims || (startp && countp))); |
1882 | | |
1883 | | /* Convert from size_t and ptrdiff_t to size64_t. Also do sanity |
1884 | | * checks. */ |
1885 | 0 | if(var->ndims == 0) { /* scalar */ |
1886 | 0 | start[0] = 0; |
1887 | 0 | count[0] = 1; |
1888 | 0 | stride[0] = 1; |
1889 | 0 | ones[0] = 1; |
1890 | 0 | } else { |
1891 | 0 | for (i = 0; i < var->ndims; i++) |
1892 | 0 | { |
1893 | | /* If any of the stride values are non-positive, fail. */ |
1894 | 0 | if (stridep && stridep[i] <= 0) |
1895 | 0 | return NC_ESTRIDE; |
1896 | 0 | start[i] = startp[i]; |
1897 | 0 | count[i] = countp[i]; |
1898 | 0 | stride[i] = stridep ? stridep[i] : 1; |
1899 | |
|
1900 | 0 | ones[i] = 1; |
1901 | | /* if any of the count values are zero don't actually read. */ |
1902 | 0 | if (count[i] == 0) |
1903 | 0 | no_read++; |
1904 | | |
1905 | | /* Get dimension sizes also */ |
1906 | 0 | fdims[i] = var->dim[i]->len; |
1907 | | /* if any of the counts are zero don't actually read. */ |
1908 | 0 | if (count[i] == 0) |
1909 | 0 | no_read++; |
1910 | 0 | } |
1911 | 0 | } |
1912 | | |
1913 | | #ifdef LOGGING |
1914 | | log_dim_info(var, fdims, fdims, start, count); |
1915 | | #endif |
1916 | | |
1917 | | /* Check the type_info fields. */ |
1918 | 0 | assert(var->type_info && var->type_info->size && |
1919 | 0 | var->type_info->format_type_info); |
1920 | | |
1921 | | /* Later on, we will need to know the size of this type in the |
1922 | | * file. */ |
1923 | 0 | file_type_size = var->type_info->size; |
1924 | | |
1925 | | /* Are we going to convert any data? (No converting of compound or |
1926 | | * opaque types.) */ |
1927 | 0 | if (mem_nc_type != var->type_info->hdr.id && |
1928 | 0 | mem_nc_type != NC_COMPOUND && mem_nc_type != NC_OPAQUE) |
1929 | 0 | { |
1930 | | /* We must convert - allocate a buffer. */ |
1931 | 0 | need_to_convert++; |
1932 | 0 | if(zvar->scalar) { |
1933 | 0 | len *= countp[0]; |
1934 | 0 | } else { |
1935 | 0 | for (d2 = 0; d2 < (var->ndims); d2++) |
1936 | 0 | len *= countp[d2]; |
1937 | 0 | } |
1938 | 0 | LOG((4, "converting data for var %s type=%d len=%d", var->hdr.name, |
1939 | 0 | var->type_info->hdr.id, len)); |
1940 | | |
1941 | | /* If we're reading, we need bufr to have enough memory to store |
1942 | | * the data in the file. If we're writing, we need bufr to be |
1943 | | * big enough to hold all the data in the file's type. */ |
1944 | 0 | if (len > 0) |
1945 | 0 | if (!(bufr = malloc(len * file_type_size))) |
1946 | 0 | BAIL(NC_ENOMEM); |
1947 | 0 | } |
1948 | 0 | else |
1949 | 0 | if (!bufr) |
1950 | 0 | bufr = data; |
1951 | | |
1952 | | /* Check dimension bounds. Remember that unlimited dimensions can |
1953 | | * read/write data beyond their largest current length. */ |
1954 | 0 | for (d2 = 0; d2 < var->ndims; d2++) |
1955 | 0 | { |
1956 | 0 | size64_t endindex = start[d2] + stride[d2] * (count[d2] - 1); /* last index read */ |
1957 | 0 | dim = var->dim[d2]; |
1958 | 0 | assert(dim && dim->hdr.id == var->dimids[d2]); |
1959 | 0 | if (count[d2] == 0) |
1960 | 0 | endindex = start[d2]; /* fixup for zero read count */ |
1961 | 0 | if (dim->unlimited) |
1962 | 0 | { |
1963 | 0 | size64_t ulen = (size64_t)dim->len; |
1964 | | /* Check for out of bound requests. */ |
1965 | | /* Allow start to equal dim size if count is zero. */ |
1966 | 0 | if (start[d2] > ulen || (start[d2] == ulen && count[d2] > 0)) |
1967 | 0 | BAIL_QUIET(NC_EINVALCOORDS); |
1968 | 0 | if (count[d2] && endindex >= ulen) |
1969 | 0 | BAIL_QUIET(NC_EEDGE); |
1970 | | |
1971 | | /* Things get a little tricky here. If we're getting a GET |
1972 | | request beyond the end of this var's current length in |
1973 | | an unlimited dimension, we'll later need to return the |
1974 | | fill value for the variable. */ |
1975 | 0 | if (!no_read) |
1976 | 0 | { |
1977 | 0 | if (start[d2] >= (size64_t)fdims[d2]) |
1978 | 0 | fill_value_size[d2] = count[d2]; |
1979 | 0 | else if (endindex >= fdims[d2]) |
1980 | 0 | fill_value_size[d2] = count[d2] - ((fdims[d2] - start[d2])/stride[d2]); |
1981 | 0 | else |
1982 | 0 | fill_value_size[d2] = 0; |
1983 | 0 | count[d2] -= fill_value_size[d2]; |
1984 | 0 | if (count[d2] == 0) |
1985 | 0 | no_read++; |
1986 | 0 | if (fill_value_size[d2]) |
1987 | 0 | provide_fill++; |
1988 | 0 | } |
1989 | 0 | else |
1990 | 0 | fill_value_size[d2] = count[d2]; |
1991 | 0 | } |
1992 | 0 | else /* Dim is not unlimited. */ |
1993 | 0 | { |
1994 | | /* Check for out of bound requests. */ |
1995 | | /* Allow start to equal dim size if count is zero. */ |
1996 | 0 | if (start[d2] > (size64_t)fdims[d2] || |
1997 | 0 | (start[d2] == (size64_t)fdims[d2] && count[d2] > 0)) |
1998 | 0 | BAIL_QUIET(NC_EINVALCOORDS); |
1999 | 0 | if (count[d2] && endindex >= fdims[d2]) |
2000 | 0 | BAIL_QUIET(NC_EEDGE); |
2001 | | /* Set the fill value boundary */ |
2002 | 0 | fill_value_size[d2] = count[d2]; |
2003 | 0 | } |
2004 | 0 | } |
2005 | | |
2006 | 0 | if (!no_read) |
2007 | 0 | { |
2008 | | #ifdef LOOK |
2009 | | /* Now you would think that no one would be crazy enough to write |
2010 | | a scalar dataspace with one of the array function calls, but you |
2011 | | would be wrong. So let's check to see if the dataset is |
2012 | | scalar. If it is, we won't try to set up a hyperslab. */ |
2013 | | if (H5Sget_simple_extent_type(file_spaceid) == H5S_SCALAR) |
2014 | | { |
2015 | | if ((mem_spaceid = H5Screate(H5S_SCALAR)) < 0) |
2016 | | BAIL(NC_EHDFERR); |
2017 | | scalar++; |
2018 | | } |
2019 | | else |
2020 | | { |
2021 | | if (H5Sselect_hyperslab(file_spaceid, H5S_SELECT_SET, |
2022 | | start, stride, ones, count) < 0) |
2023 | | BAIL(NC_EHDFERR); |
2024 | | /* Create a space for the memory, just big enough to hold the slab |
2025 | | we want. */ |
2026 | | if ((mem_spaceid = H5Screate_simple(var->ndims, count, NULL)) < 0) |
2027 | | BAIL(NC_EHDFERR); |
2028 | | } |
2029 | | #endif |
2030 | |
|
2031 | | #ifdef LOOK |
2032 | | /* Fix bug when reading ZARR files with variable of type |
2033 | | * fixed-length string. We need to make it look like a |
2034 | | * variable-length string, because that's all netCDF-4 data |
2035 | | * model supports, lacking anonymous dimensions. So |
2036 | | * variable-length strings are in allocated memory that user has |
2037 | | * to free, which we allocate here. */ |
2038 | | if (var->type_info->nc_type_class == NC_STRING && |
2039 | | H5Tget_size(ncz_type->hdf_typeid) > 1 && |
2040 | | !H5Tis_variable_str(ncz_type->hdf_typeid)) |
2041 | | { |
2042 | | size64_t fstring_len; |
2043 | | |
2044 | | if ((fstring_len = H5Tget_size(ncz_type->hdf_typeid)) == 0) |
2045 | | BAIL(NC_EHDFERR); |
2046 | | if (!(*(char **)data = malloc(1 + fstring_len))) |
2047 | | BAIL(NC_ENOMEM); |
2048 | | bufr = *(char **)data; |
2049 | | } |
2050 | | #endif |
2051 | |
|
2052 | | #ifdef LOOK |
2053 | | /* Create the data transfer property list. */ |
2054 | | if ((xfer_plistid = H5Pcreate(H5P_DATASET_XFER)) < 0) |
2055 | | BAIL(NC_EHDFERR); |
2056 | | |
2057 | | /* Read this hyperslab into memory. */ |
2058 | | LOG((5, "About to H5Dread some data...")); |
2059 | | if (H5Dread(ncz_var->hdf_datasetid, |
2060 | | ((NCZ_TYPE_INFO_T *)var->type_info->format_type_info)->native_hdf_typeid, |
2061 | | mem_spaceid, file_spaceid, xfer_plistid, bufr) < 0) |
2062 | | BAIL(NC_EHDFERR); |
2063 | | #endif /*LOOK*/ |
2064 | |
|
2065 | 0 | if((retval = NCZ_transferslice(var, READING, start, count, stride, bufr, var->type_info->hdr.id))) |
2066 | 0 | BAIL(retval); |
2067 | 0 | } /* endif ! no_read */ |
2068 | | |
2069 | | /* Now we need to fake up any further data that was asked for, |
2070 | | using the fill values instead. First skip past the data we |
2071 | | just read, if any. */ |
2072 | 0 | if (!zvar->scalar && provide_fill) |
2073 | 0 | { |
2074 | 0 | void *filldata; |
2075 | 0 | size_t real_data_size = 0; |
2076 | 0 | size_t fill_len; |
2077 | | |
2078 | | /* Skip past the real data we've already read. */ |
2079 | 0 | if (!no_read) |
2080 | 0 | for (real_data_size = file_type_size, d2 = 0; d2 < var->ndims; d2++) |
2081 | 0 | real_data_size *= count[d2]; |
2082 | | |
2083 | | /* Get the fill value from the ZARR variable. Memory will be |
2084 | | * allocated. */ |
2085 | 0 | if (NCZ_ensure_fill_value(var)) |
2086 | 0 | BAIL(NC_EINVAL); |
2087 | | |
2088 | | /* How many fill values do we need? */ |
2089 | 0 | for (fill_len = 1, d2 = 0; d2 < var->ndims; d2++) |
2090 | 0 | fill_len *= (fill_value_size[d2] ? fill_value_size[d2] : 1); |
2091 | | |
2092 | | /* Copy the fill value into the rest of the data buffer. */ |
2093 | 0 | filldata = (char *)data + real_data_size; |
2094 | 0 | for (i = 0; i < fill_len; i++) |
2095 | 0 | { |
2096 | | /* Copy one instance of the fill_value */ |
2097 | 0 | if((retval = NC_copy_data(h5->controller,var->type_info->hdr.id,var->fill_value,1,filldata))) |
2098 | 0 | BAIL(retval); |
2099 | 0 | filldata = (char *)filldata + file_type_size; |
2100 | 0 | } |
2101 | 0 | } |
2102 | | |
2103 | | /* Convert data type if needed. */ |
2104 | 0 | if (need_to_convert) |
2105 | 0 | { |
2106 | 0 | if(var->quantize_mode < 0) {if((retval = NCZ_ensure_quantizer(ncid,var))) BAIL(retval);} |
2107 | 0 | if ((retval = nc4_convert_type(bufr, data, var->type_info->hdr.id, mem_nc_type, |
2108 | 0 | len, &range_error, var->fill_value, |
2109 | 0 | (h5->cmode & NC_CLASSIC_MODEL), var->quantize_mode, |
2110 | 0 | var->nsd))) |
2111 | 0 | BAIL(retval); |
2112 | | /* For strict netcdf-3 rules, ignore erange errors between UBYTE |
2113 | | * and BYTE types. */ |
2114 | 0 | if ((h5->cmode & NC_CLASSIC_MODEL) && |
2115 | 0 | (var->type_info->hdr.id == NC_UBYTE || var->type_info->hdr.id == NC_BYTE) && |
2116 | 0 | (mem_nc_type == NC_UBYTE || mem_nc_type == NC_BYTE) && |
2117 | 0 | range_error) |
2118 | 0 | range_error = 0; |
2119 | 0 | } |
2120 | | |
2121 | 0 | exit: |
2122 | | #ifdef LOOK |
2123 | | if (file_spaceid > 0) |
2124 | | if (H5Sclose(file_spaceid) < 0) |
2125 | | BAIL2(NC_EHDFERR); |
2126 | | if (mem_spaceid > 0) |
2127 | | if (H5Sclose(mem_spaceid) < 0) |
2128 | | BAIL2(NC_EHDFERR); |
2129 | | if (xfer_plistid > 0) |
2130 | | if (H5Pclose(xfer_plistid) < 0) |
2131 | | |
2132 | | BAIL2(NC_EHDFERR); |
2133 | | #endif |
2134 | 0 | if (need_to_convert && bufr) |
2135 | 0 | free(bufr); |
2136 | | /* If there was an error return it, otherwise return any potential |
2137 | | range error value. If none, return NC_NOERR as usual.*/ |
2138 | 0 | if (retval) |
2139 | 0 | return THROW(retval); |
2140 | 0 | if (range_error) |
2141 | 0 | return THROW(NC_ERANGE); |
2142 | 0 | return NC_NOERR; |
2143 | 0 | } |
2144 | | |
2145 | | /** |
2146 | | * @internal Get all the information about a variable. Pass NULL for |
2147 | | * whatever you don't care about. |
2148 | | * |
2149 | | * @param ncid File ID. |
2150 | | * @param varid Variable ID. |
2151 | | * @param name Gets name. |
2152 | | * @param xtypep Gets type. |
2153 | | * @param ndimsp Gets number of dims. |
2154 | | * @param dimidsp Gets array of dim IDs. |
2155 | | * @param nattsp Gets number of attributes. |
2156 | | * @param shufflep Gets shuffle setting. |
2157 | | * @param deflatep Gets deflate setting. |
2158 | | * @param deflate_levelp Gets deflate level. |
2159 | | * @param fletcher32p Gets fletcher32 setting. |
2160 | | * @param contiguousp Gets contiguous setting. |
2161 | | * @param chunksizesp Gets chunksizes. |
2162 | | * @param no_fill Gets fill mode. |
2163 | | * @param fill_valuep Gets fill value. |
2164 | | * @param endiannessp Gets one of ::NC_ENDIAN_BIG ::NC_ENDIAN_LITTLE |
2165 | | * @param idp Pointer to memory to store filter id. |
2166 | | * @param nparamsp Pointer to memory to store filter parameter count. |
2167 | | * @param params Pointer to vector of unsigned integers into which |
2168 | | * to store filter parameters. |
2169 | | * |
2170 | | * @returns ::NC_NOERR No error. |
2171 | | * @returns ::NC_EBADID Bad ncid. |
2172 | | * @returns ::NC_ENOTVAR Bad varid. |
2173 | | * @returns ::NC_ENOMEM Out of memory. |
2174 | | * @returns ::NC_EINVAL Invalid input. |
2175 | | * @author Dennis Heimbigner, Ed Hartnett |
2176 | | */ |
2177 | | int |
2178 | | NCZ_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, |
2179 | | int *ndimsp, int *dimidsp, int *nattsp, |
2180 | | int *shufflep, int *unused4, int *unused5, |
2181 | | int *fletcher32p, int *storagep, size_t *chunksizesp, |
2182 | | int *no_fill, void *fill_valuep, int *endiannessp, |
2183 | | unsigned int *unused1, size_t *unused2, unsigned int *unused3) |
2184 | 0 | { |
2185 | 0 | NC_FILE_INFO_T *h5; |
2186 | 0 | NC_GRP_INFO_T *grp; |
2187 | 0 | NC_VAR_INFO_T *var = NULL; |
2188 | 0 | int retval; |
2189 | |
|
2190 | 0 | ZTRACE(1,"ncid=%d varid=%d",ncid,varid); |
2191 | |
|
2192 | 0 | LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); |
2193 | | |
2194 | | /* Find the file, group, and var info, and do lazy att read if |
2195 | | * needed. */ |
2196 | 0 | if ((retval = ncz_find_grp_var_att(ncid, varid, NULL, 0, 0, NULL, |
2197 | 0 | &h5, &grp, &var, NULL))) |
2198 | 0 | goto done; |
2199 | 0 | assert(grp && h5); |
2200 | | |
2201 | | /* Short-circuit the filter-related inquiries */ |
2202 | 0 | if(shufflep) { |
2203 | 0 | *shufflep = 0; |
2204 | 0 | if((retval = NCZ_inq_var_filter_info(ncid,varid,H5Z_FILTER_SHUFFLE,NULL,NULL))==NC_NOERR) |
2205 | 0 | *shufflep = 1; |
2206 | 0 | } |
2207 | 0 | retval = NC_NOERR; /* reset */ |
2208 | |
|
2209 | 0 | if(fletcher32p) { |
2210 | 0 | *fletcher32p = 0; |
2211 | 0 | if((retval = NCZ_inq_var_filter_info(ncid,varid,H5Z_FILTER_FLETCHER32,NULL,NULL))==NC_NOERR) |
2212 | 0 | *fletcher32p = 1; |
2213 | 0 | } |
2214 | 0 | retval = NC_NOERR; /* reset */ |
2215 | | |
2216 | | /* Now that lazy atts have been read, use the libsrc4 function to |
2217 | | * get the answers. */ |
2218 | 0 | retval = NC4_inq_var_all(ncid, varid, name, xtypep, ndimsp, dimidsp, nattsp, |
2219 | 0 | NULL, unused4, unused5, NULL, |
2220 | 0 | storagep, chunksizesp, no_fill, fill_valuep, |
2221 | 0 | endiannessp, unused1, unused2, unused3); |
2222 | 0 | done: |
2223 | 0 | return ZUNTRACEX(retval,"xtype=%d natts=%d shuffle=%d fletcher32=%d no_fill=%d endianness=%d ndims=%d dimids=%s storage=%d chunksizes=%s", |
2224 | 0 | (xtypep?*xtypep:-1), |
2225 | 0 | (nattsp?*nattsp:-1), |
2226 | 0 | (shufflep?*shufflep:-1), |
2227 | 0 | (fletcher32p?*fletcher32p:-1), |
2228 | 0 | (no_fill?*no_fill:-1), |
2229 | 0 | (endiannessp?*endiannessp:-1), |
2230 | 0 | (ndimsp?*ndimsp:-1), |
2231 | 0 | (dimidsp?nczprint_idvector(var->ndims,dimidsp):"null"), |
2232 | 0 | (storagep?*storagep:-1), |
2233 | 0 | (chunksizesp?nczprint_sizevector(var->ndims,chunksizesp):"null")); |
2234 | 0 | } |
2235 | | |
2236 | | #ifdef LOOK |
2237 | | /** |
2238 | | * @internal A wrapper for NCZ_set_var_chunk_cache(), we need this |
2239 | | * version for fortran. Negative values leave settings as they are. |
2240 | | * |
2241 | | * @param ncid File ID. |
2242 | | * @param varid Variable ID. |
2243 | | * @param size Size in bytes to set cache. |
2244 | | * @param nelems Number of elements in cache. |
2245 | | * @param preemption Controls cache swapping. |
2246 | | * |
2247 | | * @returns ::NC_NOERR for success |
2248 | | * @author Dennis Heimbigner, Ed Hartnett |
2249 | | */ |
2250 | | int |
2251 | | ncz_set_var_chunk_cache_ints(int ncid, int varid, int size, int nelems, |
2252 | | int preemption) |
2253 | | { |
2254 | | size_t real_size = H5D_CHUNK_CACHE_NBYTES_DEFAULT; |
2255 | | size_t real_nelems = H5D_CHUNK_CACHE_NSLOTS_DEFAULT; |
2256 | | float real_preemption = CHUNK_CACHE_PREEMPTION; |
2257 | | |
2258 | | if (size >= 0) |
2259 | | real_size = ((size_t) size) * MEGABYTE; |
2260 | | |
2261 | | if (nelems >= 0) |
2262 | | real_nelems = nelems; |
2263 | | |
2264 | | if (preemption >= 0) |
2265 | | real_preemption = preemption / 100.; |
2266 | | |
2267 | | return NCZ_set_var_chunk_cache(ncid, varid, real_size, real_nelems, |
2268 | | real_preemption); |
2269 | | } |
2270 | | #endif |
2271 | | |
2272 | | int |
2273 | | ncz_gettype(NC_FILE_INFO_T* h5, NC_GRP_INFO_T* container, int xtype, NC_TYPE_INFO_T** typep) |
2274 | 0 | { |
2275 | 0 | int retval = NC_NOERR; |
2276 | 0 | NC_TYPE_INFO_T* type = NULL; |
2277 | 0 | NCZ_TYPE_INFO_T* ztype = NULL; |
2278 | | |
2279 | | /* If this is a user-defined type, there is a type struct with |
2280 | | * all the type information. For atomic types, fake up a type |
2281 | | * struct. */ |
2282 | 0 | if (xtype <= NC_STRING) |
2283 | 0 | { |
2284 | 0 | size_t len; |
2285 | 0 | char name[NC_MAX_NAME]; |
2286 | | |
2287 | | /* Get type name and length. */ |
2288 | 0 | if((retval = NC4_inq_atomic_type(xtype,name,&len))) |
2289 | 0 | BAIL(retval); |
2290 | | |
2291 | | /* Create new NC_TYPE_INFO_T struct for this atomic type. */ |
2292 | 0 | if ((retval = nc4_type_new(len, name, xtype, &type))) |
2293 | 0 | BAIL(retval); |
2294 | 0 | assert(type->rc == 0); |
2295 | 0 | type->container = container; |
2296 | 0 | type->endianness = (NC_isLittleEndian()?NC_ENDIAN_LITTLE:NC_ENDIAN_BIG); |
2297 | 0 | type->size = len; |
2298 | | |
2299 | | /* Allocate storage for NCZ-specific type info. */ |
2300 | 0 | if (!(ztype = calloc(1, sizeof(NCZ_TYPE_INFO_T)))) |
2301 | 0 | return NC_ENOMEM; |
2302 | 0 | type->format_type_info = ztype; |
2303 | 0 | ztype->common.file = h5; |
2304 | 0 | ztype = NULL; |
2305 | | |
2306 | | /* Set the "class" of the type */ |
2307 | 0 | if (xtype == NC_CHAR) |
2308 | 0 | type->nc_type_class = NC_CHAR; |
2309 | 0 | else |
2310 | 0 | { |
2311 | 0 | if(xtype == NC_FLOAT || xtype == NC_DOUBLE) |
2312 | 0 | type->nc_type_class = NC_FLOAT; |
2313 | 0 | else if(xtype < NC_STRING) |
2314 | 0 | type->nc_type_class = NC_INT; |
2315 | 0 | else |
2316 | 0 | type->nc_type_class = NC_STRING; |
2317 | 0 | } |
2318 | 0 | } |
2319 | 0 | else |
2320 | 0 | { |
2321 | | #ifdef LOOK |
2322 | | /* If this is a user defined type, find it. */ |
2323 | | if (nc4_find_type(grp->nc4_info, xtype, &type)) |
2324 | | #endif |
2325 | 0 | BAIL(NC_EBADTYPE); |
2326 | 0 | } |
2327 | | |
2328 | | /* increment its ref. count */ |
2329 | 0 | type->rc++; |
2330 | |
|
2331 | 0 | if(typep) {*typep = type; type = NULL;} |
2332 | 0 | return THROW(NC_NOERR); |
2333 | | |
2334 | 0 | exit: |
2335 | 0 | if (type) |
2336 | 0 | retval = nc4_type_free(type); |
2337 | 0 | nullfree(ztype); |
2338 | 0 | return THROW(retval); |
2339 | 0 | } |
2340 | | |
2341 | | #if 0 |
2342 | | /** |
2343 | | Given start+count+stride+dim vectors, determine the largest |
2344 | | index touched per dimension. If that index is greater-than |
2345 | | the dimension size, then do one of two things: |
2346 | | 1. If the dimension is fixed size, then return NC_EDIMSIZE. |
2347 | | 2. If the dimension is unlimited, then extend the size of that |
2348 | | dimension to cover that maximum point. |
2349 | | |
2350 | | @param var |
2351 | | @param start vector |
2352 | | @param count vector |
2353 | | @param stride vector |
2354 | | @param reading vs writing |
2355 | | @return NC_EXXX error code |
2356 | | */ |
2357 | | int |
2358 | | NCZ_update_dim_extents(NC_VAR_INFO_T* var, size64_t* start, size64_t* count, size64_t* stride, int reading) |
2359 | | { |
2360 | | int r; |
2361 | | int rank = var->ndims; |
2362 | | |
2363 | | NC_UNUSED(reading); |
2364 | | |
2365 | | for(r=0;r<rank;r++) { |
2366 | | NC_DIM_INFO_T* dim = var->dim[r]; |
2367 | | size64_t endpoint; /* compute last point touched */ |
2368 | | endpoint = start[r] + stride[r]*count[r] - stride[r]; |
2369 | | if(dim->len < endpoint) { |
2370 | | if(!dim->unlimited) return NC_EDIMSIZE; |
2371 | | /*else*/ dim->len = endpoint+1; |
2372 | | } |
2373 | | } |
2374 | | return NC_NOERR; |
2375 | | } |
2376 | | #endif |