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