source: XIOS/trunk/extern/src_netcdf4/dcopy.c @ 409

Last change on this file since 409 was 409, checked in by ymipsl, 11 years ago

Add improved nectdf internal library src

YM

  • Property svn:eol-style set to native
File size: 17.2 KB
Line 
1/* Copyright 2010 University Corporation for Atmospheric
2   Research/Unidata. See COPYRIGHT file for more info.
3
4   This file has the var and att copy functions.
5
6   "$Id: copy.c,v 1.1 2010/06/01 15:46:49 ed Exp $"
7*/
8
9#include "ncdispatch.h"
10#include <nc_logging.h>
11
12#ifdef USE_NETCDF4
13/* Compare two netcdf types for equality. Must have the ncids as well,
14   to find user-defined types. */
15static int
16NC_compare_nc_types(int ncid1, int typeid1, int ncid2, int typeid2, 
17                    int *equalp)
18{
19   int ret = NC_NOERR;
20
21   /* If you don't care about the answer, neither do I! */
22   if(equalp == NULL) 
23      return NC_NOERR;
24
25   /* Assume the types are not equal. If we find any inequality, then
26      exit with NC_NOERR and we're done. */
27   *equalp = 0;
28
29   /* Atomic types are so easy! */
30   if (typeid1 <= NC_MAX_ATOMIC_TYPE) 
31   {
32      if (typeid2 != typeid1) 
33         return NC_NOERR;
34      *equalp = 1;
35   }
36   else 
37   {
38      int i, ret, equal1;
39      char name1[NC_MAX_NAME];
40      char name2[NC_MAX_NAME];
41      size_t size1, size2;
42      nc_type base1, base2;
43      size_t nelems1, nelems2;
44      int class1, class2;
45      void* value1 = NULL;
46      void* value2 = NULL;
47      size_t offset1, offset2;
48      nc_type ftype1, ftype2;
49      int ndims1, ndims2;
50      int dimsizes1[NC_MAX_VAR_DIMS];
51      int dimsizes2[NC_MAX_VAR_DIMS];
52
53      /* Find out about the two types. */
54      if ((ret = nc_inq_user_type(ncid1, typeid1, name1, &size1,
55                                  &base1, &nelems1, &class1)))
56         return ret;     
57      if ((ret = nc_inq_user_type(ncid2, typeid2, name2, &size2,
58                                  &base2, &nelems2, &class2)))
59         return ret;     
60
61      /* Check the obvious. */
62      if(size1 != size2 || class1 != class2 || strcmp(name1,name2))
63         return NC_NOERR;
64
65      /* Check user-defined types in detail. */
66      switch(class1) 
67      {
68         case NC_VLEN:
69            if((ret = NC_compare_nc_types(ncid1, base1, ncid2,
70                                          base1, &equal1)))
71               return ret;
72            if(!equal1) 
73               return NC_NOERR;
74            break;
75         case NC_OPAQUE:
76            /* Already checked size above. */
77            break;
78         case NC_ENUM:
79            if(base1 != base2 || nelems1 != nelems2) return NC_NOERR;
80
81            if (!(value1 = malloc(size1)))
82               return NC_ENOMEM;
83            if (!(value2 = malloc(size2)))
84               return NC_ENOMEM;
85
86            for(i = 0; i < nelems1; i++) 
87            {
88               if ((ret = nc_inq_enum_member(ncid1, typeid1, i, name1,
89                                             value1)) ||
90                   (ret = nc_inq_enum_member(ncid2, typeid2, i, name2,
91                                             value2)) ||
92                   strcmp(name1, name2) || memcmp(value1, value2, size1))
93               {
94                  free(value1); 
95                  free(value2);
96                  return ret;
97               }
98            }
99            free(value1); 
100            free(value2);
101            break;
102         case NC_COMPOUND:
103            if(nelems1 != nelems2) 
104               return NC_NOERR;
105
106            /* Compare each field. Each must be equal! */
107            for(i = 0; i < nelems1; i++) 
108            {
109               int j;
110               if ((ret = nc_inq_compound_field(ncid1, typeid1, i, name1, &offset1, 
111                                                &ftype1, &ndims1, dimsizes1)))
112                  return ret;
113               if ((ret = nc_inq_compound_field(ncid2, typeid2, i, name2, &offset2,
114                                                &ftype2, &ndims2, dimsizes2)))
115                  return ret;
116               if(ndims1 != ndims2) 
117                  return NC_NOERR;
118               for(j = 0; j < ndims1;j++) 
119                  if(dimsizes1[j] != dimsizes2[j]) 
120                     return NC_NOERR;
121
122               /* Compare user-defined field types. */
123               if((ret = NC_compare_nc_types(ncid1, ftype1, ncid2, ftype2,
124                                             &equal1)))
125                  return ret;
126               if(!equal1) 
127                  return NC_NOERR;
128            }
129            break;
130         default:
131            return NC_EINVAL;
132      }
133      *equalp = 1;
134   }
135   return ret;
136}
137
138/* Recursively hunt for a netCDF type id. (Code from nc4internal.c);
139   Return matching typeid or 0 if not found. */
140static int
141NC_rec_find_nc_type(int ncid1, nc_type tid1, int ncid2, nc_type* tid2)
142{
143   int i,ret = NC_NOERR;
144   int nids;
145   int* ids = NULL;
146
147   /* Get all types in grp ncid2 */
148   if(tid2) 
149      *tid2 = 0;
150   if ((ret = nc_inq_typeids(ncid2, &nids, NULL)))
151      return ret;
152   if (nids)
153   {
154      if (!(ids = (int *)malloc(nids * sizeof(int))))
155         return NC_ENOMEM;
156      if ((ret = nc_inq_typeids(ncid2, &nids, ids)))
157         return ret;
158      for(i = 0; i < nids; i++) 
159      {
160         int equal = 0;
161         if ((ret = NC_compare_nc_types(ncid1, tid1, ncid2, ids[i], &equal)))
162            return ret;
163         if(equal) 
164         {
165            if(tid2) 
166               *tid2 = ids[i]; 
167            free(ids);
168            return NC_NOERR;
169         }
170      }
171      free(ids);
172   }
173   
174   /* recurse */
175   if ((ret = nc_inq_grps(ncid1, &nids, NULL)))
176      return ret;
177   if (nids)
178   {
179      if (!(ids = (int *)malloc(nids * sizeof(int))))
180         return NC_ENOMEM;
181      if ((ret = nc_inq_grps(ncid1, &nids, ids)))
182      {
183         free(ids);
184         return ret;
185      }
186      for (i = 0; i < nids; i++) 
187      {
188         ret = NC_rec_find_nc_type(ncid1, tid1, ids[i], tid2);
189         if (ret && ret != NC_EBADTYPE) 
190            break;
191         if (tid2 && *tid2 != 0) /* found */
192         {
193            free(ids);
194            return NC_NOERR;
195         }
196      }
197      free(ids);
198   }
199   return NC_EBADTYPE; /* not found */
200}
201
202/* Given a type in one file, find its equal (if any) in another
203 * file. It sounds so simple, but it's a real pain! */
204static int
205NC_find_equal_type(int ncid1, nc_type xtype1, int ncid2, nc_type *xtype2)
206{
207   int ret = NC_NOERR;
208
209   /* Check input */
210   if(xtype1 <= NC_NAT) 
211      return NC_EINVAL;
212
213   /* Handle atomic types. */
214   if (xtype1 <= NC_MAX_ATOMIC_TYPE) 
215   {
216      if(xtype2) 
217         *xtype2 = xtype1;
218      return NC_NOERR;
219   }
220
221   /* Recursively search group ncid2 and its children
222      to find a type that is equal (using compare_type)
223      to xtype1. */
224   ret = NC_rec_find_nc_type(ncid1, xtype1 , ncid2, xtype2);
225   return ret;
226}
227
228#endif /* USE_NETCDF4 */
229
230/* This will copy a variable from one file to another, assuming
231   dimensions in output file are already defined and have same
232   dimension ids.
233
234   This function must work even if the files are different formats,
235   (i.e. one old netcdf, the other hdf5-netcdf.)
236
237   But if you're copying into a netcdf-3 file, from a netcdf-4 file,
238   you must be copying a var of one of the six netcdf-3
239   types. Similarly for the attributes. */
240int
241nc_copy_var(int ncid_in, int varid_in, int ncid_out)
242{
243   char name[NC_MAX_NAME + 1];
244   char att_name[NC_MAX_NAME + 1];
245   nc_type xtype;
246   int ndims, dimids[NC_MAX_VAR_DIMS], natts, real_ndims;
247   int varid_out;
248   int a, d;
249   void *data = NULL;
250   size_t *count = NULL, *start = NULL;
251   size_t reclen = 1;
252   size_t *dimlen = NULL;
253   int retval = NC_NOERR;
254   size_t type_size;
255   int src_format, dest_format;
256   char type_name[NC_MAX_NAME+1];
257
258   /* Learn about this var. */
259   if ((retval = nc_inq_var(ncid_in, varid_in, name, &xtype, 
260                            &ndims, dimids, &natts)))
261      return retval;
262
263#ifdef USE_NETCDF4
264   LOG((2, "nc_copy_var: ncid_in 0x%x varid_in %d ncid_out 0x%x", 
265        ncid_in, varid_in, ncid_out));
266#endif
267
268   /* Make sure we are not trying to write into a netcdf-3 file
269    * anything that won't fit in netcdf-3. */
270   if ((retval = nc_inq_format(ncid_in, &src_format)))
271      return retval;
272   if ((retval = nc_inq_format(ncid_out, &dest_format)))
273      return retval;
274   if ((dest_format == NC_FORMAT_CLASSIC || dest_format == NC_FORMAT_64BIT) &&
275       src_format == NC_FORMAT_NETCDF4 && xtype > NC_DOUBLE)
276      return NC_ENOTNC4;
277
278   /* Later on, we will need to know the size of this type. */
279   if ((retval = nc_inq_type(ncid_in, xtype, type_name, &type_size)))
280      return retval;
281#ifdef USE_NETCDF4
282   LOG((3, "type %s has size %d", type_name, type_size));
283#endif
284
285   /* Switch back to define mode, and create the output var. */
286   retval = nc_redef(ncid_out);
287   if (retval && retval != NC_EINDEFINE)
288      BAIL(retval);
289   if ((retval = nc_def_var(ncid_out, name, xtype,
290                            ndims, dimids, &varid_out)))
291      BAIL(retval);
292
293   /* Copy the attributes. */
294   for (a=0; a<natts; a++)
295   {
296      if ((retval = nc_inq_attname(ncid_in, varid_in, a, att_name)))
297         BAIL(retval);
298      if ((retval = nc_copy_att(ncid_in, varid_in, att_name, 
299                                ncid_out, varid_out)))
300         BAIL(retval);
301   }
302
303   /* End define mode, to write metadata and create file. */
304   nc_enddef(ncid_out);
305   nc_sync(ncid_out);
306
307   /* Allocate memory for our start and count arrays. If ndims = 0
308      this is a scalar, which I will treat as a 1-D array with one
309      element. */
310   real_ndims = ndims ? ndims : 1;
311   if (!(start = malloc(real_ndims * sizeof(size_t))))
312      BAIL(NC_ENOMEM);
313   if (!(count = malloc(real_ndims * sizeof(size_t))))
314      BAIL(NC_ENOMEM);
315
316   /* The start array will be all zeros, except the first element,
317      which will be the record number. Count will be the dimension
318      size, except for the first element, which will be one, because
319      we will copy one record at a time. For this we need the var
320      shape. */
321   if (!(dimlen = malloc(real_ndims * sizeof(size_t))))
322      BAIL(NC_ENOMEM);
323
324   /* Find out how much data. */
325   for (d=0; d<ndims; d++)
326   {
327      if ((retval = nc_inq_dimlen(ncid_in, dimids[d], &dimlen[d])))
328         BAIL(retval);
329#ifdef USE_NETCDF4
330      LOG((4, "nc_copy_var: there are %d data", dimlen[d]));
331#endif
332   }
333
334   /* If this is really a scalar, then set the dimlen to 1. */
335   if (ndims == 0)
336      dimlen[0] = 1;
337
338   for (d=0; d<real_ndims; d++)
339   {
340      start[d] = 0;
341      count[d] = d ? dimlen[d] : 1;
342      if (d) reclen *= dimlen[d];
343   }
344
345   /* If there are no records, we're done. */
346   if (!dimlen[0])
347      goto exit;
348
349   /* Allocate memory for one record. */
350   if (!(data = malloc(reclen * type_size)))
351      return NC_ENOMEM;
352   
353   /* Copy the var data one record at a time. */
354   for (start[0]=0; !retval && start[0]<(size_t)dimlen[0]; start[0]++)
355   {
356      switch (xtype)
357      {
358         case NC_BYTE:
359            retval = nc_get_vara_schar(ncid_in, varid_in, start, count,
360                                       (signed char *)data);
361            if (!retval)
362               retval = nc_put_vara_schar(ncid_out, varid_out, start, count, 
363                                          (const signed char *)data);
364            break;
365         case NC_CHAR:
366            retval = nc_get_vara_text(ncid_in, varid_in, start, count,
367                                      (char *)data);
368            if (!retval)
369               retval = nc_put_vara_text(ncid_out, varid_out, start, count, 
370                                         (char *)data);
371            break;
372         case NC_SHORT:
373            retval = nc_get_vara_short(ncid_in, varid_in, start, count,
374                                       (short *)data);
375            if (!retval)
376               retval = nc_put_vara_short(ncid_out, varid_out, start, count,
377                                          (short *)data);
378            break;
379         case NC_INT:
380            retval = nc_get_vara_int(ncid_in, varid_in, start, count,
381                                     (int *)data);
382            if (!retval)
383               retval = nc_put_vara_int(ncid_out, varid_out, start, count,
384                                        (int *)data);
385            break;
386         case NC_FLOAT:
387            retval = nc_get_vara_float(ncid_in, varid_in, start, count,
388                                       (float *)data);
389            if (!retval)
390               retval = nc_put_vara_float(ncid_out, varid_out, start, count,
391                                          (float *)data);
392            break;
393         case NC_DOUBLE:
394            retval = nc_get_vara_double(ncid_in, varid_in, start, count,
395                                        (double *)data);
396            if (!retval)
397               retval = nc_put_vara_double(ncid_out, varid_out, start, count, 
398                                           (double *)data);
399            break;
400         case NC_UBYTE:
401            retval = nc_get_vara_uchar(ncid_in, varid_in, start, count,
402                                       (unsigned char *)data);
403            if (!retval)
404               retval = nc_put_vara_uchar(ncid_out, varid_out, start, count, 
405                                          (unsigned char *)data);
406            break;
407         case NC_USHORT:
408            retval = nc_get_vara_ushort(ncid_in, varid_in, start, count,
409                                        (unsigned short *)data);
410            if (!retval)
411               retval = nc_put_vara_ushort(ncid_out, varid_out, start, count, 
412                                           (unsigned short *)data);
413            break;
414         case NC_UINT:
415            retval = nc_get_vara_uint(ncid_in, varid_in, start, count,
416                                      (unsigned int *)data);
417            if (!retval)
418               retval = nc_put_vara_uint(ncid_out, varid_out, start, count, 
419                                         (unsigned int *)data);
420            break;
421         case NC_INT64:
422            retval = nc_get_vara_longlong(ncid_in, varid_in, start, count,
423                                          (long long *)data);
424            if (!retval)
425               retval = nc_put_vara_longlong(ncid_out, varid_out, start, count, 
426                                             (long long *)data);
427            break;
428         case NC_UINT64:
429            retval = nc_get_vara_ulonglong(ncid_in, varid_in, start, count,
430                                           (unsigned long long *)data);
431            if (!retval)
432               retval = nc_put_vara_ulonglong(ncid_out, varid_out, start, count, 
433                                              (unsigned long long *)data);
434            break;
435         default:
436            retval = NC_EBADTYPE;
437      }
438   }
439   
440  exit:
441   if (data) free(data);
442   if (dimlen) free(dimlen);
443   if (start) free(start);
444   if (count) free(count);
445   return retval;
446}
447
448static int
449NC_copy_att(int ncid_in, int varid_in, const char *name, 
450            int ncid_out, int varid_out)
451{
452   nc_type xtype;
453   size_t len;
454   void *data=NULL;
455   int res;
456   
457   LOG((2, "nc_copy_att: ncid_in 0x%x varid_in %d name %s", 
458        ncid_in, varid_in, name));
459   
460   /* Find out about the attribute to be copied. */
461   if ((res = nc_inq_att(ncid_in, varid_in, name, &xtype, &len)))
462      return res;
463   
464   if (xtype < NC_STRING) 
465   {
466      /* Handle non-string atomic types. */
467      if (len) 
468         if (!(data = malloc(len * NC_atomictypelen(xtype))))
469            return NC_ENOMEM;
470
471      res = nc_get_att(ncid_in, varid_in, name, data);
472      if (!res)
473         res = nc_put_att(ncid_out, varid_out, name, xtype, 
474                          len, data);
475      if (len)
476         free(data);
477   }
478#ifdef USE_NETCDF4
479   else if (xtype == NC_STRING) 
480   {
481      /* Copy string attributes. */
482      char **str_data;
483      if (!(str_data = malloc(sizeof(char *) * len)))
484         return NC_ENOMEM;
485      res = nc_get_att_string(ncid_in, varid_in, name, str_data);
486      if (!res)
487         res = nc_put_att_string(ncid_out, varid_out, name, len, 
488                                 (const char **)str_data);
489      nc_free_string(len, str_data);
490      free(str_data);
491   } 
492   else 
493   {
494      /* Copy user-defined type attributes. */
495      int class;
496      size_t size;
497      void *data;
498      nc_type xtype_out = NC_NAT;
499
500      /* Find out if there is an equal type in the output file. */
501      /* Note: original code used a libsrc4 specific internal function
502         which we had to "duplicate" here */
503      if ((res = NC_find_equal_type(ncid_in, xtype, ncid_out, &xtype_out)))
504         return res;
505      if (xtype_out) 
506      {
507         /* We found an equal type! */
508         if ((res = nc_inq_user_type(ncid_in, xtype, NULL, &size, 
509                                    NULL, NULL, &class)))
510            return res;
511         if (class == NC_VLEN) /* VLENs are different... */
512         { 
513            nc_vlen_t *vldata;
514            int i;
515            if (!(vldata = malloc(sizeof(nc_vlen_t) * len)))
516               return NC_ENOMEM;
517            if ((res = nc_get_att(ncid_in, varid_in, name, vldata)))
518               return res;
519            if ((res = nc_put_att(ncid_out, varid_out, name, xtype_out, 
520                                 len, vldata)))
521               return res;
522            for (i = 0; i < len; i++) 
523               if((res = nc_free_vlen(&vldata[i]))) 
524                  return res;
525            free(vldata);
526         } 
527         else /* not VLEN */
528         {
529            if (!(data = malloc(size * len)))
530               return NC_ENOMEM;
531            res = nc_get_att(ncid_in, varid_in, name, data);
532            if (!res)
533               res = nc_put_att(ncid_out, varid_out, name, xtype_out, len, data);
534            free(data);
535         }
536      }
537   }
538#endif /*!USE_NETCDF4*/
539   return res;
540}
541
542/* Copy an attribute from one open file to another.
543
544   Special programming challenge: this function must work even if one
545   of the other of the files is a netcdf version 1.0 file (i.e. not
546   HDF5). So only use top level netcdf api functions.
547
548   From the netcdf-3 docs: The output netCDF dataset should be in
549   define mode if the attribute to be copied does not already exist
550   for the target variable, or if it would cause an existing target
551   attribute to grow.
552*/
553int
554nc_copy_att(int ncid_in, int varid_in, const char *name, 
555            int ncid_out, int varid_out)
556{
557   int format, target_natts, target_attid;
558   char att_name[NC_MAX_NAME + 1];
559   int a, retval;
560
561   /* What is the destination format? */
562   if ((retval = nc_inq_format(ncid_out, &format)))
563      return retval;
564   
565   /* Can't copy to same var in same file. */
566   if (ncid_in == ncid_out && varid_in == varid_out)
567      return NC_NOERR;
568   
569   /* For classic model netCDF-4 files, order of attributes must be
570    * maintained during copies. We MUST MAINTAIN ORDER! */
571   if (format == NC_FORMAT_NETCDF4_CLASSIC)
572   {
573      /* Does this attribute already exist in the target file? */
574      retval = nc_inq_attid(ncid_out, varid_out, name, &target_attid);
575      if (retval == NC_ENOTATT)
576      {
577         /* Attribute does not exist. No order to be preserved. */
578         return NC_copy_att(ncid_in, varid_in, name, ncid_out, varid_out);
579      }
580      else if (retval == NC_NOERR)
581      {
582         /* How many atts for this var? */
583         if ((retval = nc_inq_varnatts(ncid_out, varid_out, &target_natts)))
584            return retval;
585
586         /* If this is the last attribute in the target file, we are
587          * off the hook. */
588         if (target_attid == target_natts - 1)
589            return NC_copy_att(ncid_in, varid_in, name, ncid_out, varid_out);
590         
591         /* Order MUST BE MAINTAINED! Copy all existing atts in the target
592          * file, stopping at our target att. */
593         for (a = 0; a < target_natts; a++)
594         {
595            if (a == target_attid)
596            {
597               if ((retval = NC_copy_att(ncid_in, varid_in, name, ncid_out, varid_out)))
598                  return retval;
599            } 
600            else
601            {
602               if ((retval = nc_inq_attname(ncid_out, varid_out, a, att_name)))
603                  return retval;
604               if ((retval = NC_copy_att(ncid_out, varid_out, att_name, 
605                                         ncid_out, varid_out)))
606                  return retval;
607            }
608         }
609      }
610      else
611         return retval; /* Some other error occured. */
612   }
613   else
614      return NC_copy_att(ncid_in, varid_in, name, ncid_out, varid_out);
615
616   return NC_NOERR;
617}
618
619
Note: See TracBrowser for help on using the repository browser.