Coverage Report

Created: 2025-10-28 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/netcdf-c/libdispatch/dinstance_intern.c
Line
Count
Source
1
/*
2
Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
3
See COPYRIGHT for license information.
4
*/
5
6
/** \internal
7
This file contains various instance operations that operate
8
on a deep level rather than the shallow level of e.g. nc_free_vlen_t.
9
Currently two operations are defined:
10
1. reclaim a vector of instances
11
2. copy a vector of instances
12
*/
13
14
#include "config.h"
15
#include <stdlib.h>
16
#include <string.h>
17
#include <assert.h>
18
#include "netcdf.h"
19
#include "nc4internal.h"
20
#include "nc4dispatch.h"
21
#include "ncoffsets.h"
22
#include "ncbytes.h"
23
24
#undef REPORT
25
#undef DEBUG
26
27
/* It is helpful to have a structure that identifies a pointer into the overall memory */
28
typedef struct Position{char* memory;} Position;
29
30
static int type_alignment_initialized = 0;
31
32
/* Forward */
33
#ifdef USE_NETCDF4
34
static int reclaim_datar(NC_FILE_INFO_T* file, NC_TYPE_INFO_T* utype, Position instance);
35
static int copy_datar(NC_FILE_INFO_T* file, NC_TYPE_INFO_T* utype, Position src, Position dst);
36
#endif
37
38
int NC_print_data(NC_FILE_INFO_T* file, nc_type xtype, const void* memory, size_t count);
39
40
/**
41
Reclaim a vector of instances of a type.  This recursively walks the
42
top-level instances to reclaim any nested data such as vlen or strings
43
or such.
44
45
Assumes it is passed a pointer to count instances of xtype.
46
Reclaims any nested data.
47
48
These are the internal equivalents of nc_reclaim_data[_all] and
49
nc_copy_data[_all] and as such operate using internal data structures.
50
51
WARNING: DOES NOT RECLAIM THE TOP-LEVEL MEMORY.
52
The reason is that we do not know how it was allocated (e.g. static vs
53
dynamic); only the caller can know that.  But note that it assumes all
54
memory blocks other than the top were dynamically allocated, so they
55
will be free'd.
56
57
Should work for any netcdf type.
58
59
Note that this has been optimized significantly, largely
60
by unwinding various reclaim cliche procedures.
61
62
@param nc NC* structure
63
@param xtype type id
64
@param memory ptr to top-level memory to reclaim
65
@param count number of instances of the type in memory block
66
@return error code
67
*/
68
69
int
70
NC_reclaim_data(NC* nc, nc_type xtype, void* memory, size_t count)
71
0
{
72
0
    int stat = NC_NOERR;
73
0
    size_t i;
74
0
    Position instance;
75
0
    NC_FILE_INFO_T* file = NULL;
76
0
    NC_TYPE_INFO_T* utype = NULL;
77
78
0
    assert(nc != NULL);
79
0
    assert((memory == NULL && count == 0) || (memory != NULL || count > 0));
80
81
    /* Process atomic types */
82
83
    /* Optimize: Vector of fixed size atomic types (always the case for netcdf-3)*/
84
0
    if(xtype < NC_STRING) goto done;
85
86
0
#ifdef USE_NETCDF4
87
    /* Optimize: Vector of strings */
88
0
    if(xtype == NC_STRING) {
89
0
  char** ss = (char**)memory;
90
0
        for(i=0;i<count;i++) {
91
0
      nullfree(ss[i]);
92
0
  }
93
0
        goto done;
94
0
    }
95
96
    /* Process User types */
97
0
    assert(USEFILEINFO(nc) != 0);
98
0
    file = (NC_FILE_INFO_T*)(nc)->dispatchdata;
99
0
    if((stat = nc4_find_type(file,xtype,&utype))) goto done;
100
101
    /* Optimize: vector of fixed sized compound type instances */
102
0
    if(!utype->varsized) goto done; /* no need to reclaim anything */
103
104
    /* Remaining cases: vector of VLEN and vector of (transitive) variable sized compound types.
105
       These all require potential recursion.
106
    */
107
108
    /* Build a memory walker object */
109
0
    instance.memory = (char*)memory; /* use char* so we can do pointer arithmetic */
110
    /* Walk each vector instance */
111
    /* Note that we avoid reclaiming the top level memory */
112
0
    for(i=0;i<count;i++) {
113
0
        if((stat=reclaim_datar(file,utype,instance))) goto done;
114
0
  instance.memory += utype->size; /* move to next entry */
115
0
    }
116
#else
117
    stat = NC_EBADTYPE;
118
#endif
119
120
0
done:
121
0
    return stat;
122
0
}
123
124
#ifdef USE_NETCDF4
125
/* Recursive type walker: reclaim a single instance of a variable-sized user-defined type;
126
   specifically a vlen or a variable-sized compound type instance
127
*/
128
static int
129
reclaim_datar(NC_FILE_INFO_T* file, NC_TYPE_INFO_T* utype, Position instance)
130
0
{
131
0
    int i,stat = NC_NOERR;
132
0
    nc_type basetypeid;
133
0
    NC_TYPE_INFO_T* basetype = NULL;
134
0
    size_t nfields;
135
0
    nc_vlen_t* vlen;
136
0
    size_t fid;
137
0
    int ndims;
138
0
    int dimsizes[NC_MAX_VAR_DIMS];
139
0
    size_t alignment = 0;
140
0
    Position vinstance; /* walk the vlen instance memory */
141
142
0
    assert(utype->varsized); /* All fixed size cases are optimized out */
143
144
    /* Leaving VLEN or Compound */
145
146
0
    if(utype->nc_type_class == NC_VLEN) {
147
0
  basetypeid = utype->u.v.base_nc_typeid; /* Get basetype */
148
0
  vlen = (nc_vlen_t*)instance.memory;/* memory as vector of nc_vlen_t instances */
149
  /* Optimize: basetype is atomic fixed size */
150
0
  if(basetypeid < NC_STRING) {
151
0
      goto out;
152
0
    }
153
  /* Optimize: basetype is string */
154
0
  if(basetypeid == NC_STRING) {
155
0
            if(vlen->len > 0 && vlen->p != NULL) {
156
0
          char** slist = (char**)vlen->p; /* vlen instance is a vector of string pointers */
157
0
    for(i=0;i<vlen->len;i++) {if(slist[i] != NULL) free(slist[i]);}
158
0
      }
159
0
      goto out;
160
0
  }
161
  /* Optimize: vlen basetype is a fixed-size user-type */
162
0
        if((stat = nc4_find_type(file,basetypeid,&basetype))) goto done;
163
0
        if(!basetype->varsized) {
164
0
      goto out;
165
0
  }
166
        /* Remaining case: basetype is itself variable size => recurse */
167
0
        if((stat = NC_type_alignment_internal(file,basetypeid,basetype,&alignment))) goto done;;
168
0
        vinstance.memory = (char*)vlen->p; /* use char* so we can do pointer arithmetic */
169
0
        vinstance.memory = (void*)NC_read_align((uintptr_t)vinstance.memory,alignment);
170
0
        for(i=0;i<vlen->len;i++) {
171
0
            if((stat=reclaim_datar(file,basetype,vinstance))) goto done; /* reclaim one basetype instance */
172
0
      vinstance.memory += basetype->size; /* move to next base instance */
173
0
        }
174
0
out:
175
0
        if(vlen->len > 0 && vlen->p != NULL) {free(vlen->p);}
176
0
        goto done;  
177
0
    } else if(utype->nc_type_class == NC_COMPOUND) {    
178
0
  Position finstance;  /* mark the fields's instance */
179
0
  nfields = nclistlength(utype->u.c.field);
180
        /* Get info about each field in turn and reclaim it */
181
0
        for(fid=0;fid<nfields;fid++) {
182
0
      NC_FIELD_INFO_T* field = NULL;
183
      
184
      /* Get field's dimension sizes */
185
0
      field = (NC_FIELD_INFO_T*)nclistget(utype->u.c.field,fid);    
186
0
      ndims = field->ndims;
187
0
      int arraycount = 1;
188
0
      for(i=0;i<ndims;i++) {dimsizes[i] = field->dim_size[i]; arraycount *= dimsizes[i];}
189
0
            if(field->ndims == 0) {ndims=1; dimsizes[0]=1;} /* fake the scalar case */
190
191
            /* "Move" to start of this field's instance */
192
0
            finstance.memory = instance.memory + field->offset; /* includes proper alignment */
193
194
      /* optimize: fixed length atomic type */
195
0
      if(field->nc_typeid < NC_STRING) continue;
196
197
      /* optimize: string field type */
198
0
      if(field->nc_typeid == NC_STRING) {
199
0
          char** strvec = (char**)finstance.memory;
200
0
    for(i=0;i<arraycount;i++) {
201
0
        if(strvec[i] != NULL) free(strvec[i]);
202
0
    }
203
0
    continue; /* do next field */
204
0
      }
205
      
206
      /* optimize: fixed length compound base type */
207
0
            if((stat = nc4_find_type(file,field->nc_typeid,&basetype))) goto done;
208
0
      if(!basetype->varsized) continue;
209
210
      /* Field is itself variable length (possibly transitively) */
211
0
      for(i=0;i<arraycount;i++) {
212
0
                if((stat = reclaim_datar(file, basetype, finstance))) goto done;
213
0
    finstance.memory += basetype->size;
214
0
            }
215
0
        }
216
0
  goto done;
217
0
    } else {stat = NC_EBADTYPE; goto done;}
218
219
0
done:
220
0
    return stat;
221
0
}
222
#endif
223
224
/**************************************************/
225
226
/**
227
Copy a vector of instances of a type.  This recursively walks
228
the top-level instances to copy any nested data such as vlen or
229
strings or such.
230
231
Assumes it is passed a pointer to count instances of xtype and a
232
space into which to copy the instance.  Copys any nested data
233
by calling malloc().
234
235
WARNING: DOES NOT ALLOCATE THE TOP-LEVEL MEMORY (see the
236
nc_copy_data_all function).  Note that all memory blocks other
237
than the top are dynamically allocated.
238
239
Should work for any netcdf type.
240
241
@param file NC_FILE_INFO_T* structure
242
@param xtype type id
243
@param memory ptr to top-level memory to reclaim
244
@param count number of instances of the type in memory block
245
@param copy top-level space into which to copy the instance
246
@return error code
247
*/
248
249
int
250
NC_copy_data(NC* nc, nc_type xtype, const void* memory, size_t count, void* copy)
251
0
{
252
0
    int stat = NC_NOERR;
253
0
    size_t i;
254
0
    Position src;
255
0
    Position dst;
256
0
    NC_FILE_INFO_T* file = NULL;
257
0
    NC_TYPE_INFO_T* utype = NULL;
258
0
    size_t typesize = 0;
259
260
0
    if(memory == NULL || count == 0)
261
0
        goto done; /* ok, do nothing */
262
263
0
    assert(nc != NULL);
264
0
    assert(memory != NULL || count > 0);
265
0
    assert(copy != NULL || count == 0);
266
267
    /* Process atomic types */
268
269
    /* Optimize: Vector of fixed size atomic types */
270
0
    if(xtype < NC_STRING) {
271
0
  typesize = NC_atomictypelen(xtype);
272
0
        memcpy(copy,memory,count*typesize);
273
0
  goto done;
274
0
    }
275
    
276
0
#ifdef USE_NETCDF4
277
    /* Optimize: Vector of strings */
278
0
    if(xtype == NC_STRING) {
279
0
  char** svec = (char**)memory;
280
0
  char** dvec = (char**)copy;
281
0
  size_t len;
282
0
        for(i=0;i<count;i++) {
283
0
      const char* ssrc = svec[i];
284
0
      char* sdst;
285
0
      if(ssrc == NULL)
286
0
    sdst = NULL;
287
0
      else {
288
0
          len = nulllen(ssrc);
289
0
                if((sdst = (char*)malloc(len+1))==NULL) {stat = NC_ENOMEM; goto done;}
290
0
          memcpy(sdst,ssrc,len+1); /* +1 for trailing nul */
291
0
      }
292
0
      dvec[i] = sdst;
293
0
  }
294
0
        goto done;
295
0
    }
296
297
0
    assert(USEFILEINFO(nc) != 0);
298
0
    file = (NC_FILE_INFO_T*)(nc)->dispatchdata;
299
300
    /* Process User types */
301
0
    if((stat = nc4_find_type(file,xtype,&utype))) goto done;
302
303
    /* Optimize: vector of fixed sized compound type instances */
304
0
    if(!utype->varsized) {
305
0
        memcpy(copy,memory,count*utype->size);
306
0
  goto done;
307
0
    }
308
309
    /* Remaining cases: vector of VLEN and vector of variable sized compound types.
310
       These all require potential recursion.
311
    */
312
313
0
    src.memory = (char*)memory; /* use char* so we can do pointer arithmetic */
314
0
    dst.memory = (char*)copy; /* use char* so we can do pointer arithmetic */
315
    /* Walk each vector instance */
316
0
    for(i=0;i<count;i++) {
317
0
        if((stat=copy_datar(file,utype,src,dst))) goto done;
318
0
  src.memory += utype->size;
319
0
  dst.memory += utype->size;
320
0
    }
321
#else
322
    stat = NC_EBADTYPE;
323
#endif
324
325
0
done:
326
0
    return stat;
327
0
}
328
329
#ifdef USE_NETCDF4
330
/* Recursive type walker: reclaim an instance of variable-sized user-defined types;
331
   specifically a vlen or a variable-sized compound type instance
332
*/
333
static int
334
copy_datar(NC_FILE_INFO_T* file, NC_TYPE_INFO_T* utype, Position src, Position dst)
335
0
{
336
0
    int i, stat = NC_NOERR;
337
0
    nc_type basetypeid;
338
0
    NC_TYPE_INFO_T* basetype = NULL;
339
0
    size_t nfields;
340
0
    nc_vlen_t* srcvlens = NULL;
341
0
    nc_vlen_t* dstvlens = NULL;
342
0
    size_t fid, arraycount;
343
0
    int ndims;
344
0
    int dimsizes[NC_MAX_VAR_DIMS];
345
0
    Position vsrc, vdst;
346
0
    size_t alignment = 0;
347
348
0
    assert(utype->varsized); /* All fixed size cases are optimized out */
349
350
    /* Leaving VLEN or Compound */
351
352
0
    if(utype->nc_type_class == NC_VLEN) {
353
0
  size_t basetypesize = 0;
354
0
  size_t copycount = 0;
355
356
0
  basetypeid = utype->u.v.base_nc_typeid; /* Get basetype */
357
0
  srcvlens = (nc_vlen_t*)src.memory;
358
0
  dstvlens = (nc_vlen_t*)dst.memory;
359
0
  dstvlens->len = srcvlens->len; /* always */
360
361
0
  if(srcvlens->len == 0) {
362
0
      dstvlens->p = NULL;
363
0
      goto done;
364
0
  }
365
366
0
  if(basetypeid <= NC_MAX_ATOMIC_TYPE)
367
0
      basetypesize = NC_atomictypelen(basetypeid);
368
0
        copycount = srcvlens->len*basetypesize;
369
370
  /* Optimize: basetype is atomic fixed size */
371
0
  if(basetypeid < NC_STRING) {
372
0
      if((dstvlens->p = (void*)malloc(copycount))==NULL) {stat = NC_ENOMEM; goto done;}
373
0
      memcpy(dstvlens->p,srcvlens->p,copycount);
374
0
      goto done;
375
0
    }
376
377
  /* Optimize: basetype is string */
378
0
  if(basetypeid == NC_STRING) {
379
0
      char** srcstrvec = (char**)srcvlens->p;
380
0
          char** dststrvec = NULL;
381
0
      if((dststrvec = (void*)malloc(copycount))==NULL) {stat = NC_ENOMEM; goto done;}
382
0
          dstvlens->p = (void*)dststrvec;
383
0
      for(i=0;i<srcvlens->len;i++) {
384
0
    if((dststrvec[i] = strdup(srcstrvec[i]))==NULL) {stat = NC_ENOMEM; goto done;}
385
0
      }
386
0
      goto done;
387
0
  }
388
389
  /* User-defined type */
390
        /* Recompute base type size */
391
0
        if((stat = nc4_find_type(file,basetypeid,&basetype))) goto done;
392
0
  basetypesize = basetype->size;
393
0
        copycount = srcvlens->len*basetypesize;
394
395
  /* Optimize: basetype is user-type fixed size */
396
0
        if(!basetype->varsized) {
397
0
      if((dstvlens->p = (void*)malloc(copycount))==NULL) {stat = NC_ENOMEM; goto done;}
398
0
      memcpy(dstvlens->p,srcvlens->p,copycount);
399
0
      goto done;
400
0
  }
401
402
        /* Remaining case: basetype is itself variable size => recurse */
403
0
        if((stat = NC_type_alignment_internal(file,basetypeid,basetype,&alignment))) goto done;;
404
0
        vsrc.memory = (char*)srcvlens->p; /* use char* so we can do pointer arithmetic */
405
0
        if((vdst.memory = (char*)malloc(copycount))==NULL) {stat = NC_ENOMEM; goto done;}
406
0
        dstvlens->p = vdst.memory; /* don't lose it */
407
0
        vsrc.memory = (void*)NC_read_align((uintptr_t)vsrc.memory,alignment);
408
0
  vdst.memory = (void*)NC_read_align((uintptr_t)vdst.memory,alignment);
409
0
        for(i=0;i<srcvlens->len;i++) {
410
0
            if((stat=copy_datar(file,basetype,vsrc,vdst))) goto done;
411
0
      vsrc.memory += basetype->size;
412
0
      vdst.memory += basetype->size;
413
0
        }
414
0
  goto done; 
415
0
    } else if(utype->nc_type_class == NC_COMPOUND) {    
416
0
  Position fsrc;  /* mark the src fields's instance */
417
0
  Position fdst;  /* mark the dst fields's instance */
418
0
  nfields = nclistlength(utype->u.c.field);
419
        /* Get info about each field in turn and copy it */
420
0
        for(fid=0;fid<nfields;fid++) {
421
0
      NC_FIELD_INFO_T* field = NULL;
422
      
423
0
      field = (NC_FIELD_INFO_T*)nclistget(utype->u.c.field,fid);    
424
0
      ndims = field->ndims;
425
0
      arraycount = 1;
426
0
      for(i=0;i<ndims;i++) {dimsizes[i] = field->dim_size[i]; arraycount *= (size_t)dimsizes[i];}
427
0
            if(field->ndims == 0) {ndims=1; dimsizes[0]=1;} /* fake the scalar case */
428
429
            /* "move" to this field */
430
0
            fsrc.memory = src.memory + field->offset;
431
0
            fdst.memory = dst.memory + field->offset;
432
433
      /* optimize: fixed length atomic type */
434
0
      if(field->nc_typeid < NC_STRING) {
435
0
    size_t typesize = NC_atomictypelen(field->nc_typeid);
436
0
    memcpy(fdst.memory,fsrc.memory,arraycount*typesize);
437
0
    continue; /* move to next field */
438
0
      }
439
440
      /* optimize: string field type */
441
0
      if(field->nc_typeid == NC_STRING) {
442
0
          char** srcstrvec = (char**)fsrc.memory;
443
0
          char** dststrvec = (char**)fdst.memory;
444
0
    for(i=0;i<arraycount;i++) 
445
0
        {if(srcstrvec[i] != NULL) {dststrvec[i] = strdup(srcstrvec[i]);} else {dststrvec[i] = NULL;}}
446
0
    continue; /* move to next field */
447
0
      }
448
449
      /* optimize: fixed length compound base type */
450
0
            if((stat = nc4_find_type(file,field->nc_typeid,&basetype))) goto done;
451
0
      if(!basetype->varsized) {
452
0
    memcpy(fdst.memory,fsrc.memory,arraycount*basetype->size);
453
0
    continue; /* move to next field */
454
0
      }
455
456
      /* Remaining case; field type is variable type */
457
0
            for(i=0;i<arraycount;i++) {
458
0
                if((stat = copy_datar(file, basetype, fsrc, fdst))) goto done;
459
0
    fsrc.memory += basetype->size;
460
0
    fdst.memory += basetype->size;
461
0
            }
462
0
        }
463
0
  goto done;
464
465
0
    } else {stat = NC_EBADTYPE; goto done;}
466
467
0
done:
468
0
    return stat;
469
0
}
470
#endif
471
472
/**************************************************/
473
/* Alignment functions */
474
475
#ifdef USE_NETCDF4
476
uintptr_t
477
NC_read_align(uintptr_t addr, size_t alignment)
478
0
{
479
0
  size_t loc_align = (alignment == 0 ? 1 : alignment);
480
0
  size_t delta = (addr % loc_align);
481
0
  if(delta == 0) return addr;
482
0
  return (addr + (alignment - delta));
483
0
}
484
485
486
/**
487
 * Compute proper data alignment for a type
488
 * @param file - only needed for a compound type
489
 * @param xtype - type for which alignment is requested
490
 * @param utype - if known
491
 * @return 0 if not found
492
 */
