/src/netcdf-c/libsrc/v1hpg.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2018, University Corporation for Atmospheric Research |
3 | | * See netcdf/COPYRIGHT file for copying and redistribution conditions. |
4 | | */ |
5 | | |
6 | | #if HAVE_CONFIG_H |
7 | | #include <config.h> |
8 | | #endif |
9 | | |
10 | | #include <stdlib.h> |
11 | | #include <stdio.h> |
12 | | #include <string.h> |
13 | | #include <assert.h> |
14 | | #include "nc3internal.h" |
15 | | #include "rnd.h" |
16 | | #include "ncx.h" |
17 | | |
18 | | /* |
19 | | * This module defines the external representation |
20 | | * of the "header" of a netcdf version one file and |
21 | | * the version two variant that uses 64-bit file |
22 | | * offsets instead of the 32-bit file offsets in version |
23 | | * one files. |
24 | | * For each of the components of the NC structure, |
25 | | * There are (static) ncx_len_XXX(), v1h_put_XXX() |
26 | | * and v1h_get_XXX() functions. These define the |
27 | | * external representation of the components. |
28 | | * The exported entry points for the whole NC structure |
29 | | * are built up from these. |
30 | | */ |
31 | | |
32 | | |
33 | | /* |
34 | | * "magic number" at beginning of file: 0x43444601 (big endian) |
35 | | * assert(sizeof(ncmagic) % X_ALIGN == 0); |
36 | | */ |
37 | | static const schar ncmagic[] = {'C', 'D', 'F', 0x02}; |
38 | | static const schar ncmagic1[] = {'C', 'D', 'F', 0x01}; |
39 | | static const schar ncmagic5[] = {'C', 'D', 'F', 0x05}; |
40 | | |
41 | | /* |
42 | | * v1hs == "Version 1 Header Stream" |
43 | | * |
44 | | * The netcdf file version 1 header is |
45 | | * of unknown and potentially unlimited size. |
46 | | * So, we don't know how much to get() on |
47 | | * the initial read. We build a stream, 'v1hs' |
48 | | * on top of ncio to do the header get. |
49 | | */ |
50 | | typedef struct v1hs { |
51 | | ncio *nciop; |
52 | | off_t offset; /* argument to nciop->get() */ |
53 | | size_t extent; /* argument to nciop->get() */ |
54 | | int flags; /* set to RGN_WRITE for write */ |
55 | | int version; /* format variant: NC_FORMAT_CLASSIC, NC_FORMAT_64BIT_OFFSET or NC_FORMAT_CDF5 */ |
56 | | void *base; /* beginning of current buffer */ |
57 | | void *pos; /* current position in buffer */ |
58 | | void *end; /* end of current buffer = base + extent */ |
59 | | } v1hs; |
60 | | |
61 | | |
62 | | /* |
63 | | * Release the stream, invalidate buffer |
64 | | */ |
65 | | static int |
66 | | rel_v1hs(v1hs *gsp) |
67 | 4.99k | { |
68 | 4.99k | int status; |
69 | 4.99k | if(gsp->offset == OFF_NONE || gsp->base == NULL) |
70 | 200 | return NC_NOERR; |
71 | 4.79k | status = ncio_rel(gsp->nciop, gsp->offset, |
72 | 4.79k | gsp->flags == RGN_WRITE ? RGN_MODIFIED : 0); |
73 | 4.79k | gsp->end = NULL; |
74 | 4.79k | gsp->pos = NULL; |
75 | 4.79k | gsp->base = NULL; |
76 | 4.79k | return status; |
77 | 4.99k | } |
78 | | |
79 | | |
80 | | /* |
81 | | * Release the current chunk and get the next one. |
82 | | * Also used for initialization when gsp->base == NULL. |
83 | | */ |
84 | | static int |
85 | | fault_v1hs(v1hs *gsp, size_t extent) |
86 | 4.99k | { |
87 | 4.99k | int status; |
88 | | |
89 | 4.99k | if(gsp->base != NULL) |
90 | 4.70k | { |
91 | 4.70k | const ptrdiff_t incr = (char *)gsp->pos - (char *)gsp->base; |
92 | 4.70k | status = rel_v1hs(gsp); |
93 | 4.70k | if(status) |
94 | 0 | return status; |
95 | 4.70k | gsp->offset += incr; |
96 | 4.70k | } |
97 | | |
98 | 4.99k | if(extent > gsp->extent) |
99 | 397 | gsp->extent = extent; |
100 | | |
101 | 4.99k | status = ncio_get(gsp->nciop, |
102 | 4.99k | gsp->offset, gsp->extent, |
103 | 4.99k | gsp->flags, &gsp->base); |
104 | 4.99k | if(status) |
105 | 200 | return status; |
106 | | |
107 | 4.79k | gsp->pos = gsp->base; |
108 | | |
109 | 4.79k | gsp->end = (char *)gsp->base + gsp->extent; |
110 | 4.79k | return NC_NOERR; |
111 | 4.99k | } |
112 | | |
113 | | |
114 | | /* |
115 | | * Ensure that 'nextread' bytes are available. |
116 | | */ |
117 | | static int |
118 | | check_v1hs(v1hs *gsp, size_t nextread) |
119 | 2.37M | { |
120 | | |
121 | | #if 0 /* DEBUG */ |
122 | | fprintf(stderr, "nextread %lu, remaining %lu\n", |
123 | | (unsigned long)nextread, |
124 | | (unsigned long)((char *)gsp->end - (char *)gsp->pos)); |
125 | | #endif |
126 | 2.37M | if((char *)gsp->pos + nextread <= (char *)gsp->end) |
127 | 2.37M | return NC_NOERR; |
128 | | |
129 | 4.70k | return fault_v1hs(gsp, nextread); |
130 | 2.37M | } |
131 | | |
132 | | /* End v1hs */ |
133 | | |
134 | | /* Write a size_t to the header */ |
135 | | static int |
136 | | v1h_put_size_t(v1hs *psp, const size_t *sp) |
137 | 0 | { |
138 | 0 | int status; |
139 | 0 | if (psp->version == 5) /* all integers in CDF-5 are 64 bits */ |
140 | 0 | status = check_v1hs(psp, X_SIZEOF_INT64); |
141 | 0 | else |
142 | 0 | status = check_v1hs(psp, X_SIZEOF_SIZE_T); |
143 | 0 | if(status != NC_NOERR) |
144 | 0 | return status; |
145 | 0 | if (psp->version == 5) { |
146 | 0 | unsigned long long tmp = (unsigned long long) (*sp); |
147 | 0 | return ncx_put_uint64(&psp->pos, tmp); |
148 | 0 | } |
149 | 0 | else |
150 | 0 | return ncx_put_size_t(&psp->pos, sp); |
151 | 0 | } |
152 | | |
153 | | /* Read a size_t from the header */ |
154 | | static int |
155 | | v1h_get_size_t(v1hs *gsp, size_t *sp) |
156 | 1.26M | { |
157 | 1.26M | int status; |
158 | 1.26M | if (gsp->version == 5) /* all integers in CDF-5 are 64 bits */ |
159 | 279k | status = check_v1hs(gsp, X_SIZEOF_INT64); |
160 | 982k | else |
161 | 982k | status = check_v1hs(gsp, X_SIZEOF_SIZE_T); |
162 | 1.26M | if(status != NC_NOERR) |
163 | 65 | return status; |
164 | 1.26M | if (gsp->version == 5) { |
165 | 279k | unsigned long long tmp=0; |
166 | 279k | status = ncx_get_uint64((const void **)(&gsp->pos), &tmp); |
167 | 279k | *sp = (size_t)tmp; |
168 | 279k | return status; |
169 | 279k | } |
170 | 982k | else |
171 | 982k | return ncx_get_size_t((const void **)(&gsp->pos), sp); |
172 | 1.26M | } |
173 | | |
174 | | /* Begin nc_type */ |
175 | | |
176 | 819 | #define X_SIZEOF_NC_TYPE X_SIZEOF_INT |
177 | | |
178 | | /* Write a nc_type to the header */ |
179 | | static int |
180 | | v1h_put_nc_type(v1hs *psp, const nc_type *typep) |
181 | 0 | { |
182 | 0 | const unsigned int itype = (unsigned int) *typep; |
183 | 0 | int status = check_v1hs(psp, X_SIZEOF_INT); |
184 | 0 | if(status != NC_NOERR) return status; |
185 | 0 | status = ncx_put_uint32(&psp->pos, itype); |
186 | 0 | return status; |
187 | 0 | } |
188 | | |
189 | | |
190 | | /* Read a nc_type from the header */ |
191 | | static int |
192 | | v1h_get_nc_type(v1hs *gsp, nc_type *typep) |
193 | 169k | { |
194 | 169k | unsigned int type = 0; |
195 | 169k | int status = check_v1hs(gsp, X_SIZEOF_INT); |
196 | 169k | if(status != NC_NOERR) return status; |
197 | 169k | status = ncx_get_uint32((const void**)(&gsp->pos), &type); |
198 | 169k | if(status != NC_NOERR) |
199 | 0 | return status; |
200 | | /* Fix 35382 |
201 | | assert(type == NC_BYTE |
202 | | || type == NC_CHAR |
203 | | || type == NC_SHORT |
204 | | || type == NC_INT |
205 | | || type == NC_FLOAT |
206 | | || type == NC_DOUBLE |
207 | | || type == NC_UBYTE |
208 | | || type == NC_USHORT |
209 | | || type == NC_UINT |
210 | | || type == NC_INT64 |
211 | | || type == NC_UINT64 |
212 | | || type == NC_STRING); |
213 | | */ |
214 | 169k | if(type == NC_NAT || type > NC_MAX_ATOMIC_TYPE) |
215 | 17 | return NC_EINVAL; |
216 | 169k | else |
217 | 169k | *typep = (nc_type) type; |
218 | | |
219 | 169k | return NC_NOERR; |
220 | 169k | } |
221 | | |
222 | | /* End nc_type */ |
223 | | /* Begin NCtype (internal tags) */ |
224 | | |
225 | 876 | #define X_SIZEOF_NCTYPE X_SIZEOF_INT |
226 | | |
227 | | /* Write a NCtype to the header */ |
228 | | static int |
229 | | v1h_put_NCtype(v1hs *psp, NCtype type) |
230 | 0 | { |
231 | 0 | const unsigned int itype = (unsigned int) type; |
232 | 0 | int status = check_v1hs(psp, X_SIZEOF_INT); |
233 | 0 | if(status != NC_NOERR) return status; |
234 | 0 | status = ncx_put_uint32(&psp->pos, itype); |
235 | 0 | return status; |
236 | 0 | } |
237 | | |
238 | | /* Read a NCtype from the header */ |
239 | | static int |
240 | | v1h_get_NCtype(v1hs *gsp, NCtype *typep) |
241 | 143k | { |
242 | 143k | unsigned int type = 0; |
243 | 143k | int status = check_v1hs(gsp, X_SIZEOF_INT); |
244 | 143k | if(status != NC_NOERR) return status; |
245 | 143k | status = ncx_get_uint32((const void**)(&gsp->pos), &type); |
246 | 143k | if(status != NC_NOERR) return status; |
247 | | /* else */ |
248 | 143k | *typep = (NCtype) type; |
249 | 143k | return NC_NOERR; |
250 | 143k | } |
251 | | |
252 | | /* End NCtype */ |
253 | | /* Begin NC_string */ |
254 | | |
255 | | /* |
256 | | * How much space will the xdr'd string take. |
257 | | * Formerly |
258 | | NC_xlen_string(cdfstr) |
259 | | */ |
260 | | static size_t |
261 | | ncx_len_NC_string(const NC_string *ncstrp, int version) |
262 | 1.32k | { |
263 | 1.32k | size_t sz = (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_INT; /* nchars */ |
264 | | |
265 | 1.32k | assert(ncstrp != NULL); |
266 | | |
267 | 1.32k | if(ncstrp->nchars != 0) |
268 | 372 | { |
269 | | #if 0 |
270 | | assert(ncstrp->nchars % X_ALIGN == 0); |
271 | | sz += ncstrp->nchars; |
272 | | #else |
273 | 372 | sz += _RNDUP(ncstrp->nchars, X_ALIGN); |
274 | 372 | #endif |
275 | 372 | } |
276 | 1.32k | return sz; |
277 | 1.32k | } |
278 | | |
279 | | |
280 | | /* Write a NC_string to the header */ |
281 | | static int |
282 | | v1h_put_NC_string(v1hs *psp, const NC_string *ncstrp) |
283 | 0 | { |
284 | 0 | int status; |
285 | |
|
286 | | #if 0 |
287 | | assert(ncstrp->nchars % X_ALIGN == 0); |
288 | | #endif |
289 | |
|
290 | 0 | status = v1h_put_size_t(psp, &ncstrp->nchars); |
291 | 0 | if(status != NC_NOERR) |
292 | 0 | return status; |
293 | 0 | status = check_v1hs(psp, _RNDUP(ncstrp->nchars, X_ALIGN)); |
294 | 0 | if(status != NC_NOERR) |
295 | 0 | return status; |
296 | 0 | status = ncx_pad_putn_text(&psp->pos, ncstrp->nchars, ncstrp->cp); |
297 | 0 | if(status != NC_NOERR) |
298 | 0 | return status; |
299 | | |
300 | 0 | return NC_NOERR; |
301 | 0 | } |
302 | | |
303 | | |
304 | | /* Read a NC_string from the header */ |
305 | | static int |
306 | | v1h_get_NC_string(v1hs *gsp, NC_string **ncstrpp) |
307 | 488k | { |
308 | 488k | int status = 0; |
309 | 488k | size_t nchars = 0; |
310 | 488k | NC_string *ncstrp = NULL; |
311 | | #if USE_STRICT_NULL_BYTE_HEADER_PADDING |
312 | | size_t padding = 0; |
313 | | #endif /* USE_STRICT_NULL_BYTE_HEADER_PADDING */ |
314 | | |
315 | 488k | status = v1h_get_size_t(gsp, &nchars); |
316 | 488k | if(status != NC_NOERR) |
317 | 24 | return status; |
318 | | |
319 | 488k | ncstrp = new_NC_string(nchars, NULL); |
320 | 488k | if(ncstrp == NULL) |
321 | 10 | { |
322 | 10 | return NC_ENOMEM; |
323 | 10 | } |
324 | | |
325 | | #if 0 |
326 | | /* assert(ncstrp->nchars == nchars || ncstrp->nchars - nchars < X_ALIGN); */ |
327 | | assert(ncstrp->nchars % X_ALIGN == 0); |
328 | | status = check_v1hs(gsp, ncstrp->nchars); |
329 | | #else |
330 | | |
331 | 488k | status = check_v1hs(gsp, _RNDUP(ncstrp->nchars, X_ALIGN)); |
332 | 488k | #endif |
333 | 488k | if(status != NC_NOERR) |
334 | 44 | goto unwind_alloc; |
335 | | |
336 | 488k | status = ncx_pad_getn_text((const void **)(&gsp->pos), |
337 | 488k | nchars, ncstrp->cp); |
338 | 488k | if(status != NC_NOERR) |
339 | 0 | goto unwind_alloc; |
340 | | |
341 | | #if USE_STRICT_NULL_BYTE_HEADER_PADDING |
342 | | padding = _RNDUP(X_SIZEOF_CHAR * ncstrp->nchars, X_ALIGN) |
343 | | - X_SIZEOF_CHAR * ncstrp->nchars; |
344 | | |
345 | | if (padding > 0) { |
346 | | /* CDF specification: Header padding uses null (\x00) bytes. */ |
347 | | char pad[X_ALIGN-1]; |
348 | | memset(pad, 0, X_ALIGN-1); |
349 | | if (memcmp((char*)gsp->pos-padding, pad, padding) != 0) { |
350 | | free_NC_string(ncstrp); |
351 | | return NC_ENULLPAD; |
352 | | } |
353 | | } |
354 | | #endif |
355 | | |
356 | 488k | *ncstrpp = ncstrp; |
357 | | |
358 | 488k | return NC_NOERR; |
359 | | |
360 | 44 | unwind_alloc: |
361 | 44 | free_NC_string(ncstrp); |
362 | 44 | return status; |
363 | 488k | } |
364 | | |
365 | | /* End NC_string */ |
366 | | /* Begin NC_dim */ |
367 | | |
368 | | /* |
369 | | * How much space will the xdr'd dim take. |
370 | | * Formerly |
371 | | NC_xlen_dim(dpp) |
372 | | */ |
373 | | static size_t |
374 | | ncx_len_NC_dim(const NC_dim *dimp, int version) |
375 | 510 | { |
376 | 510 | size_t sz; |
377 | | |
378 | 510 | assert(dimp != NULL); |
379 | | |
380 | 510 | sz = ncx_len_NC_string(dimp->name, version); |
381 | 510 | sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; |
382 | | |
383 | 510 | return(sz); |
384 | 510 | } |
385 | | |
386 | | |
387 | | /* Write a NC_dim to the header */ |
388 | | static int |
389 | | v1h_put_NC_dim(v1hs *psp, const NC_dim *dimp) |
390 | 0 | { |
391 | 0 | int status; |
392 | |
|
393 | 0 | status = v1h_put_NC_string(psp, dimp->name); |
394 | 0 | if(status != NC_NOERR) |
395 | 0 | return status; |
396 | | |
397 | 0 | status = v1h_put_size_t(psp, &dimp->size); |
398 | 0 | if(status != NC_NOERR) |
399 | 0 | return status; |
400 | | |
401 | 0 | return NC_NOERR; |
402 | 0 | } |
403 | | |
404 | | /* Read a NC_dim from the header */ |
405 | | static int |
406 | | v1h_get_NC_dim(v1hs *gsp, NC_dim **dimpp) |
407 | 318k | { |
408 | 318k | int status; |
409 | 318k | NC_string *ncstrp; |
410 | 318k | NC_dim *dimp; |
411 | | |
412 | 318k | status = v1h_get_NC_string(gsp, &ncstrp); |
413 | 318k | if(status != NC_NOERR) |
414 | 49 | return status; |
415 | | |
416 | 318k | dimp = new_x_NC_dim(ncstrp); |
417 | 318k | if(dimp == NULL) |
418 | 0 | { |
419 | 0 | status = NC_ENOMEM; |
420 | 0 | goto unwind_name; |
421 | 0 | } |
422 | | |
423 | 318k | status = v1h_get_size_t(gsp, &dimp->size); |
424 | 318k | if(status != NC_NOERR) |
425 | 15 | { |
426 | 15 | free_NC_dim(dimp); /* frees name */ |
427 | 15 | return status; |
428 | 15 | } |
429 | | |
430 | 318k | *dimpp = dimp; |
431 | | |
432 | 318k | return NC_NOERR; |
433 | | |
434 | 0 | unwind_name: |
435 | 0 | free_NC_string(ncstrp); |
436 | 0 | return status; |
437 | 318k | } |
438 | | |
439 | | |
440 | | /* How much space in the header is required for this NC_dimarray? */ |
441 | | static size_t |
442 | | ncx_len_NC_dimarray(const NC_dimarray *ncap, int version) |
443 | 19 | { |
444 | 19 | size_t xlen = X_SIZEOF_NCTYPE; /* type */ |
445 | 19 | xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */ |
446 | 19 | if(ncap == NULL) |
447 | 0 | return xlen; |
448 | | /* else */ |
449 | 19 | { |
450 | 19 | const NC_dim **dpp = (const NC_dim **)ncap->value; |
451 | 19 | if (dpp) |
452 | 2 | { |
453 | 2 | const NC_dim *const *const end = &dpp[ncap->nelems]; |
454 | 512 | for( /*NADA*/; dpp < end; dpp++) |
455 | 510 | { |
456 | 510 | xlen += ncx_len_NC_dim(*dpp,version); |
457 | 510 | } |
458 | 2 | } |
459 | 19 | } |
460 | 19 | return xlen; |
461 | 19 | } |
462 | | |
463 | | |
464 | | /* Write a NC_dimarray to the header */ |
465 | | static int |
466 | | v1h_put_NC_dimarray(v1hs *psp, const NC_dimarray *ncap) |
467 | 0 | { |
468 | 0 | int status; |
469 | |
|
470 | 0 | assert(psp != NULL); |
471 | |
|
472 | 0 | if(ncap == NULL |
473 | 0 | #if 1 |
474 | | /* Backward: |
475 | | * This clause is for 'byte for byte' |
476 | | * backward compatibility. |
477 | | * Strickly speaking, it is 'bug for bug'. |
478 | | */ |
479 | 0 | || ncap->nelems == 0 |
480 | 0 | #endif |
481 | 0 | ) |
482 | 0 | { |
483 | | /* |
484 | | * Handle empty netcdf |
485 | | */ |
486 | 0 | const size_t nosz = 0; |
487 | |
|
488 | 0 | status = v1h_put_NCtype(psp, NC_UNSPECIFIED); |
489 | 0 | if(status != NC_NOERR) |
490 | 0 | return status; |
491 | 0 | status = v1h_put_size_t(psp, &nosz); |
492 | 0 | if(status != NC_NOERR) |
493 | 0 | return status; |
494 | 0 | return NC_NOERR; |
495 | 0 | } |
496 | | /* else */ |
497 | | |
498 | 0 | status = v1h_put_NCtype(psp, NC_DIMENSION); |
499 | 0 | if(status != NC_NOERR) |
500 | 0 | return status; |
501 | 0 | status = v1h_put_size_t(psp, &ncap->nelems); |
502 | 0 | if(status != NC_NOERR) |
503 | 0 | return status; |
504 | | |
505 | 0 | { |
506 | 0 | const NC_dim **dpp = (const NC_dim **)ncap->value; |
507 | 0 | const NC_dim *const *const end = &dpp[ncap->nelems]; |
508 | 0 | for( /*NADA*/; dpp < end; dpp++) |
509 | 0 | { |
510 | 0 | status = v1h_put_NC_dim(psp, *dpp); |
511 | 0 | if(status) |
512 | 0 | return status; |
513 | 0 | } |
514 | 0 | } |
515 | 0 | return NC_NOERR; |
516 | 0 | } |
517 | | |
518 | | |
519 | | /* Read a NC_dimarray from the header */ |
520 | | static int |
521 | | v1h_get_NC_dimarray(v1hs *gsp, NC_dimarray *ncap) |
522 | 291 | { |
523 | 291 | int status; |
524 | 291 | NCtype type = NC_UNSPECIFIED; |
525 | | |
526 | 291 | assert(gsp != NULL && gsp->pos != NULL); |
527 | 291 | assert(ncap != NULL); |
528 | 291 | assert(ncap->value == NULL); |
529 | | |
530 | 291 | status = v1h_get_NCtype(gsp, &type); |
531 | 291 | if(status != NC_NOERR) |
532 | 0 | return status; |
533 | | |
534 | 291 | status = v1h_get_size_t(gsp, &ncap->nelems); |
535 | 291 | if(status != NC_NOERR) |
536 | 0 | return status; |
537 | | |
538 | 291 | if(ncap->nelems == 0) |
539 | 208 | return NC_NOERR; |
540 | | /* else */ |
541 | 83 | if(type != NC_DIMENSION) |
542 | 10 | return EINVAL; |
543 | | |
544 | 73 | if (ncap->nelems > SIZE_MAX / sizeof(NC_dim *)) |
545 | 0 | return NC_ERANGE; |
546 | 73 | ncap->value = (NC_dim **) calloc(1,ncap->nelems * sizeof(NC_dim *)); |
547 | 73 | if(ncap->value == NULL) |
548 | 0 | return NC_ENOMEM; |
549 | 73 | ncap->nalloc = ncap->nelems; |
550 | | |
551 | 73 | ncap->hashmap = NC_hashmapnew(ncap->nelems); |
552 | | |
553 | 73 | { |
554 | 73 | NC_dim **dpp = ncap->value; |
555 | 73 | NC_dim *const *const end = &dpp[ncap->nelems]; |
556 | 318k | for( /*NADA*/; dpp < end; dpp++) |
557 | 318k | { |
558 | 318k | status = v1h_get_NC_dim(gsp, dpp); |
559 | 318k | if(status) |
560 | 64 | { |
561 | 64 | ncap->nelems = (size_t)(dpp - ncap->value); |
562 | 64 | free_NC_dimarrayV(ncap); |
563 | 64 | return status; |
564 | 64 | } |
565 | 318k | { |
566 | 318k | uintptr_t dimid = (uintptr_t)(dpp - ncap->value); |
567 | 318k | NC_hashmapadd(ncap->hashmap, dimid, (*dpp)->name->cp, strlen((*dpp)->name->cp)); |
568 | 318k | } |
569 | 318k | } |
570 | 73 | } |
571 | | |
572 | 9 | return NC_NOERR; |
573 | 73 | } |
574 | | |
575 | | |
576 | | /* End NC_dim */ |
577 | | /* Begin NC_attr */ |
578 | | |
579 | | |
580 | | /* |
581 | | * How much space will 'attrp' take in external representation? |
582 | | * Formerly |
583 | | NC_xlen_attr(app) |
584 | | */ |
585 | | static size_t |
586 | | ncx_len_NC_attr(const NC_attr *attrp, int version) |
587 | 0 | { |
588 | 0 | size_t sz; |
589 | |
|
590 | 0 | assert(attrp != NULL); |
591 | |
|
592 | 0 | sz = ncx_len_NC_string(attrp->name, version); |
593 | 0 | sz += X_SIZEOF_NC_TYPE; /* type */ |
594 | 0 | sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* nelems */ |
595 | 0 | sz += attrp->xsz; |
596 | |
|
597 | 0 | return(sz); |
598 | 0 | } |
599 | | |
600 | | |
601 | | #undef MIN |
602 | 28.2k | #define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn)) |
603 | | |
604 | | /*----< ncmpix_len_nctype() >------------------------------------------------*/ |
605 | | /* return the length of external data type */ |
606 | | static size_t |
607 | 0 | ncmpix_len_nctype(nc_type type) { |
608 | 0 | switch(type) { |
609 | 0 | case NC_BYTE: |
610 | 0 | case NC_CHAR: |
611 | 0 | case NC_UBYTE: return X_SIZEOF_CHAR; |
612 | 0 | case NC_SHORT: return X_SIZEOF_SHORT; |
613 | 0 | case NC_USHORT: return X_SIZEOF_USHORT; |
614 | 0 | case NC_INT: return X_SIZEOF_INT; |
615 | 0 | case NC_UINT: return X_SIZEOF_UINT; |
616 | 0 | case NC_FLOAT: return X_SIZEOF_FLOAT; |
617 | 0 | case NC_DOUBLE: return X_SIZEOF_DOUBLE; |
618 | 0 | case NC_INT64: return X_SIZEOF_INT64; |
619 | 0 | case NC_UINT64: return X_SIZEOF_UINT64; |
620 | 0 | default: fprintf(stderr,"ncmpix_len_nctype bad type %d\n",type); |
621 | 0 | assert(0); |
622 | 0 | } |
623 | 0 | return 0; |
624 | 0 | } |
625 | | |
626 | | /* |
627 | | * Put the values of an attribute |
628 | | * The loop is necessary since attrp->nelems |
629 | | * could potentially be quite large. |
630 | | */ |
631 | | static int |
632 | | v1h_put_NC_attrV(v1hs *psp, const NC_attr *attrp) |
633 | 0 | { |
634 | 0 | int status = 0; |
635 | 0 | const size_t perchunk = psp->extent; |
636 | 0 | size_t remaining = attrp->xsz; |
637 | 0 | void *value = attrp->xvalue; |
638 | 0 | size_t nbytes = 0, padding = 0; |
639 | |
|
640 | 0 | assert(psp->extent % X_ALIGN == 0); |
641 | |
|
642 | 0 | do { |
643 | 0 | nbytes = MIN(perchunk, remaining); |
644 | |
|
645 | 0 | status = check_v1hs(psp, nbytes); |
646 | 0 | if(status != NC_NOERR) |
647 | 0 | return status; |
648 | | |
649 | 0 | if (value) { |
650 | 0 | (void) memcpy(psp->pos, value, nbytes); |
651 | 0 | value = (void *)((char *)value + nbytes); |
652 | 0 | } |
653 | | |
654 | 0 | psp->pos = (void *)((char *)psp->pos + nbytes); |
655 | 0 | remaining -= nbytes; |
656 | |
|
657 | 0 | } while(remaining != 0); |
658 | | |
659 | | |
660 | 0 | padding = attrp->xsz - ncmpix_len_nctype(attrp->type) * attrp->nelems; |
661 | 0 | if (padding > 0) { |
662 | | /* CDF specification: Header padding uses null (\x00) bytes. */ |
663 | 0 | memset((char*)psp->pos-padding, 0, padding); |
664 | 0 | } |
665 | |
|
666 | 0 | return NC_NOERR; |
667 | 0 | } |
668 | | |
669 | | /* Write a NC_attr to the header */ |
670 | | static int |
671 | | v1h_put_NC_attr(v1hs *psp, const NC_attr *attrp) |
672 | 0 | { |
673 | 0 | int status; |
674 | |
|
675 | 0 | status = v1h_put_NC_string(psp, attrp->name); |
676 | 0 | if(status != NC_NOERR) |
677 | 0 | return status; |
678 | | |
679 | 0 | status = v1h_put_nc_type(psp, &attrp->type); |
680 | 0 | if(status != NC_NOERR) |
681 | 0 | return status; |
682 | | |
683 | 0 | status = v1h_put_size_t(psp, &attrp->nelems); |
684 | 0 | if(status != NC_NOERR) |
685 | 0 | return status; |
686 | | |
687 | 0 | status = v1h_put_NC_attrV(psp, attrp); |
688 | 0 | if(status != NC_NOERR) |
689 | 0 | return status; |
690 | | |
691 | 0 | return NC_NOERR; |
692 | 0 | } |
693 | | |
694 | | |
695 | | /* |
696 | | * Get the values of an attribute |
697 | | * The loop is necessary since attrp->nelems |
698 | | * could potentially be quite large. |
699 | | */ |
700 | | static int |
701 | | v1h_get_NC_attrV(v1hs *gsp, NC_attr *attrp) |
702 | 26.8k | { |
703 | 26.8k | int status; |
704 | 26.8k | const size_t perchunk = gsp->extent; |
705 | 26.8k | size_t remaining = attrp->xsz; |
706 | 26.8k | void *value = attrp->xvalue; |
707 | 26.8k | size_t nget; |
708 | | #if USE_STRICT_NULL_BYTE_HEADER_PADDING |
709 | | size_t padding; |
710 | | #endif /* USE_STRICT_NULL_BYTE_HEADER_PADDING */ |
711 | | |
712 | 28.2k | do { |
713 | 28.2k | nget = MIN(perchunk, remaining); |
714 | | |
715 | 28.2k | status = check_v1hs(gsp, nget); |
716 | 28.2k | if(status != NC_NOERR) |
717 | 40 | return status; |
718 | | |
719 | 28.2k | if (value) { |
720 | 3.38k | (void) memcpy(value, gsp->pos, nget); |
721 | 3.38k | value = (void *)((signed char *)value + nget); |
722 | 3.38k | } |
723 | | |
724 | 28.2k | gsp->pos = (void*)((unsigned char *)gsp->pos + nget); |
725 | | |
726 | 28.2k | remaining -= nget; |
727 | | |
728 | 28.2k | } while(remaining != 0); |
729 | | |
730 | | #if USE_STRICT_NULL_BYTE_HEADER_PADDING |
731 | | padding = attrp->xsz - ncmpix_len_nctype(attrp->type) * attrp->nelems; |
732 | | if (padding > 0) { |
733 | | /* CDF specification: Header padding uses null (\x00) bytes. */ |
734 | | char pad[X_ALIGN-1]; |
735 | | memset(pad, 0, X_ALIGN-1); |
736 | | if (memcmp((char*)gsp->pos-padding, pad, (size_t)padding) != 0) |
737 | | return NC_ENULLPAD; |
738 | | } |
739 | | #endif |
740 | | |
741 | 26.8k | return NC_NOERR; |
742 | 26.8k | } |
743 | | |
744 | | |
745 | | /* Read a NC_attr from the header */ |
746 | | static int |
747 | | v1h_get_NC_attr(v1hs *gsp, NC_attr **attrpp) |
748 | 26.9k | { |
749 | 26.9k | NC_string *strp; |
750 | 26.9k | int status; |
751 | 26.9k | nc_type type; |
752 | 26.9k | size_t nelems; |
753 | 26.9k | NC_attr *attrp; |
754 | | |
755 | 26.9k | status = v1h_get_NC_string(gsp, &strp); |
756 | 26.9k | if(status != NC_NOERR) |
757 | 17 | return status; |
758 | | |
759 | 26.9k | status = v1h_get_nc_type(gsp, &type); |
760 | 26.9k | if(status != NC_NOERR) |
761 | 24 | goto unwind_name; |
762 | | |
763 | 26.8k | status = v1h_get_size_t(gsp, &nelems); |
764 | 26.8k | if(status != NC_NOERR) |
765 | 7 | goto unwind_name; |
766 | | |
767 | 26.8k | attrp = new_x_NC_attr(strp, type, nelems); |
768 | 26.8k | if(attrp == NULL) |
769 | 5 | { |
770 | 5 | status = NC_ENOMEM; |
771 | 5 | goto unwind_name; |
772 | 5 | } |
773 | | |
774 | 26.8k | status = v1h_get_NC_attrV(gsp, attrp); |
775 | 26.8k | if(status != NC_NOERR) |
776 | 40 | { |
777 | 40 | free_NC_attr(attrp); /* frees strp */ |
778 | 40 | return status; |
779 | 40 | } |
780 | | |
781 | 26.8k | *attrpp = attrp; |
782 | | |
783 | 26.8k | return NC_NOERR; |
784 | | |
785 | 36 | unwind_name: |
786 | 36 | free_NC_string(strp); |
787 | 36 | return status; |
788 | 26.8k | } |
789 | | |
790 | | |
791 | | /* How much space in the header is required for this NC_attrarray? */ |
792 | | static size_t |
793 | | ncx_len_NC_attrarray(const NC_attrarray *ncap, int version) |
794 | 838 | { |
795 | 838 | size_t xlen = X_SIZEOF_NCTYPE; /* type */ |
796 | 838 | xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */ |
797 | 838 | if(ncap == NULL) |
798 | 0 | return xlen; |
799 | | /* else */ |
800 | 838 | { |
801 | 838 | const NC_attr **app = (const NC_attr **)ncap->value; |
802 | 838 | if (app) |
803 | 0 | { |
804 | 0 | const NC_attr *const *const end = &app[ncap->nelems]; |
805 | 0 | for( /*NADA*/; app < end; app++) |
806 | 0 | { |
807 | 0 | xlen += ncx_len_NC_attr(*app,version); |
808 | 0 | } |
809 | 0 | } |
810 | 838 | } |
811 | 838 | return xlen; |
812 | 838 | } |
813 | | |
814 | | |
815 | | /* Write a NC_attrarray to the header */ |
816 | | static int |
817 | | v1h_put_NC_attrarray(v1hs *psp, const NC_attrarray *ncap) |
818 | 0 | { |
819 | 0 | int status; |
820 | |
|
821 | 0 | assert(psp != NULL); |
822 | |
|
823 | 0 | if(ncap == NULL |
824 | 0 | #if 1 |
825 | | /* Backward: |
826 | | * This clause is for 'byte for byte' |
827 | | * backward compatibility. |
828 | | * Strickly speaking, it is 'bug for bug'. |
829 | | */ |
830 | 0 | || ncap->nelems == 0 |
831 | 0 | #endif |
832 | 0 | ) |
833 | 0 | { |
834 | | /* |
835 | | * Handle empty netcdf |
836 | | */ |
837 | 0 | const size_t nosz = 0; |
838 | |
|
839 | 0 | status = v1h_put_NCtype(psp, NC_UNSPECIFIED); |
840 | 0 | if(status != NC_NOERR) |
841 | 0 | return status; |
842 | 0 | status = v1h_put_size_t(psp, &nosz); |
843 | 0 | if(status != NC_NOERR) |
844 | 0 | return status; |
845 | 0 | return NC_NOERR; |
846 | 0 | } |
847 | | /* else */ |
848 | | |
849 | 0 | status = v1h_put_NCtype(psp, NC_ATTRIBUTE); |
850 | 0 | if(status != NC_NOERR) |
851 | 0 | return status; |
852 | 0 | status = v1h_put_size_t(psp, &ncap->nelems); |
853 | 0 | if(status != NC_NOERR) |
854 | 0 | return status; |
855 | | |
856 | 0 | { |
857 | 0 | const NC_attr **app = (const NC_attr **)ncap->value; |
858 | 0 | const NC_attr *const *const end = &app[ncap->nelems]; |
859 | 0 | for( /*NADA*/; app < end; app++) |
860 | 0 | { |
861 | 0 | status = v1h_put_NC_attr(psp, *app); |
862 | 0 | if(status) |
863 | 0 | return status; |
864 | 0 | } |
865 | 0 | } |
866 | 0 | return NC_NOERR; |
867 | 0 | } |
868 | | |
869 | | |
870 | | /* Read a NC_attrarray from the header */ |
871 | | static int |
872 | | v1h_get_NC_attrarray(v1hs *gsp, NC_attrarray *ncap) |
873 | 142k | { |
874 | 142k | int status; |
875 | 142k | NCtype type = NC_UNSPECIFIED; |
876 | | |
877 | 142k | assert(gsp != NULL && gsp->pos != NULL); |
878 | 142k | assert(ncap != NULL); |
879 | 142k | assert(ncap->value == NULL); |
880 | | |
881 | 142k | status = v1h_get_NCtype(gsp, &type); |
882 | 142k | if(status != NC_NOERR) |
883 | 16 | return status; |
884 | 142k | status = v1h_get_size_t(gsp, &ncap->nelems); |
885 | 142k | if(status != NC_NOERR) |
886 | 8 | return status; |
887 | | |
888 | 142k | if(ncap->nelems == 0) |
889 | 142k | return NC_NOERR; |
890 | | /* else */ |
891 | 232 | if(type != NC_ATTRIBUTE) |
892 | 8 | return EINVAL; |
893 | | |
894 | 224 | ncap->value = (NC_attr **) malloc(ncap->nelems * sizeof(NC_attr *)); |
895 | 224 | if(ncap->value == NULL) |
896 | 0 | return NC_ENOMEM; |
897 | 224 | ncap->nalloc = ncap->nelems; |
898 | | |
899 | 224 | { |
900 | 224 | NC_attr **app = ncap->value; |
901 | 224 | NC_attr *const *const end = &app[ncap->nelems]; |
902 | 27.0k | for( /*NADA*/; app < end; app++) |
903 | 26.9k | { |
904 | 26.9k | status = v1h_get_NC_attr(gsp, app); |
905 | 26.9k | if(status) |
906 | 93 | { |
907 | 93 | ncap->nelems = (size_t)(app - ncap->value); |
908 | 93 | free_NC_attrarrayV(ncap); |
909 | 93 | return status; |
910 | 93 | } |
911 | 26.9k | } |
912 | 224 | } |
913 | | |
914 | 131 | return NC_NOERR; |
915 | 224 | } |
916 | | |
917 | | /* End NC_attr */ |
918 | | /* Begin NC_var */ |
919 | | |
920 | | /* |
921 | | * How much space will the xdr'd var take. |
922 | | * Formerly |
923 | | NC_xlen_var(vpp) |
924 | | */ |
925 | | static size_t |
926 | | ncx_len_NC_var(const NC_var *varp, size_t sizeof_off_t, int version) |
927 | 819 | { |
928 | 819 | size_t sz; |
929 | | |
930 | 819 | assert(varp != NULL); |
931 | 819 | assert(sizeof_off_t != 0); |
932 | | |
933 | 819 | sz = ncx_len_NC_string(varp->name, version); |
934 | 819 | if (version == 5) { |
935 | 315 | sz += X_SIZEOF_INT64; /* ndims */ |
936 | 315 | sz += ncx_len_int64(varp->ndims); /* dimids */ |
937 | 315 | } |
938 | 504 | else { |
939 | 504 | sz += X_SIZEOF_SIZE_T; /* ndims */ |
940 | 504 | sz += ncx_len_int(varp->ndims); /* dimids */ |
941 | 504 | } |
942 | 819 | sz += ncx_len_NC_attrarray(&varp->attrs, version); |
943 | 819 | sz += X_SIZEOF_NC_TYPE; /* nc_type */ |
944 | 819 | sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* vsize */ |
945 | 819 | sz += sizeof_off_t; /* begin */ |
946 | | |
947 | 819 | return(sz); |
948 | 819 | } |
949 | | |
950 | | |
951 | | /* Write a NC_var to the header */ |
952 | | static int |
953 | | v1h_put_NC_var(v1hs *psp, const NC_var *varp) |
954 | 0 | { |
955 | 0 | int status; |
956 | 0 | size_t vsize; |
957 | |
|
958 | 0 | status = v1h_put_NC_string(psp, varp->name); |
959 | 0 | if(status != NC_NOERR) |
960 | 0 | return status; |
961 | | |
962 | 0 | status = v1h_put_size_t(psp, &varp->ndims); |
963 | 0 | if(status != NC_NOERR) |
964 | 0 | return status; |
965 | | |
966 | 0 | if (psp->version == 5) { |
967 | 0 | status = check_v1hs(psp, ncx_len_int64(varp->ndims)); |
968 | 0 | if(status != NC_NOERR) |
969 | 0 | return status; |
970 | 0 | status = ncx_putn_longlong_int(&psp->pos, |
971 | 0 | varp->ndims, varp->dimids, NULL); |
972 | 0 | if(status != NC_NOERR) |
973 | 0 | return status; |
974 | 0 | } |
975 | 0 | else { |
976 | 0 | status = check_v1hs(psp, ncx_len_int(varp->ndims)); |
977 | 0 | if(status != NC_NOERR) |
978 | 0 | return status; |
979 | 0 | status = ncx_putn_int_int(&psp->pos, |
980 | 0 | varp->ndims, varp->dimids, NULL); |
981 | 0 | if(status != NC_NOERR) |
982 | 0 | return status; |
983 | 0 | } |
984 | | |
985 | 0 | status = v1h_put_NC_attrarray(psp, &varp->attrs); |
986 | 0 | if(status != NC_NOERR) |
987 | 0 | return status; |
988 | | |
989 | 0 | status = v1h_put_nc_type(psp, &varp->type); |
990 | 0 | if(status != NC_NOERR) |
991 | 0 | return status; |
992 | | |
993 | | /* write vsize to header. |
994 | | * CDF format specification: The vsize field is actually redundant, because |
995 | | * its value may be computed from other information in the header. The |
996 | | * 32-bit vsize field is not large enough to contain the size of variables |
997 | | * that require more than 2^32 - 4 bytes, so 2^32 - 1 is used in the vsize |
998 | | * field for such variables. |
999 | | */ |
1000 | 0 | vsize = varp->len; |
1001 | 0 | if (varp->len > 4294967292UL && (psp->version == NC_FORMAT_CLASSIC || |
1002 | 0 | psp->version == NC_FORMAT_64BIT_OFFSET)) |
1003 | 0 | vsize = 4294967295UL; /* 2^32-1 */ |
1004 | 0 | status = v1h_put_size_t(psp, &vsize); |
1005 | 0 | if(status != NC_NOERR) return status; |
1006 | | |
1007 | 0 | status = check_v1hs(psp, psp->version == 1 ? 4 : 8); /*begin*/ |
1008 | 0 | if(status != NC_NOERR) |
1009 | 0 | return status; |
1010 | 0 | status = ncx_put_off_t(&psp->pos, &varp->begin, psp->version == 1 ? 4 : 8); |
1011 | 0 | if(status != NC_NOERR) |
1012 | 0 | return status; |
1013 | | |
1014 | 0 | return NC_NOERR; |
1015 | 0 | } |
1016 | | |
1017 | | |
1018 | | /* Read a NC_var from the header */ |
1019 | | static int |
1020 | | v1h_get_NC_var(v1hs *gsp, NC_var **varpp) |
1021 | 142k | { |
1022 | 142k | NC_string *strp; |
1023 | 142k | int status; |
1024 | 142k | size_t ndims; |
1025 | 142k | NC_var *varp; |
1026 | | |
1027 | 142k | status = v1h_get_NC_string(gsp, &strp); |
1028 | 142k | if(status != NC_NOERR) |
1029 | 12 | return status; |
1030 | | |
1031 | 142k | status = v1h_get_size_t(gsp, &ndims); |
1032 | 142k | if(status != NC_NOERR) |
1033 | 6 | goto unwind_name; |
1034 | | |
1035 | 142k | varp = new_x_NC_var(strp, ndims); |
1036 | 142k | if(varp == NULL) |
1037 | 0 | { |
1038 | 0 | status = NC_ENOMEM; |
1039 | 0 | goto unwind_name; |
1040 | 0 | } |
1041 | | |
1042 | 142k | if (gsp->version == 5) { |
1043 | 1.07k | status = check_v1hs(gsp, ncx_len_int64(ndims)); |
1044 | 1.07k | if(status != NC_NOERR) |
1045 | 3 | goto unwind_alloc; |
1046 | 1.07k | status = ncx_getn_longlong_int((const void **)(&gsp->pos), |
1047 | 1.07k | ndims, varp->dimids); |
1048 | 1.07k | if(status != NC_NOERR) |
1049 | 17 | goto unwind_alloc; |
1050 | 1.07k | } |
1051 | 141k | else { |
1052 | 141k | status = check_v1hs(gsp, ncx_len_int(ndims)); |
1053 | 141k | if(status != NC_NOERR) |
1054 | 6 | goto unwind_alloc; |
1055 | 141k | status = ncx_getn_int_int((const void **)(&gsp->pos), |
1056 | 141k | ndims, varp->dimids); |
1057 | 141k | if(status != NC_NOERR) |
1058 | 0 | goto unwind_alloc; |
1059 | 141k | } |
1060 | 142k | status = v1h_get_NC_attrarray(gsp, &varp->attrs); |
1061 | 142k | if(status != NC_NOERR) |
1062 | 32 | goto unwind_alloc; |
1063 | 142k | status = v1h_get_nc_type(gsp, &varp->type); |
1064 | 142k | if(status != NC_NOERR) |
1065 | 12 | goto unwind_alloc; |
1066 | | |
1067 | 142k | size_t tmp; |
1068 | 142k | status = v1h_get_size_t(gsp, &tmp); |
1069 | 142k | varp->len = tmp; |
1070 | 142k | if(status != NC_NOERR) |
1071 | 5 | goto unwind_alloc; |
1072 | | |
1073 | 142k | status = check_v1hs(gsp, gsp->version == 1 ? 4 : 8); |
1074 | 142k | if(status != NC_NOERR) |
1075 | 7 | goto unwind_alloc; |
1076 | 142k | status = ncx_get_off_t((const void **)&gsp->pos, |
1077 | 142k | &varp->begin, gsp->version == 1 ? 4 : 8); |
1078 | 142k | if(status != NC_NOERR) |
1079 | 0 | goto unwind_alloc; |
1080 | | |
1081 | 142k | *varpp = varp; |
1082 | 142k | return NC_NOERR; |
1083 | | |
1084 | 82 | unwind_alloc: |
1085 | 82 | free_NC_var(varp); /* frees name */ |
1086 | 82 | return status; |
1087 | | |
1088 | 6 | unwind_name: |
1089 | 6 | free_NC_string(strp); |
1090 | 6 | return status; |
1091 | 142k | } |
1092 | | |
1093 | | |
1094 | | /* How much space in the header is required for this NC_vararray? */ |
1095 | | static size_t |
1096 | | ncx_len_NC_vararray(const NC_vararray *ncap, size_t sizeof_off_t, int version) |
1097 | 19 | { |
1098 | 19 | size_t xlen = X_SIZEOF_NCTYPE; /* type */ |
1099 | 19 | xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */ |
1100 | 19 | if(ncap == NULL) |
1101 | 0 | return xlen; |
1102 | | /* else */ |
1103 | 19 | { |
1104 | 19 | const NC_var **vpp = (const NC_var **)ncap->value; |
1105 | 19 | if (vpp) |
1106 | 8 | { |
1107 | 8 | const NC_var *const *const end = &vpp[ncap->nelems]; |
1108 | 827 | for( /*NADA*/; vpp < end; vpp++) |
1109 | 819 | { |
1110 | 819 | xlen += ncx_len_NC_var(*vpp, sizeof_off_t, version); |
1111 | 819 | } |
1112 | 8 | } |
1113 | 19 | } |
1114 | 19 | return xlen; |
1115 | 19 | } |
1116 | | |
1117 | | |
1118 | | /* Write a NC_vararray to the header */ |
1119 | | static int |
1120 | | v1h_put_NC_vararray(v1hs *psp, const NC_vararray *ncap) |
1121 | 0 | { |
1122 | 0 | int status; |
1123 | |
|
1124 | 0 | assert(psp != NULL); |
1125 | |
|
1126 | 0 | if(ncap == NULL |
1127 | 0 | #if 1 |
1128 | | /* Backward: |
1129 | | * This clause is for 'byte for byte' |
1130 | | * backward compatibility. |
1131 | | * Strickly speaking, it is 'bug for bug'. |
1132 | | */ |
1133 | 0 | || ncap->nelems == 0 |
1134 | 0 | #endif |
1135 | 0 | ) |
1136 | 0 | { |
1137 | | /* |
1138 | | * Handle empty netcdf |
1139 | | */ |
1140 | 0 | const size_t nosz = 0; |
1141 | |
|
1142 | 0 | status = v1h_put_NCtype(psp, NC_UNSPECIFIED); |
1143 | 0 | if(status != NC_NOERR) |
1144 | 0 | return status; |
1145 | 0 | status = v1h_put_size_t(psp, &nosz); |
1146 | 0 | if(status != NC_NOERR) |
1147 | 0 | return status; |
1148 | 0 | return NC_NOERR; |
1149 | 0 | } |
1150 | | /* else */ |
1151 | | |
1152 | 0 | status = v1h_put_NCtype(psp, NC_VARIABLE); |
1153 | 0 | if(status != NC_NOERR) |
1154 | 0 | return status; |
1155 | 0 | status = v1h_put_size_t(psp, &ncap->nelems); |
1156 | 0 | if(status != NC_NOERR) |
1157 | 0 | return status; |
1158 | | |
1159 | 0 | { |
1160 | 0 | const NC_var **vpp = (const NC_var **)ncap->value; |
1161 | 0 | const NC_var *const *const end = &vpp[ncap->nelems]; |
1162 | 0 | for( /*NADA*/; vpp < end; vpp++) |
1163 | 0 | { |
1164 | 0 | status = v1h_put_NC_var(psp, *vpp); |
1165 | 0 | if(status) |
1166 | 0 | return status; |
1167 | 0 | } |
1168 | 0 | } |
1169 | 0 | return NC_NOERR; |
1170 | 0 | } |
1171 | | |
1172 | | |
1173 | | /* Read a NC_vararray from the header */ |
1174 | | static int |
1175 | | v1h_get_NC_vararray(v1hs *gsp, NC_vararray *ncap) |
1176 | 124 | { |
1177 | 124 | int status; |
1178 | 124 | NCtype type = NC_UNSPECIFIED; |
1179 | | |
1180 | 124 | assert(gsp != NULL && gsp->pos != NULL); |
1181 | 124 | assert(ncap != NULL); |
1182 | 124 | assert(ncap->value == NULL); |
1183 | | |
1184 | 124 | status = v1h_get_NCtype(gsp, &type); |
1185 | 124 | if(status != NC_NOERR) |
1186 | 0 | return status; |
1187 | | |
1188 | 124 | status = v1h_get_size_t(gsp, &ncap->nelems); |
1189 | 124 | if(status != NC_NOERR) |
1190 | 0 | return status; |
1191 | | |
1192 | 124 | if(ncap->nelems == 0) |
1193 | 11 | return NC_NOERR; |
1194 | | /* else */ |
1195 | 113 | if(type != NC_VARIABLE) |
1196 | 0 | return EINVAL; |
1197 | | |
1198 | 113 | if (ncap->nelems > SIZE_MAX / sizeof(NC_var *)) |
1199 | 5 | return NC_ERANGE; |
1200 | 108 | ncap->value = (NC_var **) calloc(1,ncap->nelems * sizeof(NC_var *)); |
1201 | 108 | if(ncap->value == NULL) |
1202 | 0 | return NC_ENOMEM; |
1203 | 108 | ncap->nalloc = ncap->nelems; |
1204 | | |
1205 | 108 | ncap->hashmap = NC_hashmapnew(ncap->nelems); |
1206 | 108 | if (ncap->hashmap == NULL) |
1207 | 0 | return NC_ENOMEM; |
1208 | 108 | { |
1209 | 108 | NC_var **vpp = ncap->value; |
1210 | 108 | NC_var *const *const end = &vpp[ncap->nelems]; |
1211 | 142k | for( /*NADA*/; vpp < end; vpp++) |
1212 | 142k | { |
1213 | 142k | status = v1h_get_NC_var(gsp, vpp); |
1214 | 142k | if(status) |
1215 | 100 | { |
1216 | 100 | ncap->nelems = (size_t)(vpp - ncap->value); |
1217 | 100 | free_NC_vararrayV(ncap); |
1218 | 100 | return status; |
1219 | 100 | } |
1220 | 142k | { |
1221 | 142k | uintptr_t varid = (uintptr_t)(vpp - ncap->value); |
1222 | 142k | NC_hashmapadd(ncap->hashmap, varid, (*vpp)->name->cp, strlen((*vpp)->name->cp)); |
1223 | 142k | } |
1224 | 142k | } |
1225 | 108 | } |
1226 | | |
1227 | 8 | return NC_NOERR; |
1228 | 108 | } |
1229 | | |
1230 | | |
1231 | | /* End NC_var */ |
1232 | | /* Begin NC */ |
1233 | | |
1234 | | /* |
1235 | | * Recompute the shapes of all variables |
1236 | | * Sets ncp->begin_var to start of first variable. |
1237 | | * Sets ncp->begin_rec to start of first record variable. |
1238 | | * Returns -1 on error. The only possible error is a reference |
1239 | | * to a non existent dimension, which could occur for a corrupted |
1240 | | * netcdf file. |
1241 | | */ |
1242 | | static int |
1243 | | NC_computeshapes(NC3_INFO* ncp) |
1244 | 19 | { |
1245 | 19 | NC_var **vpp = (NC_var **)ncp->vars.value; |
1246 | 19 | NC_var *first_var = NULL; /* first "non-record" var */ |
1247 | 19 | NC_var *first_rec = NULL; /* first "record" var */ |
1248 | 19 | int status; |
1249 | | |
1250 | 19 | ncp->begin_var = (off_t) ncp->xsz; |
1251 | 19 | ncp->begin_rec = (off_t) ncp->xsz; |
1252 | 19 | ncp->recsize = 0; |
1253 | | |
1254 | 19 | if(ncp->vars.nelems == 0) |
1255 | 11 | return(0); |
1256 | | |
1257 | 8 | if (vpp) |
1258 | 8 | { |
1259 | 8 | NC_var *const *const end = &vpp[ncp->vars.nelems]; |
1260 | 821 | for( /*NADA*/; vpp < end; vpp++) |
1261 | 815 | { |
1262 | 815 | status = NC_var_shape(*vpp, &ncp->dims); |
1263 | 815 | if(status != NC_NOERR) |
1264 | 2 | return(status); |
1265 | | |
1266 | 813 | if(IS_RECVAR(*vpp)) |
1267 | 0 | { |
1268 | 0 | if(first_rec == NULL) |
1269 | 0 | first_rec = *vpp; |
1270 | 0 | ncp->recsize += (*vpp)->len; |
1271 | 0 | } |
1272 | 813 | else |
1273 | 813 | { |
1274 | 813 | if(first_var == NULL) |
1275 | 7 | first_var = *vpp; |
1276 | | /* |
1277 | | * Overwritten each time thru. |
1278 | | * Usually overwritten in first_rec != NULL clause below. |
1279 | | */ |
1280 | 813 | ncp->begin_rec = (*vpp)->begin + (off_t)(*vpp)->len; |
1281 | 813 | } |
1282 | 813 | } |
1283 | 8 | } |
1284 | | |
1285 | 6 | if(first_rec != NULL) |
1286 | 0 | { |
1287 | 0 | if(ncp->begin_rec > first_rec->begin) |
1288 | 0 | return(NC_ENOTNC); /* not a netCDF file or corrupted */ |
1289 | 0 | ncp->begin_rec = first_rec->begin; |
1290 | | /* |
1291 | | * for special case of exactly one record variable, pack value |
1292 | | */ |
1293 | 0 | if(ncp->recsize == first_rec->len) |
1294 | 0 | ncp->recsize = *first_rec->dsizes * first_rec->xsz; |
1295 | 0 | } |
1296 | | |
1297 | 6 | if(first_var != NULL) |
1298 | 6 | { |
1299 | 6 | ncp->begin_var = first_var->begin; |
1300 | 6 | } |
1301 | 0 | else |
1302 | 0 | { |
1303 | 0 | ncp->begin_var = ncp->begin_rec; |
1304 | 0 | } |
1305 | | |
1306 | 6 | if(ncp->begin_var <= 0 || |
1307 | 4 | ncp->xsz > (size_t)ncp->begin_var || |
1308 | 4 | ncp->begin_rec <= 0 || |
1309 | 4 | ncp->begin_var > ncp->begin_rec) |
1310 | 4 | return(NC_ENOTNC); /* not a netCDF file or corrupted */ |
1311 | | |
1312 | 2 | return(NC_NOERR); |
1313 | 6 | } |
1314 | | |
1315 | | /* How much space in the header is required for the NC data structure? */ |
1316 | | size_t |
1317 | | ncx_len_NC(const NC3_INFO* ncp, size_t sizeof_off_t) |
1318 | 19 | { |
1319 | 19 | int version=1; |
1320 | 19 | size_t xlen = sizeof(ncmagic); |
1321 | | |
1322 | 19 | assert(ncp != NULL); |
1323 | 19 | if (fIsSet(ncp->flags, NC_64BIT_DATA)) /* CDF-5 */ |
1324 | 3 | version = 5; |
1325 | 16 | else if (fIsSet(ncp->flags, NC_64BIT_OFFSET)) /* CDF-2 */ |
1326 | 4 | version = 2; |
1327 | | |
1328 | 19 | xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* numrecs */ |
1329 | 19 | xlen += ncx_len_NC_dimarray(&ncp->dims, version); |
1330 | 19 | xlen += ncx_len_NC_attrarray(&ncp->attrs, version); |
1331 | 19 | xlen += ncx_len_NC_vararray(&ncp->vars, sizeof_off_t, version); |
1332 | | |
1333 | 19 | return xlen; |
1334 | 19 | } |
1335 | | |
1336 | | |
1337 | | /* Write the file header */ |
1338 | | int |
1339 | | ncx_put_NC(const NC3_INFO* ncp, void **xpp, off_t offset, size_t extent) |
1340 | 0 | { |
1341 | 0 | int status = NC_NOERR; |
1342 | 0 | v1hs ps; /* the get stream */ |
1343 | |
|
1344 | 0 | assert(ncp != NULL); |
1345 | | |
1346 | | /* Initialize stream ps */ |
1347 | |
|
1348 | 0 | ps.nciop = ncp->nciop; |
1349 | 0 | ps.flags = RGN_WRITE; |
1350 | |
|
1351 | 0 | if (ncp->flags & NC_64BIT_DATA) |
1352 | 0 | ps.version = 5; |
1353 | 0 | else if (ncp->flags & NC_64BIT_OFFSET) |
1354 | 0 | ps.version = 2; |
1355 | 0 | else |
1356 | 0 | ps.version = 1; |
1357 | |
|
1358 | 0 | if(xpp == NULL) |
1359 | 0 | { |
1360 | | /* |
1361 | | * Come up with a reasonable stream read size. |
1362 | | */ |
1363 | 0 | extent = ncp->xsz; |
1364 | 0 | if(extent <= ((ps.version==5)?MIN_NC5_XSZ:MIN_NC3_XSZ)) |
1365 | 0 | { |
1366 | | /* first time read */ |
1367 | 0 | extent = ncp->chunk; |
1368 | | /* Protection for when ncp->chunk is huge; |
1369 | | * no need to read hugely. */ |
1370 | 0 | if(extent > 4096) |
1371 | 0 | extent = 4096; |
1372 | 0 | } |
1373 | 0 | else if(extent > ncp->chunk) |
1374 | 0 | extent = ncp->chunk; |
1375 | |
|
1376 | 0 | ps.offset = 0; |
1377 | 0 | ps.extent = extent; |
1378 | 0 | ps.base = NULL; |
1379 | 0 | ps.pos = ps.base; |
1380 | |
|
1381 | 0 | status = fault_v1hs(&ps, extent); |
1382 | 0 | if(status) |
1383 | 0 | return status; |
1384 | 0 | } |
1385 | 0 | else |
1386 | 0 | { |
1387 | 0 | ps.offset = offset; |
1388 | 0 | ps.extent = extent; |
1389 | 0 | ps.base = *xpp; |
1390 | 0 | ps.pos = ps.base; |
1391 | 0 | ps.end = (char *)ps.base + ps.extent; |
1392 | 0 | } |
1393 | | |
1394 | 0 | if (ps.version == 5) |
1395 | 0 | status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic5), ncmagic5, NULL); |
1396 | 0 | else if (ps.version == 2) |
1397 | 0 | status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic), ncmagic, NULL); |
1398 | 0 | else |
1399 | 0 | status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic1), ncmagic1, NULL); |
1400 | 0 | if(status != NC_NOERR) |
1401 | 0 | goto release; |
1402 | | |
1403 | 0 | { |
1404 | 0 | const size_t nrecs = NC_get_numrecs(ncp); |
1405 | 0 | if (ps.version == 5) { |
1406 | 0 | unsigned long long tmp = (unsigned long long) nrecs; |
1407 | 0 | status = ncx_put_uint64(&ps.pos, tmp); |
1408 | 0 | } |
1409 | 0 | else |
1410 | 0 | status = ncx_put_size_t(&ps.pos, &nrecs); |
1411 | 0 | if(status != NC_NOERR) |
1412 | 0 | goto release; |
1413 | 0 | } |
1414 | | |
1415 | 0 | assert((char *)ps.pos < (char *)ps.end); |
1416 | |
|
1417 | 0 | status = v1h_put_NC_dimarray(&ps, &ncp->dims); |
1418 | 0 | if(status != NC_NOERR) |
1419 | 0 | goto release; |
1420 | | |
1421 | 0 | status = v1h_put_NC_attrarray(&ps, &ncp->attrs); |
1422 | 0 | if(status != NC_NOERR) |
1423 | 0 | goto release; |
1424 | | |
1425 | 0 | status = v1h_put_NC_vararray(&ps, &ncp->vars); |
1426 | 0 | if(status != NC_NOERR) |
1427 | 0 | goto release; |
1428 | | |
1429 | 0 | release: |
1430 | 0 | (void) rel_v1hs(&ps); |
1431 | |
|
1432 | 0 | return status; |
1433 | 0 | } |
1434 | | |
1435 | | |
1436 | | /* Make the in-memory NC structure from reading the file header */ |
1437 | | int |
1438 | | nc_get_NC(NC3_INFO* ncp) |
1439 | 291 | { |
1440 | 291 | int status; |
1441 | 291 | v1hs gs; /* the get stream */ |
1442 | | |
1443 | 291 | assert(ncp != NULL); |
1444 | | |
1445 | | /* Initialize stream gs */ |
1446 | | |
1447 | 291 | gs.nciop = ncp->nciop; |
1448 | 291 | gs.offset = 0; /* beginning of file */ |
1449 | 291 | gs.extent = 0; |
1450 | 291 | gs.flags = 0; |
1451 | 291 | gs.version = 0; |
1452 | 291 | gs.base = NULL; |
1453 | 291 | gs.pos = gs.base; |
1454 | | |
1455 | 291 | { |
1456 | | /* |
1457 | | * Come up with a reasonable stream read size. |
1458 | | */ |
1459 | 291 | off_t filesize; |
1460 | 291 | size_t extent = ncp->xsz; |
1461 | | |
1462 | 291 | if(extent <= ((fIsSet(ncp->flags, NC_64BIT_DATA))?MIN_NC5_XSZ:MIN_NC3_XSZ)) |
1463 | 291 | { |
1464 | 291 | status = ncio_filesize(ncp->nciop, &filesize); |
1465 | 291 | if(status) |
1466 | 0 | return status; |
1467 | 291 | if(filesize < sizeof(ncmagic)) { /* too small, not netcdf */ |
1468 | |
|
1469 | 0 | status = NC_ENOTNC; |
1470 | 0 | return status; |
1471 | 0 | } |
1472 | | /* first time read */ |
1473 | 291 | extent = ncp->chunk; |
1474 | | /* Protection for when ncp->chunk is huge; |
1475 | | * no need to read hugely. */ |
1476 | 291 | if(extent > 4096) |
1477 | 199 | extent = 4096; |
1478 | 291 | if(extent > filesize) |
1479 | 0 | extent = (size_t)filesize; |
1480 | 291 | } |
1481 | 0 | else if(extent > ncp->chunk) |
1482 | 0 | extent = ncp->chunk; |
1483 | | |
1484 | | /* |
1485 | | * Invalidate the I/O buffers to force a read of the header |
1486 | | * region. |
1487 | | */ |
1488 | 291 | status = ncio_sync(gs.nciop); |
1489 | 291 | if(status) |
1490 | 0 | return status; |
1491 | | |
1492 | 291 | status = fault_v1hs(&gs, extent); |
1493 | 291 | if(status) |
1494 | 0 | return status; |
1495 | 291 | } |
1496 | | |
1497 | | /* get the header from the stream gs */ |
1498 | | |
1499 | 291 | { |
1500 | | /* Get & check magic number */ |
1501 | 291 | schar magic[sizeof(ncmagic)]; |
1502 | 291 | (void) memset(magic, 0, sizeof(magic)); |
1503 | | |
1504 | 291 | status = ncx_getn_schar_schar( |
1505 | 291 | (const void **)(&gs.pos), sizeof(magic), magic); |
1506 | 291 | if(status != NC_NOERR) |
1507 | 0 | goto unwind_get; |
1508 | | |
1509 | 291 | if(memcmp(magic, ncmagic, sizeof(ncmagic)-1) != 0) |
1510 | 0 | { |
1511 | 0 | status = NC_ENOTNC; |
1512 | 0 | goto unwind_get; |
1513 | 0 | } |
1514 | | /* Check version number in last byte of magic */ |
1515 | 291 | if (magic[sizeof(ncmagic)-1] == 0x1) { |
1516 | 93 | gs.version = 1; |
1517 | 198 | } else if (magic[sizeof(ncmagic)-1] == 0x2) { |
1518 | 100 | gs.version = 2; |
1519 | 100 | fSet(ncp->flags, NC_64BIT_OFFSET); |
1520 | | /* Now we support version 2 file access on non-LFS systems -- rkr */ |
1521 | | #if 0 |
1522 | | if (sizeof(off_t) != 8) { |
1523 | | fprintf(stderr, "NETCDF WARNING: Version 2 file on 32-bit system.\n"); |
1524 | | } |
1525 | | #endif |
1526 | 100 | } else if (magic[sizeof(ncmagic)-1] == 0x5) { |
1527 | 98 | gs.version = 5; |
1528 | 98 | fSet(ncp->flags, NC_64BIT_DATA); |
1529 | 98 | } else { |
1530 | 0 | status = NC_ENOTNC; |
1531 | 0 | goto unwind_get; |
1532 | 0 | } |
1533 | 291 | } |
1534 | | |
1535 | 291 | { |
1536 | 291 | size_t nrecs = 0; |
1537 | 291 | if (gs.version == 5) { |
1538 | 98 | unsigned long long tmp = 0; |
1539 | 98 | status = ncx_get_uint64((const void **)(&gs.pos), &tmp); |
1540 | 98 | nrecs = (size_t)tmp; |
1541 | 98 | } |
1542 | 193 | else |
1543 | 193 | status = ncx_get_size_t((const void **)(&gs.pos), &nrecs); |
1544 | 291 | if(status != NC_NOERR) |
1545 | 0 | goto unwind_get; |
1546 | 291 | NC_set_numrecs(ncp, nrecs); |
1547 | 291 | } |
1548 | | |
1549 | 291 | assert((char *)gs.pos < (char *)gs.end); |
1550 | | |
1551 | 291 | status = v1h_get_NC_dimarray(&gs, &ncp->dims); |
1552 | 291 | if(status != NC_NOERR) |
1553 | 74 | goto unwind_get; |
1554 | | |
1555 | 217 | status = v1h_get_NC_attrarray(&gs, &ncp->attrs); |
1556 | 217 | if(status != NC_NOERR) |
1557 | 93 | goto unwind_get; |
1558 | | |
1559 | 124 | status = v1h_get_NC_vararray(&gs, &ncp->vars); |
1560 | 124 | if(status != NC_NOERR) |
1561 | 105 | goto unwind_get; |
1562 | | |
1563 | 19 | ncp->xsz = ncx_len_NC(ncp, (gs.version == 1) ? 4 : 8); |
1564 | | |
1565 | 19 | status = NC_computeshapes(ncp); |
1566 | 19 | if(status != NC_NOERR) |
1567 | 6 | goto unwind_get; |
1568 | | |
1569 | 13 | status = NC_check_vlens(ncp); |
1570 | 13 | if(status != NC_NOERR) |
1571 | 0 | goto unwind_get; |
1572 | | |
1573 | 13 | status = NC_check_voffs(ncp); |
1574 | 13 | if(status != NC_NOERR) |
1575 | 2 | goto unwind_get; |
1576 | | |
1577 | 291 | unwind_get: |
1578 | 291 | (void) rel_v1hs(&gs); |
1579 | 291 | return status; |
1580 | 13 | } |