1 /*
2 Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
3 See COPYRIGHT for license information.
4 */
5
6 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7 * Copyright by The HDF Group. *
8 * Copyright by the Board of Trustees of the University of Illinois. *
9 * All rights reserved. *
10 * *
11 * This file is part of HDF5. The full HDF5 copyright notice, including *
12 * terms governing use, modification, and redistribution, is contained in *
13 * the files COPYING and Copyright.html. COPYING can be found at the root *
14 * of the source code distribution tree; Copyright.html can be found at the *
15 * root level of an installed copy of the electronic HDF5 document set and *
16 * is linked from the top-level documents page. It can also be found at *
17 * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
18 * access to either file, you may request a copy from help@hdfgroup.org. *
19 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <assert.h>
24 #include "config.h"
25 #include "netcdf.h"
26 #include "netcdf_aux.h"
27 #include "ncoffsets.h"
28 #include "nclog.h"
29
30 struct NCAUX_FIELD {
31 char* name;
32 nc_type fieldtype;
33 size_t ndims;
34 int dimsizes[NC_MAX_VAR_DIMS];
35 size_t size;
36 size_t offset;
37 size_t alignment;
38 };
39
40 struct NCAUX_CMPD {
41 int ncid;
42 int mode;
43 char* name;
44 size_t nfields;
45 struct NCAUX_FIELD* fields;
46 size_t size;
47 size_t offset; /* cumulative as fields are added */
48 size_t alignment;
49 };
50
51
52 /* It is helpful to have a structure that contains memory and an offset */
53 typedef struct Position{char* memory; ptrdiff_t offset;} Position;
54
55 /* Forward */
56 static int reclaim_datar(int ncid, int xtype, size_t typesize, Position*);
57
58 static int ncaux_initialized = 0;
59
60 #ifdef USE_NETCDF4
61 static int reclaim_usertype(int ncid, int xtype, Position* offset);
62 static int reclaim_compound(int ncid, int xtype, size_t size, size_t nfields, Position* offset);
63 static int reclaim_vlen(int ncid, int xtype, int basetype, Position* offset);
64 static int reclaim_enum(int ncid, int xtype, int basetype, size_t, Position* offset);
65 static int reclaim_opaque(int ncid, int xtype, size_t size, Position* offset);
66
67 static int computefieldinfo(struct NCAUX_CMPD* cmpd);
68 #endif /* USE_NETCDF4 */
69
70 /**************************************************/
71
72 /**
73 Reclaim the output tree of data from a call
74 to e.g. nc_get_vara or the input to e.g. nc_put_vara.
75 This recursively walks the top-level instances to
76 reclaim any nested data such as vlen or strings or such.
77
78 Assumes it is passed a pointer to count instances of xtype.
79 Reclaims any nested data.
80 WARNING: does not reclaim the top-level memory because
81 we do not know how it was allocated.
82
83 Should work for any netcdf format.
84
85 @param ncid file ncid
86 @param xtype type id
87 @param memory to reclaim
88 @param count number of instances of the type in memory
89 @return error code
90 */
91
92 EXTERNL int
ncaux_reclaim_data(int ncid,int xtype,void * memory,size_t count)93 ncaux_reclaim_data(int ncid, int xtype, void* memory, size_t count)
94 {
95 int stat = NC_NOERR;
96 size_t typesize = 0;
97 size_t i;
98 Position offset;
99
100 if(ncid < 0 || xtype < 0
101 || (memory == NULL && count > 0)
102 || xtype == NC_NAT)
103 {stat = NC_EINVAL; goto done;}
104 if(memory == NULL || count == 0)
105 goto done; /* ok, do nothing */
106 if((stat=nc_inq_type(ncid,xtype,NULL,&typesize))) goto done;
107 offset.memory = (char*)memory; /* use char* so we can do pointer arithmetic */
108 offset.offset = 0;
109 for(i=0;i<count;i++) {
110 if((stat=reclaim_datar(ncid,xtype,typesize,&offset))) /* reclaim one instance */
111 break;
112 }
113
114 done:
115 return stat;
116 }
117
118 /* Recursive type walker: reclaim a single instance */
119 static int
reclaim_datar(int ncid,int xtype,size_t typesize,Position * offset)120 reclaim_datar(int ncid, int xtype, size_t typesize, Position* offset)
121 {
122 int stat = NC_NOERR;
123
124 switch (xtype) {
125 case NC_CHAR: case NC_BYTE: case NC_UBYTE:
126 case NC_SHORT: case NC_USHORT:
127 case NC_INT: case NC_UINT: case NC_FLOAT:
128 case NC_INT64: case NC_UINT64: case NC_DOUBLE:
129 offset->offset += typesize;
130 break;
131
132 #ifdef USE_NETCDF4
133 case NC_STRING: {
134 char** sp = (char**)(offset->memory + offset->offset);
135 /* Need to reclaim string */
136 if(*sp != NULL) free(*sp);
137 offset->offset += typesize;
138 } break;
139 default:
140 /* reclaim a user type */
141 stat = reclaim_usertype(ncid,xtype,offset);
142 #else
143 default:
144 stat = NC_ENOTNC4;
145 #endif
146 break;
147 }
148 return stat;
149 }
150
151 #ifdef USE_NETCDF4
152
153 static ptrdiff_t
read_align(ptrdiff_t offset,size_t alignment)154 read_align(ptrdiff_t offset, size_t alignment)
155 {
156 size_t loc_align = (alignment == 0 ? 1 : alignment);
157 size_t delta = (offset % loc_align);
158 if(delta == 0) return offset;
159 return offset + (alignment - delta);
160 }
161
162 static int
reclaim_usertype(int ncid,int xtype,Position * offset)163 reclaim_usertype(int ncid, int xtype, Position* offset)
164 {
165 int stat = NC_NOERR;
166 size_t size;
167 nc_type basetype;
168 size_t nfields;
169 int klass;
170
171 /* Get info about the xtype */
172 stat = nc_inq_user_type(ncid, xtype, NULL, &size, &basetype, &nfields, &klass);
173 switch (klass) {
174 case NC_OPAQUE: stat = reclaim_opaque(ncid,xtype,size,offset); break;
175 case NC_ENUM: stat = reclaim_enum(ncid,xtype,basetype,size,offset); break;
176 case NC_COMPOUND: stat = reclaim_compound(ncid,xtype,size,nfields,offset); break;
177 case NC_VLEN: stat = reclaim_vlen(ncid,xtype,basetype,offset); break;
178 default:
179 stat = NC_EINVAL;
180 break;
181 }
182 return stat;
183 }
184
185 static int
reclaim_vlen(int ncid,int xtype,int basetype,Position * offset)186 reclaim_vlen(int ncid, int xtype, int basetype, Position* offset)
187 {
188 int stat = NC_NOERR;
189 size_t i, basesize;
190 nc_vlen_t* vl = (nc_vlen_t*)(offset->memory+offset->offset);
191
192 /* Get size of the basetype */
193 if((stat=nc_inq_type(ncid,basetype,NULL,&basesize))) goto done;
194 /* Free up each entry in the vlen list */
195 if(vl->p != NULL) {
196 Position voffset;
197 unsigned int alignment = ncaux_type_alignment(basetype,ncid);
198 voffset.memory = vl->p;
199 voffset.offset = 0;
200 for(i=0;i<vl->len;i++) {
201 voffset.offset = read_align(voffset.offset,alignment);
202 if((stat = reclaim_datar(ncid,basetype,basesize,&voffset))) goto done;
203 }
204 offset->offset += sizeof(nc_vlen_t);
205 free(vl->p);
206 }
207
208 done:
209 return stat;
210 }
211
212 static int
reclaim_enum(int ncid,int xtype,int basetype,size_t basesize,Position * offset)213 reclaim_enum(int ncid, int xtype, int basetype, size_t basesize, Position* offset)
214 {
215 /* basically same as an instance of the enum's integer basetype */
216 return reclaim_datar(ncid,basetype,basesize,offset);
217 }
218
219 static int
reclaim_opaque(int ncid,int xtype,size_t opsize,Position * offset)220 reclaim_opaque(int ncid, int xtype, size_t opsize, Position* offset)
221 {
222 /* basically a fixed size sequence of bytes */
223 offset->offset += opsize;
224 return NC_NOERR;
225 }
226
227 static int
reclaim_compound(int ncid,int xtype,size_t cmpdsize,size_t nfields,Position * offset)228 reclaim_compound(int ncid, int xtype, size_t cmpdsize, size_t nfields, Position* offset)
229 {
230 int stat = NC_NOERR;
231 size_t fid, fieldoffset, i, fieldsize, arraycount;
232 int dimsizes[NC_MAX_VAR_DIMS];
233 int ndims;
234 nc_type fieldtype;
235 ptrdiff_t saveoffset;
236
237 saveoffset = offset->offset;
238
239 /* Get info about each field in turn and reclaim it */
240 for(fid=0;fid<nfields;fid++) {
241 unsigned int fieldalignment;
242 /* Get all relevant info about the field */
243 if((stat = nc_inq_compound_field(ncid,xtype,fid,NULL,&fieldoffset, &fieldtype, &ndims, dimsizes))) goto done;
244 fieldalignment = ncaux_type_alignment(fieldtype,ncid);
245 if((stat = nc_inq_type(ncid,fieldtype,NULL,&fieldsize))) goto done;
246 if(ndims == 0) {ndims=1; dimsizes[0]=1;} /* fake the scalar case */
247 /* Align to this field */
248 offset->offset = read_align(offset->offset,fieldalignment);
249 /* compute the total number of elements in the field array */
250 arraycount = 1;
251 for(i=0;i<ndims;i++) arraycount *= dimsizes[i];
252 for(i=0;i<arraycount;i++) {
253 if((stat = reclaim_datar(ncid, fieldtype, fieldsize, offset))) goto done;
254 }
255 }
256 /* Return to beginning of the compound and move |compound| */
257 offset->offset = saveoffset;
258 offset->offset += cmpdsize;
259
260 done:
261 return stat;
262 }
263
264 #endif /*USE_NETCDF4*/
265
266
267 /**************************************************/
268
269 /*
270 This code is a variant of the H5detect.c code from HDF5.
271 Author: D. Heimbigner 10/7/2008
272 */
273
274 EXTERNL int
ncaux_begin_compound(int ncid,const char * name,int alignmode,void ** tagp)275 ncaux_begin_compound(int ncid, const char *name, int alignmode, void** tagp)
276 {
277 #ifdef USE_NETCDF4
278 int status = NC_NOERR;
279 struct NCAUX_CMPD* cmpd = NULL;
280
281 if(tagp) *tagp = NULL;
282
283 cmpd = (struct NCAUX_CMPD*)calloc(1,sizeof(struct NCAUX_CMPD));
284 if(cmpd == NULL) {status = NC_ENOMEM; goto fail;}
285 cmpd->ncid = ncid;
286 cmpd->mode = alignmode;
287 cmpd->nfields = 0;
288 cmpd->name = strdup(name);
289 if(cmpd->name == NULL) {status = NC_ENOMEM; goto fail;}
290
291 if(tagp) {
292 *tagp = (void*)cmpd;
293 } else { /* Error, free cmpd to avoid memory leak. */
294 free(cmpd);
295 }
296 return status;
297
298 fail:
299 ncaux_abort_compound((void*)cmpd);
300 return status;
301 #else
302 return NC_ENOTBUILT;
303 #endif
304 }
305
306 EXTERNL int
ncaux_abort_compound(void * tag)307 ncaux_abort_compound(void* tag)
308 {
309 #ifdef USE_NETCDF4
310 int i;
311 struct NCAUX_CMPD* cmpd = (struct NCAUX_CMPD*)tag;
312 if(cmpd == NULL) goto done;
313 if(cmpd->name) free(cmpd->name);
314 for(i=0;i<cmpd->nfields;i++) {
315 struct NCAUX_FIELD* field = &cmpd->fields[i];
316 if(field->name) free(field->name);
317 }
318 if(cmpd->fields) free(cmpd->fields);
319 free(cmpd);
320
321 done:
322 return NC_NOERR;
323 #else
324 return NC_ENOTBUILT;
325 #endif
326 }
327
328 EXTERNL int
ncaux_add_field(void * tag,const char * name,nc_type field_type,int ndims,const int * dimsizes)329 ncaux_add_field(void* tag, const char *name, nc_type field_type,
330 int ndims, const int* dimsizes)
331 {
332 #ifdef USE_NETCDF4
333 int i;
334 int status = NC_NOERR;
335 struct NCAUX_CMPD* cmpd = (struct NCAUX_CMPD*)tag;
336 struct NCAUX_FIELD* newfields = NULL;
337 struct NCAUX_FIELD* field = NULL;
338
339 if(cmpd == NULL) goto done;
340 if(ndims < 0) {status = NC_EINVAL; goto done;}
341 for(i=0;i<ndims;i++) {
342 if(dimsizes[i] <= 0) {status = NC_EINVAL; goto done;}
343 }
344 if(cmpd->fields == NULL) {
345 newfields = (struct NCAUX_FIELD*)calloc(1,sizeof(struct NCAUX_FIELD));
346 } else {
347 newfields = (struct NCAUX_FIELD*)realloc(cmpd->fields,cmpd->nfields+1*sizeof(struct NCAUX_FIELD));
348 }
349 if(cmpd->fields == NULL) {status = NC_ENOMEM; goto done;}
350 cmpd->fields = newfields;
351 field = &cmpd->fields[cmpd->nfields+1];
352 field->name = strdup(name);
353 field->fieldtype = field_type;
354 if(field->name == NULL) {status = NC_ENOMEM; goto done;}
355 field->ndims = (size_t)ndims;
356 memcpy(field->dimsizes,dimsizes,sizeof(int)*field->ndims);
357 cmpd->nfields++;
358
359 done:
360 if(newfields)
361 free(newfields);
362 return status;
363 #else
364 return NC_ENOTBUILT;
365 #endif
366 }
367
368 EXTERNL int
ncaux_end_compound(void * tag,nc_type * idp)369 ncaux_end_compound(void* tag, nc_type* idp)
370 {
371 #ifdef USE_NETCDF4
372 int i;
373 int status = NC_NOERR;
374 struct NCAUX_CMPD* cmpd = (struct NCAUX_CMPD*)tag;
375
376 if(cmpd == NULL) {status = NC_EINVAL; goto done;}
377
378 /* Compute field and compound info */
379 status = computefieldinfo(cmpd);
380 if(status != NC_NOERR) goto done;
381
382 status = nc_def_compound(cmpd->ncid, cmpd->size, cmpd->name, idp);
383 if(status != NC_NOERR) goto done;
384
385 for(i=0;i<cmpd->nfields;i++) {
386 struct NCAUX_FIELD* field = &cmpd->fields[i];
387 if(field->ndims > 0) {
388 status = nc_insert_compound(cmpd->ncid, *idp, field->name,
389 field->offset, field->fieldtype);
390 } else {
391 status = nc_insert_array_compound(cmpd->ncid, *idp, field->name,
392 field->offset, field->fieldtype,
393 (int)field->ndims,field->dimsizes);
394 }
395 if(status != NC_NOERR) goto done;
396 }
397
398 done:
399 return status;
400 #else
401 return NC_ENOTBUILT;
402 #endif
403 }
404
405 /**************************************************/
406
407 /**
408 @param ncclass - type class for which alignment is requested; excludes ENUM|COMPOUND
409 */
410 EXTERNL size_t
ncaux_class_alignment(int ncclass)411 ncaux_class_alignment(int ncclass)
412 {
413 if(ncclass <= NC_MAX_ATOMIC_TYPE || ncclass == NC_VLEN || ncclass == NC_OPAQUE)
414 return NC_class_alignment(ncclass);
415 nclog(NCLOGERR,"ncaux_class_alignment: class %d; alignment cannot be determermined",ncclass);
416 return 0;
417 }
418
419 /**
420 @param ncid - only needed for a compound type
421 @param xtype - type for which alignment is requested
422 */
423 EXTERNL size_t
ncaux_type_alignment(int xtype,int ncid)424 ncaux_type_alignment(int xtype, int ncid)
425 {
426 if(!ncaux_initialized) {
427 NC_compute_alignments();
428 ncaux_initialized = 1;
429 }
430 if(xtype <= NC_MAX_ATOMIC_TYPE)
431 return NC_class_alignment(xtype); /* type == class */
432 #ifdef USE_NETCDF4
433 else {/* Presumably a user type */
434 int klass = NC_NAT;
435 int stat = nc_inq_user_type(ncid,xtype,NULL,NULL,NULL,NULL,&klass);
436 if(stat) goto done;
437 switch(klass) {
438 case NC_VLEN: return NC_class_alignment(klass);
439 case NC_OPAQUE: return NC_class_alignment(klass);
440 case NC_COMPOUND: {/* get alignment of the first field of the compound */
441 int fieldtype = NC_NAT;
442 if((stat=nc_inq_compound_fieldtype(ncid,xtype,0,&fieldtype))) goto done;
443 return ncaux_type_alignment(fieldtype,ncid); /* may recurse repeatedly */
444 } break;
445 default: break;
446 }
447 }
448
449 done:
450 #endif /*USE_NETCDF4 */
451 return 0; /* fail */
452 }
453
454 #ifdef USE_NETCDF4
455 /* Find first primitive field of a possibly nested sequence of compounds */
456 static nc_type
findfirstfield(int ncid,nc_type xtype)457 findfirstfield(int ncid, nc_type xtype)
458 {
459 int status = NC_NOERR;
460 nc_type fieldtype = xtype;
461 if(xtype <= NC_MAX_ATOMIC_TYPE) goto done;
462
463 status = nc_inq_compound_fieldtype(ncid, xtype, 0, &fieldtype);
464 if(status != NC_NOERR) goto done;
465 fieldtype = findfirstfield(ncid,fieldtype);
466
467 done:
468 return (status == NC_NOERR?fieldtype:NC_NAT);
469 }
470
471 static size_t
getpadding(size_t offset,size_t alignment)472 getpadding(size_t offset, size_t alignment)
473 {
474 size_t rem = (alignment==0?0:(offset % alignment));
475 size_t pad = (rem==0?0:(alignment - rem));
476 return pad;
477 }
478
479 static size_t
dimproduct(size_t ndims,int * dimsizes)480 dimproduct(size_t ndims, int* dimsizes)
481 {
482 int i;
483 size_t product = 1;
484 for(i=0;i<ndims;i++) product *= (size_t)dimsizes[i];
485 return product;
486 }
487
488 static int
computefieldinfo(struct NCAUX_CMPD * cmpd)489 computefieldinfo(struct NCAUX_CMPD* cmpd)
490 {
491 int i;
492 int status = NC_NOERR;
493 size_t offset = 0;
494 size_t totaldimsize;
495
496 /* Assign the sizes for the fields */
497 for(i=0;i<cmpd->nfields;i++) {
498 struct NCAUX_FIELD* field = &cmpd->fields[i];
499 status = nc_inq_type(cmpd->ncid,field->fieldtype,NULL,&field->size);
500 if(status != NC_NOERR) goto done;
501 totaldimsize = dimproduct(field->ndims,field->dimsizes);
502 field->size *= totaldimsize;
503 }
504
505 for(offset=0,i=0;i<cmpd->nfields;i++) {
506 struct NCAUX_FIELD* field = &cmpd->fields[i];
507 int alignment = 0;
508 nc_type firsttype = findfirstfield(cmpd->ncid,field->fieldtype);
509
510 /* only support 'c' alignment for now*/
511 switch (field->fieldtype) {
512 case NC_OPAQUE:
513 field->alignment = 1;
514 break;
515 case NC_ENUM:
516 field->alignment = ncaux_type_alignment(firsttype,cmpd->ncid);
517 break;
518 case NC_VLEN: /*fall thru*/
519 case NC_COMPOUND:
520 field->alignment = ncaux_type_alignment(firsttype,cmpd->ncid);
521 break;
522 default:
523 field->alignment = ncaux_type_alignment(field->fieldtype,cmpd->ncid);
524 break;
525
526 }
527 offset += getpadding(offset,alignment);
528 field->offset = offset;
529 offset += field->size;
530 }
531 cmpd->size = offset;
532 cmpd->alignment = cmpd->fields[0].alignment;
533
534 done:
535 return status;
536 }
537
538 #endif /*USE_NETCDF4*/
539