493
int
494
NC_type_alignment_internal(NC_FILE_INFO_T* file, nc_type xtype, NC_TYPE_INFO_T* utype, size_t* alignp)
495
0
{
496
0
    int stat = NC_NOERR;
497
0
    size_t align = 0;
498
0
    int klass;
499
500
0
    if(!type_alignment_initialized) {
501
0
  NC_compute_alignments();
502
0
  type_alignment_initialized = 1;
503
0
    }
504
0
    if(xtype <= NC_MAX_ATOMIC_TYPE)
505
0
        {stat = NC_class_alignment(xtype,&align); goto done;}
506
0
    else {/* Presumably a user type */
507
0
  if(utype == NULL) {
508
0
            if((stat = nc4_find_type(file,xtype,&utype))) goto done;
509
0
  }
510
0
  klass = utype->nc_type_class;
511
0
  switch(klass) {
512
0
        case NC_VLEN: stat = NC_class_alignment(klass,&align); break;
513
0
        case NC_OPAQUE: stat = NC_class_alignment(klass,&align); break;
514
0
        case NC_COMPOUND: {/* get alignment of the first field of the compound */
515
0
      NC_FIELD_INFO_T* field = NULL;
516
0
      NC_TYPE_INFO_T* basetype = NULL;
517
0
      if(nclistlength(utype->u.c.field) == 0) {stat = NC_EINVAL; goto done;}
518
0
          field = (NC_FIELD_INFO_T*)nclistget(utype->u.c.field,0);
519
0
      if(field->nc_typeid <= NC_MAX_ATOMIC_TYPE) {
520
0
          if((stat = nc4_find_type(file,field->nc_typeid,&basetype))) goto done;
521
0
      }
522
0
      stat =  NC_type_alignment_internal(file,field->nc_typeid,basetype,&align); /* may recurse repeatedly */
523
0
  } break;
524
0
        default: break;
525
0
  }
526
0
    }
527
0
    if(alignp) *alignp = align;
528
529
0
done:
530
#if 0
531
Why was this here?
532
    if(stat == NC_NOERR && align == 0) stat = NC_EINVAL;
533
#endif
534
0
    return stat;
535
0
}
536
#endif
537
538
/**************************************************/
539
540
/* Internal versions of the XX_all functions */
541
542
/* Alternate entry point: includes recovering the top-level memory */
543
int
544
NC_reclaim_data_all(NC* nc, nc_type xtypeid, void* memory, size_t count)
545
0
{
546
0
    int stat = NC_NOERR;
547
548
0
    assert(nc != NULL);
549
550
0
    stat = NC_reclaim_data(nc,xtypeid,memory,count);
551
0
    if(stat == NC_NOERR && memory != NULL)
552
0
        free(memory);
553
0
    return stat;
554
0
}
555
556
/* Alternate entry point: includes recovering the top-level memory */
557
int
558
NC_copy_data_all(NC* nc, nc_type xtype, const void* memory, size_t count, void** copyp)
559
0
{
560
0
    int stat = NC_NOERR;
561
0
    size_t xsize = 0;
562
0
    void* copy = NULL;
563
0
    NC_FILE_INFO_T* file = NULL;
564
0
    NC_TYPE_INFO_T* utype = NULL;
565
566
0
    assert(nc != NULL);
567
568
0
    if(xtype <= NC_STRING && count > 0) {
569
0
        xsize = NC_atomictypelen(xtype);
570
0
        if((copy = calloc(count,xsize))==NULL) {stat = NC_ENOMEM; goto done;}
571
0
  if(xtype < NC_STRING) /* fixed-size atomic type */
572
0
        memcpy(copy,memory,xsize*count);
573
0
  else { /* string type */
574
0
      size_t i;
575
0
      char** strvec = (char**)memory;
576
0
      char** scopyvec = (char**)copy;
577
0
      for(i=0;i<count;i++) {
578
0
    char* s = strvec[i];
579
0
    char* sdup = NULL;
580
0
    if(s != NULL) sdup = strdup(s);
581
0
    scopyvec[i] = sdup;
582
0
      }
583
0
  }
584
0
    }
585
0
#ifdef USE_NETCDF4
586
0
    else {
587
0
        file = (NC_FILE_INFO_T*)(nc)->dispatchdata;
588
0
        if((stat = nc4_find_type(file,xtype,&utype))) goto done;
589
0
        xsize = utype->size;
590
        /* allocate the top-level */
591
0
        if(count > 0) {
592
0
            if((copy = calloc(count,xsize))==NULL) {stat = NC_ENOMEM; goto done;}
593
0
        }
594
0
        if((stat = NC_copy_data(nc,xtype,memory,count,copy)))
595
0
            (void)NC_reclaim_data_all(nc,xtype,copy,count);
596
0
    }
597
0
#endif
598
0
    if(copyp) {*copyp = copy; copy = NULL;}
599
0
done:
600
0
    return stat;
601
0
}
602
603
/* Alternate entry point: includes recovering the top-level memory */
604
int
605
NC_print_data(NC_FILE_INFO_T* file, nc_type xtype, const void* memory, size_t count)
606
0
{
607
0
    EXTERNL int nc_print_data(int ncid, nc_type xtype, const void* memory, size_t count);
608
0
    return nc_print_data(file->controller->ext_ncid,xtype,memory,count);
609
0
}
610