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


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