1/*
2
3This file is part of netcdf-4, a netCDF-like interface for HDF5, or a
4HDF5 backend for netCDF, depending on your point of view.
5
6This file handles the nc4 user-defined type functions (i.e. compound
7and opaque types).
8
9Copyright 2005, University Corporation for Atmospheric Research. See
10the COPYRIGHT file for copying and redistribution conditions.
11
12$Id: nc4type.c,v 1.73 2010/05/25 17:54:24 dmh Exp $
13*/
14
15#include "nc4internal.h"
16#include "nc4dispatch.h"
17
18#define NUM_ATOMIC_TYPES 13
19char atomic_name[NUM_ATOMIC_TYPES][NC_MAX_NAME + 1] = {"none", "byte", "char",
20        "short", "int", "float",
21        "double", "ubyte",
22        "ushort", "uint",
23        "int64", "uint64", "string"};
24
25EXTERNL int
26NC4_inq_type_equal(int ncid1nc_type typeid1, int ncid2,
27   nc_type typeid2, int *equalp)
28{
29   NC_GRP_INFO_T *grpone, *grptwo;
30   NC_TYPE_INFO_T *type1, *type2;
31   int retval;
32
33   LOG((2, "nc_inq_type_equal: ncid1 0x%x typeid1 %d ncid2 0x%x typeid2 %d",
34 ncid1typeid1ncid2typeid2));
35
36   /* Check input. */
37   if(equalp == NULL) return NC_NOERR;
38
39   if (typeid1 <= NC_NAT || typeid2 <= NC_NAT)
40      return NC_EINVAL;
41
42   /* If one is atomic, and the other user-defined, the types are not
43    * equal. */
44   if ((typeid1 <= NC_STRING && typeid2 > NC_STRING) ||
45       (typeid2 <= NC_STRING && typeid1 > NC_STRING))
46   {
47      if (equalp) *equalp = 0;
48      return NC_NOERR;
49   }
50
51   /* If both are atomic types, the answer is easy. */
52   if (typeid1 <= NUM_ATOMIC_TYPES)
53   {
54      if (equalp)
55      {
56  if (typeid1 == typeid2)
57     *equalp = 1;
58  else
59     *equalp = 0;
60      }
61      return NC_NOERR;
62   }
63
64   /* Not atomic types - so find type1 and type2 information. */
65   if ((retval = nc4_find_nc4_grp(ncid1, &grpone)))
66      return retval;
67   if (!(type1 = nc4_rec_find_nc_type(grpone->nc4_info->root_grp,
68       typeid1)))
69      return NC_EBADTYPE;
70   if ((retval = nc4_find_nc4_grp(ncid2, &grptwo)))
71      return retval;
72   if (!(type2 = nc4_rec_find_nc_type(grptwo->nc4_info->root_grp,
73       typeid2)))
74      return NC_EBADTYPE;
75
76   /* Are the two types equal? */
77   if (equalp)
78      *equalp = (int)H5Tequal(type1->native_hdf_typeidtype2->native_hdf_typeid);
79
80   return NC_NOERR;
81}
82
83/* Get the id of a type from the name. */
84EXTERNL int
85NC4_inq_typeid(int ncid, const char *namenc_type *typeidp)
86{
87   NC_GRP_INFO_T *grp;
88   NC_GRP_INFO_T *grptwo;
89   NC_HDF5_FILE_INFO_T *h5;
90   NC_TYPE_INFO_T *type = NULL;
91   char *norm_name;
92   int iretval;
93
94   for (i = 0; i < NUM_ATOMIC_TYPESi++)
95      if (!strcmp(nameatomic_name[i]))
96      {
97  if (typeidp)
98     *typeidp = i;
99  return NC_NOERR;
100      }
101
102   /* Find info for this file and group, and set pointer to each. */
103   if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
104      return retval;
105
106   /* Must be a netCDF-4 file. */
107   if (!h5)
108      return NC_ENOTNC4;
109
110   /* If the first char is a /, this is a fully-qualified
111    * name. Otherwise, this had better be a local name (i.e. no / in
112    * the middle). */
113   if (name[0] != '/' && strstr(name, "/"))
114      return NC_EINVAL;
115
116   /* Normalize name. */
117   if (!(norm_name = (char*)malloc(strlen(name) + 1)))
118      return NC_ENOMEM;
119   if ((retval = nc4_normalize_name(namenorm_name))) {
120     free(norm_name);
121     return retval;
122   }
123   /* Is the type in this group? If not, search parents. */
124   for (grptwo = grpgrptwogrptwo = grptwo->parent)
125      for (type = grptwo->typetypetype = type->l.next)
126  if (!strcmp(norm_nametype->name))
127  {
128     if (typeidp)
129        *typeidp = type->nc_typeid;
130     break;
131  }
132
133   /* Still didn't find type? Search file recursively, starting at the
134    * root group. */
135   if (!type)
136      if ((type = nc4_rec_find_named_type(grp->nc4_info->root_grpnorm_name)))
137  if (typeidp)
138     *typeidp = type->nc_typeid;
139
140   free(norm_name);
141
142   /* OK, I give up already! */
143   if (!type)
144      return NC_EBADTYPE;
145
146   return NC_NOERR;
147}
148
149/* Find all user-defined types for a location. This finds all
150 * user-defined types in a group. */
151int
152NC4_inq_typeids(int ncid, int *ntypes, int *typeids)
153{
154   NC_GRP_INFO_T *grp;
155   NC_HDF5_FILE_INFO_T *h5;
156   NC_TYPE_INFO_T *type;
157   int num = 0;
158   int retval;
159
160   LOG((2, "nc_inq_typeids: ncid 0x%x", ncid));
161
162   /* Find info for this file and group, and set pointer to each. */
163   if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
164      return retval;
165
166   /* If this is a netCDF-4 file, count types. */
167   if (h5 && grp->type)
168      for (type = grp->typetypetype = type->l.next)
169      {
170  if (typeids)
171     typeids[num] = type->nc_typeid;
172  num++;
173      }
174
175   /* Give the count to the user. */
176   if (ntypes)
177      *ntypes = num;
178
179   return NC_NOERR;
180}
181
182
183/* This internal function adds a new user defined type to the metadata
184 * of a group of an open file. */
185static int
186add_user_type(int ncid, size_t size, const char *namenc_type base_typeid,
187       nc_type type_classnc_type *typeidp)
188{
189   NC_HDF5_FILE_INFO_T *h5;
190   NC_GRP_INFO_T *grp;
191   NC_TYPE_INFO_T *type;
192   char norm_name[NC_MAX_NAME + 1];
193   int retval;
194
195   /* Check and normalize the name. */
196   if ((retval = nc4_check_name(namenorm_name)))
197      return retval;
198
199   LOG((2, "%s: ncid 0x%x size %d name %s base_typeid %d ",
200 __FUNCTION__ncidsizenorm_namebase_typeid));
201
202   /* Find group metadata. */
203   if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
204      return retval;
205
206   /* Only netcdf-4 files! */
207   if (!h5)
208      return NC_ENOTNC4;
209
210   /* Turn on define mode if it is not on. */
211   if (!(h5->cmode & NC_INDEF))
212      if ((retval = NC4_redef(ncid)))
213  return retval;
214
215   /* No size is provided for vlens or enums, get it from the base type. */
216   if (type_class == NC_VLEN || type_class == NC_ENUM)
217   {
218      if ((retval = nc4_get_typelen_mem(grp->nc4_infobase_typeid, 0,
219 &size)))
220  return retval;
221   }
222   else if (size <= 0)
223      return NC_EINVAL;
224
225   /* Check that this name is not in use as a var, grp, or type. */
226   if ((retval = nc4_check_dup_name(grpnorm_name)))
227      return retval;
228
229   /* Add to our list of types. */
230   if ((retval = nc4_type_list_add(grpsizenorm_name, &type)))
231      return retval;
232
233   /* Remember info about this type. */
234   type->nc_type_class = type_class;
235   if (type_class == NC_VLEN)
236      type->u.v.base_nc_typeid = base_typeid;
237   else if (type_class == NC_ENUM)
238      type->u.e.base_nc_typeid = base_typeid;
239
240   /* Return the typeid to the user. */
241   if (typeidp)
242      *typeidp = type->nc_typeid;
243
244   return NC_NOERR;
245}
246
247
248/* The sizes of types may vary from platform to platform, but within
249 * netCDF files, type sizes are fixed. */
250#define NC_CHAR_LEN sizeof(char)
251#define NC_STRING_LEN sizeof(char *)
252#define NC_BYTE_LEN 1
253#define NC_SHORT_LEN 2
254#define NC_INT_LEN 4
255#define NC_FLOAT_LEN 4
256#define NC_DOUBLE_LEN 8
257#define NC_INT64_LEN 8
258
259/* Get the name and size of a type. For strings, 1 is returned. For
260 * VLEN the base type len is returned. */
261int
262NC4_inq_type(int ncidnc_type typeid1, char *name, size_t *size)
263{
264   NC_GRP_INFO_T *grp;
265   NC_TYPE_INFO_T *type;
266   int atomic_size[NUM_ATOMIC_TYPES] = {0, NC_BYTE_LENNC_CHAR_LENNC_SHORT_LEN,
267 NC_INT_LENNC_FLOAT_LENNC_DOUBLE_LEN,
268 NC_BYTE_LENNC_SHORT_LENNC_INT_LENNC_INT64_LEN,
269 NC_INT64_LENNC_STRING_LEN};
270
271   int retval;
272
273   LOG((2, "nc_inq_type: ncid 0x%x typeid %d", ncidtypeid1));
274
275   /* If this is an atomic type, the answer is easy. */
276   if (typeid1 < NUM_ATOMIC_TYPES)
277   {
278      if (name)
279 strcpy(nameatomic_name[typeid1]);
280      if (size)
281 *size = atomic_size[typeid1];
282      return NC_NOERR;
283   }
284
285   /* Not an atomic type - so find group. */
286   if ((retval = nc4_find_nc4_grp(ncid, &grp)))
287      return retval;
288
289   /* Find this type. */
290   if (!(type = nc4_rec_find_nc_type(grp->nc4_info->root_grptypeid1)))
291      return NC_EBADTYPE;
292
293   if (name)
294      strcpy(nametype->name);
295
296   if (size)
297   {
298      if (type->nc_type_class == NC_VLEN)
299  *size = sizeof(nc_vlen_t);
300      else if (type->nc_type_class == NC_STRING)
301  *size = 1;
302      else
303  *size = type->size;
304   }
305
306   return NC_NOERR;
307}
308
309/* Create a compound type. */
310int
311NC4_def_compound(int ncid, size_t size, const char *namenc_type *typeidp)
312{
313   return add_user_type(ncidsizename, 0, NC_COMPOUNDtypeidp);
314}
315
316/* Insert a named field into a compound type. */
317int
318NC4_insert_compound(int ncidnc_type typeid1, const char *name, size_t offset,
319    nc_type field_typeid)
320{
321   return nc_insert_array_compound(ncidtypeid1nameoffset,
322    field_typeid, 0, NULL);
323}
324
325/* Insert a named array into a compound type. */
326EXTERNL int
327NC4_insert_array_compound(int ncid, int typeid1, const char *name,
328  size_t offsetnc_type field_typeid,
329  int ndims, const int *dim_sizesp)
330{
331   NC_GRP_INFO_T *grp;
332   NC_TYPE_INFO_T *type;
333   char norm_name[NC_MAX_NAME + 1];
334   int retval;
335
336   LOG((2, "nc_insert_array_compound: ncid 0x%x, typeid %d name %s "
337 "offset %d field_typeid %d ndims %d", ncidtypeid1,
338 nameoffsetfield_typeidndims));
339
340   /* Check and normalize the name. */
341   if ((retval = nc4_check_name(namenorm_name)))
342      return retval;
343
344   /* Find file metadata. */
345   if ((retval = nc4_find_nc4_grp(ncid, &grp)))
346      return retval;
347
348   /* Find type metadata. */
349   if ((retval = nc4_find_type(grp->nc4_infotypeid1, &type)))
350      return retval;
351
352   /* Did the user give us a good compound type typeid? */
353   if (!type || type->nc_type_class != NC_COMPOUND)
354      return NC_EBADTYPE;
355
356   /* If this type has already been written to the file, you can't
357    * change it. */
358   if (type->committed)
359      return NC_ETYPDEFINED;
360
361   /* Insert new field into this type's list of fields. */
362   if ((retval = nc4_field_list_add(&type->u.c.fieldtype->u.c.num_fields,
363     norm_nameoffset, 0, 0, field_typeid,
364     ndimsdim_sizesp)))
365      return retval;
366   type->u.c.num_fields++;
367
368   return NC_NOERR;
369}
370
371/* Find info about any user defined type. */
372int
373NC4_inq_user_type(int ncidnc_type typeid1, char *name, size_t *size,
374  nc_type *base_nc_typep, size_t *nfieldsp, int *classp)
375{
376   NC_GRP_INFO_T *grp;
377   NC_TYPE_INFO_T *type;
378   int retval;
379
380   LOG((2, "nc_inq_user_type: ncid 0x%x typeid %d", ncidtypeid1));
381
382   /* Find group metadata. */
383   if ((retval = nc4_find_nc4_grp(ncid, &grp)))
384      return retval;
385
386   /* Find this type. */
387   if (!(type = nc4_rec_find_nc_type(grp->nc4_info->root_grptypeid1)))
388      return NC_EBADTYPE;
389
390   /* Count the number of fields. */
391   if (nfieldsp)
392   {
393      if (type->nc_type_class == NC_COMPOUND)
394         *nfieldsp = type->u.c.num_fields;
395      else if (type->nc_type_class == NC_ENUM)
396  *nfieldsp = type->u.e.num_members;
397      else
398  *nfieldsp = 0;
399   }
400
401   /* Fill in size and name info, if desired. */
402   if (size)
403   {
404      if (type->nc_type_class == NC_VLEN)
405  *size = sizeof(nc_vlen_t);
406      else if (type->nc_type_class == NC_STRING)
407  *size = 1;
408      else
409  *size = type->size;
410   }
411   if (name)
412      strcpy(nametype->name);
413
414   /* VLENS and ENUMs have a base type - that is, they type they are
415    * arrays of or enums of. */
416   if (base_nc_typep)
417   {
418      if (type->nc_type_class == NC_ENUM)
419         *base_nc_typep = type->u.e.base_nc_typeid;
420      else if (type->nc_type_class == NC_VLEN)
421         *base_nc_typep = type->u.v.base_nc_typeid;
422      else
423         *base_nc_typep = NC_NAT;
424   }
425
426   /* If the user wants it, tell whether this is a compound, opaque,
427    * vlen, enum, or string class of type. */
428   if (classp)
429      *classp = type->nc_type_class;
430
431   return NC_NOERR;
432}
433
434/* Given the ncid, typeid and fieldid, get info about the field. */
435int
436NC4_inq_compound_field(int ncidnc_type typeid1, int fieldid, char *name,
437       size_t *offsetpnc_type *field_typeidp, int *ndimsp,
438       int *dim_sizesp)
439{
440   NC_GRP_INFO_T *grp;
441   NC_TYPE_INFO_T *type;
442   NC_FIELD_INFO_T *field;
443   int dretval;
444
445   /* Find file metadata. */
446   if ((retval = nc4_find_nc4_grp(ncid, &grp)))
447      return retval;
448
449   /* Find this type. */
450   if (!(type = nc4_rec_find_nc_type(grp->nc4_info->root_grptypeid1)))
451      return NC_EBADTYPE;
452
453   /* Find the field. */
454   for (field = type->u.c.fieldfieldfield = field->l.next)
455      if (field->fieldid == fieldid)
456      {
457  if (name)
458     strcpy(namefield->name);
459  if (offsetp)
460     *offsetp = field->offset;
461  if (field_typeidp)
462     *field_typeidp = field->nc_typeid;
463  if (ndimsp)
464     *ndimsp = field->ndims;
465  if (dim_sizesp)
466     for (d = 0; d < field->ndimsd++)
467        dim_sizesp[d] = field->dim_size[d];
468  return NC_NOERR;
469      }
470
471   return NC_EBADFIELD;
472}
473
474/* Find a netcdf-4 file. THis will return an error if it finds a
475 * netcdf-3 file, or a netcdf-4 file with strict nc3 rules. */
476static int
477find_nc4_file(int ncidNC **nc)
478{
479   NC_HDF5_FILE_INFO_Th5;
480
481   /* Find file metadata. */
482   if (!((*nc) = nc4_find_nc_file(ncid,&h5)))
483      return NC_EBADID;
484
485   if (h5->cmode & NC_CLASSIC_MODEL)
486      return NC_ESTRICTNC3;
487
488   return NC_NOERR;
489}
490
491/* Given the typeid and the name, get the fieldid. */
492int
493NC4_inq_compound_fieldindex(int ncidnc_type typeid1, const char *name, int *fieldidp)
494{
495   NC *nc;
496   NC_TYPE_INFO_T *type;
497   NC_FIELD_INFO_T *field;
498   char norm_name[NC_MAX_NAME + 1];
499   int retval;
500
501   LOG((2, "nc_inq_compound_fieldindex: ncid 0x%x typeid %d name %s",
502 ncidtypeid1name));
503
504   /* Find file metadata. */
505   if ((retval = find_nc4_file(ncid, &nc)))
506      return retval;
507
508   /* Find the type. */
509   if ((retval = nc4_find_type(NC4_DATA(nc), typeid1, &type)))
510      return retval;
511
512   /* Did the user give us a good compound type typeid? */
513   if (!type || type->nc_type_class != NC_COMPOUND)
514      return NC_EBADTYPE;
515
516   /* Normalize name. */
517   if ((retval = nc4_normalize_name(namenorm_name)))
518      return retval;
519
520   /* Find the field with this name. */
521   for (field = type->u.c.fieldfieldfield = field->l.next)
522      if (!strcmp(field->namenorm_name))
523  break;
524
525   if (!field)
526      return NC_EBADFIELD;
527
528   if (fieldidp)
529      *fieldidp = field->fieldid;
530   return NC_NOERR;
531}
532
533
534/* Opaque type. */
535
536/* Create an opaque type. Provide a size and a name. */
537int
538NC4_def_opaque(int ncid, size_t datum_size, const char *name,
539       nc_type *typeidp)
540{
541   return add_user_type(nciddatum_sizename, 0, NC_OPAQUEtypeidp);
542}
543
544
545/* Define a variable length type. */
546int
547NC4_def_vlen(int ncid, const char *namenc_type base_typeid,
548     nc_type *typeidp)
549{
550   return add_user_type(ncid, 0, namebase_typeidNC_VLENtypeidp);
551}
552
553/* Create an enum type. Provide a base type and a name. At the moment
554 * only ints are accepted as base types. */
555int
556NC4_def_enum(int ncidnc_type base_typeid, const char *name,
557     nc_type *typeidp)
558{
559   return add_user_type(ncid, 0, namebase_typeidNC_ENUMtypeidp);
560}
561
562
563/* Get enum name from enum value. Name size will be <= NC_MAX_NAME. */
564int
565NC4_inq_enum_ident(int ncidnc_type xtype, long long value, char *identifier)
566{
567   NC_GRP_INFO_T *grp;
568   NC_TYPE_INFO_T *type;
569   NC_ENUM_MEMBER_INFO_T *enum_member;
570   long long ll_val;
571   int i;
572   int retval;
573
574   LOG((3, "nc_inq_enum_ident: xtype %d value %d\n", xtypevalue));
575
576   /* Find group metadata. */
577   if ((retval = nc4_find_nc4_grp(ncid, &grp)))
578      return retval;
579
580   /* Find this type. */
581   if (!(type = nc4_rec_find_nc_type(grp->nc4_info->root_grpxtype)))
582      return NC_EBADTYPE;
583
584   /* Complain if they are confused about the type. */
585   if (type->nc_type_class != NC_ENUM)
586      return NC_EBADTYPE;
587
588   /* Move to the desired enum member in the list. */
589   enum_member = type->u.e.enum_member;
590   for (i = 0; i < type->u.e.num_membersi++)
591   {
592      switch (type->u.e.base_nc_typeid)
593      {
594  case NC_BYTE:
595     ll_val = *(char *)enum_member->value;
596     break;
597  case NC_UBYTE:
598     ll_val = *(unsigned char *)enum_member->value;
599     break;
600  case NC_SHORT:
601     ll_val = *(short *)enum_member->value;
602     break;
603  case NC_USHORT:
604     ll_val = *(unsigned short *)enum_member->value;
605     break;
606  case NC_INT:
607     ll_val = *(int *)enum_member->value;
608     break;
609  case NC_UINT:
610     ll_val = *(unsigned int *)enum_member->value;
611     break;
612  case NC_INT64:
613  case NC_UINT64:
614     ll_val = *(long long *)enum_member->value;
615     break;
616  default:
617     return NC_EINVAL;
618      }
619      LOG((4, "ll_val=%d", ll_val));
620      if (ll_val == value)
621      {
622  if (identifier)
623     strcpy(identifierenum_member->name);
624  break;
625      }
626      else
627  enum_member = enum_member->l.next;
628   }
629
630   /* If we didn't find it, life sucks for us. :-( */
631   if (i == type->u.e.num_members)
632      return NC_EINVAL;
633
634   return NC_NOERR;
635}
636
637/* Get information about an enum member: an identifier and
638 * value. Identifier size will be <= NC_MAX_NAME. */
639int
640NC4_inq_enum_member(int ncidnc_type typeid1, int idx, char *identifier,
641    void *value)
642{
643   NC_GRP_INFO_T *grp;
644   NC_TYPE_INFO_T *type;
645   NC_ENUM_MEMBER_INFO_T *enum_member;
646   int i;
647   int retval;
648
649   LOG((2, "nc_inq_enum_member: ncid 0x%x typeid %d", ncidtypeid1));
650
651   /* Find group metadata. */
652   if ((retval = nc4_find_nc4_grp(ncid, &grp)))
653      return retval;
654
655   /* Find this type. */
656   if (!(type = nc4_rec_find_nc_type(grp->nc4_info->root_grptypeid1)))
657      return NC_EBADTYPE;
658
659   /* Complain if they are confused about the type. */
660   if (type->nc_type_class != NC_ENUM)
661      return NC_EBADTYPE;
662
663   /* Check index. */
664   if (idx >= type->u.e.num_members)
665      return NC_EINVAL;
666
667   /* Move to the desired enum member in the list. */
668   enum_member = type->u.e.enum_member;
669   for (i = 0; i < idxi++)
670      enum_member = enum_member->l.next;
671
672   /* Give the people what they want. */
673   if (identifier)
674      strcpy(identifierenum_member->name);
675   if (value)
676      memcpy(valueenum_member->valuetype->size);
677
678   return NC_NOERR;
679}
680
681/* Insert a identifierd value into an enum type. The value must fit within
682 * the size of the enum type, the identifier size must be <= NC_MAX_NAME. */
683int
684NC4_insert_enum(int ncidnc_type typeid1, const char *identifier,
685        const void *value)
686{
687   NC_GRP_INFO_T *grp;
688   NC_TYPE_INFO_T *type;
689   char norm_name[NC_MAX_NAME + 1];
690   int retval;
691
692   LOG((2, "nc_insert_enum: ncid 0x%x, typeid %d identifier %s value %d", ncid,
693 typeid1identifiervalue));
694
695   /* Check and normalize the name. */
696   if ((retval = nc4_check_name(identifiernorm_name)))
697      return retval;
698
699   /* Find file metadata. */
700   if ((retval = nc4_find_nc4_grp(ncid, &grp)))
701      return retval;
702
703   /* Find type metadata. */
704   if ((retval = nc4_find_type(grp->nc4_infotypeid1, &type)))
705      return retval;
706
707   /* Did the user give us a good enum typeid? */
708   if (!type || type->nc_type_class != NC_ENUM)
709      return NC_EBADTYPE;
710
711   /* If this type has already been written to the file, you can't
712    * change it. */
713   if (type->committed)
714      return NC_ETYPDEFINED;
715
716   /* Insert new field into this type's list of fields. */
717   if ((retval = nc4_enum_member_add(&type->u.e.enum_membertype->size,
718      norm_namevalue)))
719      return retval;
720   type->u.e.num_members++;
721
722   return NC_NOERR;
723}
724
725/* Insert one element into an already allocated vlen array element. */
726int
727NC4_put_vlen_element(int ncid, int typeid1, void *vlen_element,
728     size_t len, const void *data)
729{
730   nc_vlen_t *tmp = (nc_vlen_t*)vlen_element;
731   tmp->len = len;
732   tmp->p = (void *)data;
733   return NC_NOERR;
734}
735
736/* Insert one element into an already allocated vlen array element. */
737int
738NC4_get_vlen_element(int ncid, int typeid1, const void *vlen_element,
739     size_t *len, void *data)
740{
741   const nc_vlen_t *tmp = (nc_vlen_t*)vlen_element;
742   int type_size = 4;
743
744   *len = tmp->len;
745   memcpy(datatmp->ptmp->len * type_size);
746   return NC_NOERR;
747}
748


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed