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