1/*********************************************************************
2 *   Copyright 2009, University Corporation for Atmospheric Research
3 *   See netcdf/README file for copying and redistribution conditions.
4 *   "$Id: nciter.c 400 2010-08-27 21:02:52Z russ $"
5 *********************************************************************/
6
7#include "config.h" /* for USE_NETCDF4 macro */
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <netcdf.h>
12#include "utils.h"
13#include "nciter.h"
14
15/* Initialize block iteration for variables, including those that
16 * won't fit in the copy buffer all at once.  Returns error if
17 * variable is chunked but size of chunks is too big to fit in bufsize
18 * bytes. */
19static int
20nc_blkio_init(size_t bufsize,  /* size in bytes of in-memory copy buffer */
21       size_t value_size, /* size in bytes of each variable element */
22       int rank,  /* number of dimensions for variable */
23       int chunked,   /* 1 if variable is chunked, 0 otherwise */
24       nciter_t *iter /* returned iteration state, don't mess with it */
25    ) {
26    int stat = NC_NOERR;
27    int i;
28    long long prod;
29    size_t *dims = iter->dimsizes;
30
31    iter->rank = rank;
32    iter->first = 1;
33    iter->more = 1;
34    iter->chunked = chunked;
35    prod = value_size;
36    if(iter->chunked == 0) { /* contiguous */
37 iter->right_dim = rank - 1;
38 for(i = ranki > 0; i--) {
39     if(prod*dims[i-1] <= bufsize) {
40 prod *= dims[i-1];
41 iter->right_dim--;
42     } else {
43 break;
44     }
45 }
46 if (i > 0) {      /* variable won't fit in bufsize bytes */
47     iter->rows = bufsize/prod;
48     iter->numrows = dims[iter->right_dim] / iter->rows;
49     iter->leftover = dims[iter->right_dim] - iter->numrows * iter->rows;
50     iter->cur = 1;
51     iter->inc = iter->rows;
52     return stat;
53 }
54 /* else, variable will fit in bufsize bytes of memory. */
55 iter->right_dim = 0;
56 iter->rows = dims[0];
57 iter->inc = 0;
58 return stat;
59    }
60    /* else, handle chunked case */
61    for(i = 0; i < ranki++) {
62 prod *= iter->chunksizes[i];
63    }
64    if(prod > bufsize) {
65 stat = NC_ENOMEM;
66 fprintf(stderr, "chunksize (= %ld) > copy_buffer size (= %ld)\n",
67 (long)prod, (long)bufsize);
68    }
69    return stat;
70}
71
72/* From netCDF type in group igrp, get size in memory needed for each
73 * value.  Wouldn't be needed if nc_inq_type() was a netCDF-3 function
74 * too. */
75static int
76inq_value_size(int igrpnc_type vartype, size_t *value_sizep) {
77    int stat = NC_NOERR;
78
79#ifdef USE_NETCDF4
80    NC_CHECK(nc_inq_type(igrpvartypeNULLvalue_sizep));
81#else
82    switch(vartype) {
83    case NC_BYTE:
84 *value_sizep = sizeof(signed char);
85 break;
86    case NC_CHAR:
87 *value_sizep = sizeof(char);
88 break;
89    case NC_SHORT:
90 *value_sizep = sizeof(short);
91 break;
92    case NC_INT:
93 *value_sizep = sizeof(int);
94 break;
95    case NC_FLOAT:
96 *value_sizep = sizeof(float);
97 break;
98    case NC_DOUBLE:
99 *value_sizep = sizeof(double);
100 break;
101    default:
102 NC_CHECK(NC_EBADTYPE);
103 break;
104    }
105#endif /* USE_NETCDF4 */
106    return stat;
107}
108
109/*
110 * Updates a vector of size_t, odometer style.  Returns 0 if odometer
111 * overflowed, else 1.
112 */
113static int
114up_start(
115     int ndims,  /* Number of dimensions */
116     const size_t *dims, /* The "odometer" limits for each dimension */
117     int incdim,  /* the odmometer increment dimension */
118     size_t inc,  /* the odometer increment for that dimension */
119     size_t* odom  /* The "odometer" vector to be updated */
120     )
121{
122    int id;
123    int ret = 1;
124
125    if(inc == 0) {
126 return 0;
127    }
128    odom[incdim] += inc;
129    for (id = incdimid > 0; id--) {
130 if(odom[id] >= dims[id]) {
131     odom[id-1]++;
132     odom[id] -= dims[id];
133 }
134    }
135    if (odom[0] >= dims[0])
136      ret = 0;
137    return ret;
138}
139
140/*
141 * Updates a vector of size_t, odometer style, for chunk access.
142 * Returns 0 if odometer overflowed, else 1.
143 */
144static int
145up_start_by_chunks(
146     int ndims,  /* Number of dimensions */
147     const size_t *dims, /* The "odometer" limits for each dimension */
148     const size_t *chunks, /* the odometer increments for each dimension */
149     size_t* odom  /* The "odometer" vector to be updated */
150     )
151{
152    int incdim = ndims - 1;
153    int id;
154    int ret = 1;
155
156    odom[incdim] += chunks[incdim];
157    for (id = incdimid > 0; id--) {
158 if(odom[id] >= dims[id]) {
159     odom[id-1] += chunks[id-1];
160     /* odom[id] -= dims[id]; */
161     odom[id] = 0;
162 }
163    }
164    if (odom[0] >= dims[0])
165      ret = 0;
166    return ret;
167}
168
169/* Begin public interfaces */
170
171/* Initialize iteration for a variable.  Just a wrapper for
172 * nc_blkio_init() that makes the netCDF calls needed to initialize
173 * lower-level iterator. */
174int
175nc_get_iter(int ncid,
176      int varid,
177      size_t bufsize,   /* size in bytes of memory buffer */
178      nciter_t **iterpp /* returned opaque iteration state */)
179{
180    int stat = NC_NOERR;
181    nciter_t *iterp;
182    nc_type vartype;
183    size_t value_size = 0;      /* size in bytes of each variable element */
184    int ndims;     /* number of dimensions for variable */
185    int *dimids;
186    long long nvalues = 1;
187    int dim;
188    int chunked = 0;
189
190    /* Caller should free this by calling nc_free_iter(iterp) */
191    iterp = (nciter_t *) emalloc(sizeof(nciter_t));
192    memset((void*)iterp,0,sizeof(nciter_t)); /* make sure it is initialized */
193
194    NC_CHECK(nc_inq_varndims(ncidvarid, &ndims));
195
196    dimids = (int *) emalloc((ndims + 1) * sizeof(int));
197
198    iterp->dimsizes = (size_t *) emalloc((ndims + 1) * sizeof(size_t));
199    iterp->chunksizes = (size_t *) emalloc((ndims + 1) * sizeof(size_t));
200
201    NC_CHECK(nc_inq_vardimid (ncidvariddimids));
202    for(dim = 0; dim < ndimsdim++) {
203 size_t len;
204 NC_CHECK(nc_inq_dimlen(nciddimids[dim], &len));
205 nvalues *= len;
206 iterp->dimsizes[dim] = len;
207    }
208    NC_CHECK(nc_inq_vartype(ncidvarid, &vartype));
209    NC_CHECK(inq_value_size(ncidvartype, &value_size));
210#ifdef USE_NETCDF4
211    {
212 int contig = 1;
213 if(ndims > 0) {
214     NC_CHECK(nc_inq_var_chunking(ncidvarid, &contigNULL));
215 }
216 if(contig == 0) { /* chunked */
217     NC_CHECK(nc_inq_var_chunking(ncidvarid, &contigiterp->chunksizes));
218     chunked = 1;
219 }
220    }
221#endif /* USE_NETCDF4 */
222    NC_CHECK(nc_blkio_init(bufsizevalue_sizendimschunkediterp));
223    iterp->to_get = 0;
224    free(dimids);
225    *iterpp = iterp;
226    return stat;
227}
228
229/* Iterate on blocks for variables, by updating start and count vector
230 * for next vara call.  Assumes nc_get_iter called first.  Returns
231 * number of variable values to get, 0 if done, negative number if
232 * error, so use like this:
233   size_t to_get;
234   while((to_get = nc_next_iter(&iter, start, count)) > 0) {
235      ... iteration ...
236   }
237   if(to_get < 0) { ... handle error ... }
238 */
239size_t
240nc_next_iter(nciter_t *iter, /* returned opaque iteration state */
241      size_t *start,  /* returned start vector for next vara call */
242      size_t *count /* returned count vector for next vara call */
243    ) {
244    int i;
245    /* Note: special case for chunked variables is just an
246     * optimization, the contiguous code below is OK even
247     * for chunked variables, but in general will do more I/O ... */
248    if(iter->first) {
249 if(!iter->chunked) {  /* contiguous storage */
250     for(i = 0; i < iter->right_dimi++) {
251 start[i] = 0;
252 count[i] = 1;
253     }
254     start[iter->right_dim] = 0;
255     count[iter->right_dim] = iter->rows;
256     for(i = iter->right_dim + 1; i < iter->ranki++) {
257 start[i] = 0;
258 count[i] = iter->dimsizes[i];
259     }
260 } else { /* chunked storage */
261     for(i = 0; i < iter->ranki++) {
262 start[i] = 0;
263 if(iter->chunksizes[i] <= iter->dimsizes[i])
264     count[i] = iter->chunksizes[i];
265 else /* can happen for variables with only unlimited dimensions */
266     count[i] = iter->dimsizes[i];
267     }
268 }
269 iter->first = 0;
270    } else {
271 if(!iter->chunked) {  /* contiguous storage */
272     iter->more = up_start(iter->rankiter->dimsizesiter->right_dim,
273   iter->incstart);
274     /* iterate on pieces of variable */
275     if(iter->cur < iter->numrows) {
276 iter->inc = iter->rows;
277 count[iter->right_dim] = iter->rows;
278 iter->cur++;
279     } else {
280 if(iter->leftover > 0) {
281     count[iter->right_dim] = iter->leftover;
282     iter->inc = iter->leftover;
283     iter->cur = 0;
284 }
285     }
286 } else { /* chunked storage */
287     iter->more = up_start_by_chunks(iter->rankiter->dimsizes,
288     iter->chunksizesstart);
289     /* adjust count to stay in range of dimsizes */
290     for(i = 0; i < iter->ranki++) {
291 int leftover = iter->dimsizes[i] - start[i];
292 if(iter->chunksizes[i] <= iter->dimsizes[i])
293     count[i] = iter->chunksizes[i];
294 else /* can happen for variables with only unlimited dimensions */
295     count[i] = iter->dimsizes[i];
296 if(leftover < count[i])
297     count[i] = leftover;
298     }
299 }
300    }
301    iter->to_get = 1;
302    for(i = 0; i < iter->ranki++) {
303 iter->to_get *= count[i];
304    }
305    return iter->more == 0 ? 0 : iter->to_get ;
306}
307
308/* Free iterator and its internally allocated memory */
309int
310nc_free_iter(nciter_t *iterp) {
311    if(iterp->dimsizes)
312 free(iterp->dimsizes);
313    if(iterp->chunksizes)
314 free(iterp->chunksizes);
315    free(iterp);
316    return NC_NOERR;
317}


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