Coverage Report

Created: 2026-03-30 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/netcdf-c-4.7.4/libhdf5/nc4hdf.c
Line
Count
Source
1
/* Copyright 2018, University Corporation for Atmospheric
2
 * Research. See the COPYRIGHT file for copying and redistribution
3
 * conditions. */
4
/**
5
 * @file
6
 * @internal This file is part of netcdf-4, a netCDF-like interface
7
 * for HDF5, or a HDF5 backend for netCDF, depending on your point of
8
 * view.
9
 *
10
 * This file contains functions internal to the netcdf4 library. None of
11
 * the functions in this file are exposed in the exetnal API. These
12
 * functions handle the HDF interface.
13
 *
14
 * @author Ed Hartnett, Dennis Heimbigner, Ward Fisher
15
 */
16
17
#include "config.h"
18
#include "netcdf.h"
19
#include "nc4internal.h"
20
#include "ncdispatch.h"
21
#include "hdf5internal.h"
22
#include <math.h>
23
24
#ifdef HAVE_INTTYPES_H
25
#define __STDC_FORMAT_MACROS
26
#include <inttypes.h>
27
#endif
28
29
0
#define NC_HDF5_MAX_NAME 1024 /**< @internal Max size of HDF5 name. */
30
31
/**
32
 * @internal Flag attributes in a linked list as dirty.
33
 *
34
 * @param attlist List of attributes, may be NULL.
35
 *
36
 * @return NC_NOERR No error.
37
 * @author Dennis Heimbigner
38
 */
39
static int
40
0
flag_atts_dirty(NCindex *attlist) {
41
42
0
    NC_ATT_INFO_T *att = NULL;
43
0
    int i;
44
45
0
    if(attlist == NULL) {
46
0
        return NC_NOERR;
47
0
    }
48
49
0
    for(i=0;i<ncindexsize(attlist);i++) {
50
0
        att = (NC_ATT_INFO_T*)ncindexith(attlist,i);
51
0
        if(att == NULL) continue;
52
0
        att->dirty = NC_TRUE;
53
0
    }
54
55
0
    return NC_NOERR;
56
0
}
57
58
/**
59
 * @internal This function is needed to handle one special case: what
60
 * if the user defines a dim, writes metadata, then goes back into
61
 * define mode and adds a coordinate var for the already existing
62
 * dim. In that case, I need to recreate the dim's dimension scale
63
 * dataset, and then I need to go to every var in the file which uses
64
 * that dimension, and attach the new dimension scale.
65
 *
66
 * @param grp Pointer to group info struct.
67
 * @param dimid Dimension ID.
68
 * @param dimscaleid HDF5 dimension scale ID.
69
 *
70
 * @returns NC_NOERR No error.
71
 * @returns NC_EHDFERR HDF5 returned an error.
72
 * @author Ed Hartnett
73
 */
74
int
75
rec_reattach_scales(NC_GRP_INFO_T *grp, int dimid, hid_t dimscaleid)
76
0
{
77
0
    NC_VAR_INFO_T *var;
78
0
    NC_GRP_INFO_T *child_grp;
79
0
    int d, i;
80
0
    int retval;
81
82
0
    assert(grp && grp->hdr.name && dimid >= 0 && dimscaleid >= 0);
83
0
    LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
84
85
    /* If there are any child groups, attach dimscale there, if needed. */
86
0
    for (i = 0; i < ncindexsize(grp->children); i++)
87
0
    {
88
0
        child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children, i);
89
0
        assert(child_grp);
90
0
        if ((retval = rec_reattach_scales(child_grp, dimid, dimscaleid)))
91
0
            return retval;
92
0
    }
93
94
    /* Find any vars that use this dimension id. */
95
0
    for (i = 0; i < ncindexsize(grp->vars); i++)
96
0
    {
97
0
        NC_HDF5_VAR_INFO_T *hdf5_var;
98
99
0
        var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
100
0
        assert(var && var->format_var_info);
101
0
        hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
102
103
0
        for (d = 0; d < var->ndims; d++)
104
0
        {
105
0
            if (var->dimids[d] == dimid && !var->dimscale)
106
0
            {
107
0
                LOG((2, "%s: attaching scale for dimid %d to var %s",
108
0
                     __func__, var->dimids[d], var->hdr.name));
109
0
                if (var->created)
110
0
                {
111
0
                    if (H5DSattach_scale(hdf5_var->hdf_datasetid,
112
0
                                         dimscaleid, d) < 0)
113
0
                        return NC_EHDFERR;
114
0
                    var->dimscale_attached[d] = NC_TRUE;
115
0
                }
116
0
            }
117
0
        }
118
0
    }
119
0
    return NC_NOERR;
120
0
}
121
122
/**
123
 * @internal This function is needed to handle one special case: what
124
 * if the user defines a dim, writes metadata, then goes back into
125
 * define mode and adds a coordinate var for the already existing
126
 * dim. In that case, I need to recreate the dim's dimension scale
127
 * dataset, and then I need to go to every var in the file which uses
128
 * that dimension, and attach the new dimension scale.
129
 *
130
 * @param grp Pointer to group info struct.
131
 * @param dimid Dimension ID.
132
 * @param dimscaleid HDF5 dimension scale ID.
133
 *
134
 * @returns NC_NOERR No error.
135
 * @returns NC_EHDFERR HDF5 returned an error.
136
 * @author Ed Hartnett
137
 */
138
int
139
rec_detach_scales(NC_GRP_INFO_T *grp, int dimid, hid_t dimscaleid)
140
0
{
141
0
    NC_VAR_INFO_T *var;
142
0
    NC_GRP_INFO_T *child_grp;
143
0
    int d, i;
144
0
    int retval;
145
146
0
    assert(grp && grp->hdr.name && dimid >= 0 && dimscaleid >= 0);
147
0
    LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
148
149
    /* If there are any child groups, detach dimscale there, if needed. */
150
0
    for(i=0;i<ncindexsize(grp->children);i++) {
151
0
        child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children,i);
152
0
        if(child_grp == NULL) continue;
153
0
        if ((retval = rec_detach_scales(child_grp, dimid, dimscaleid)))
154
0
            return retval;
155
0
    }
156
157
    /* Find any vars that use this dimension id. */
158
0
    for (i = 0; i < ncindexsize(grp->vars); i++)
159
0
    {
160
0
        NC_HDF5_VAR_INFO_T *hdf5_var;
161
0
        var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i);
162
0
        assert(var && var->format_var_info);
163
0
        hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
164
165
0
        for (d = 0; d < var->ndims; d++)
166
0
        {
167
0
            if (var->dimids[d] == dimid && !var->dimscale)
168
0
            {
169
0
                LOG((2, "%s: detaching scale for dimid %d to var %s",
170
0
                     __func__, var->dimids[d], var->hdr.name));
171
0
                if (var->created)
172
0
                {
173
0
                    if (var->dimscale_attached && var->dimscale_attached[d])
174
0
                    {
175
0
                        if (H5DSdetach_scale(hdf5_var->hdf_datasetid,
176
0
                                             dimscaleid, d) < 0)
177
0
                            return NC_EHDFERR;
178
0
                        var->dimscale_attached[d] = NC_FALSE;
179
0
                    }
180
0
                }
181
0
            }
182
0
        }
183
0
    }
184
0
    return NC_NOERR;
185
0
}
186
187
/**
188
 * @internal Open a HDF5 dataset and leave it open.
189
 *
190
 * @param grp Pointer to group info struct.
191
 * @param varid Variable ID.
192
 * @param dataset Pointer that gets the HDF5 dataset ID.
193
 *
194
 * @returns NC_NOERR No error.
195
 * @returns NC_EHDFERR HDF5 returned an error.
196
 * @author Ed Hartnett
197
 */
198
int
199
nc4_open_var_grp2(NC_GRP_INFO_T *grp, int varid, hid_t *dataset)
200
1.00M
{
201
1.00M
    NC_VAR_INFO_T *var;
202
1.00M
    NC_HDF5_VAR_INFO_T *hdf5_var;
203
204
1.00M
    assert(grp && grp->format_grp_info && dataset);
205
206
    /* Find the requested varid. */
207
1.00M
    if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid)))
208
0
        return NC_ENOTVAR;
209
1.00M
    assert(var && var->hdr.id == varid && var->format_var_info);
210
1.00M
    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
211
212
    /* Open this dataset if necessary. */
213
1.00M
    if (!hdf5_var->hdf_datasetid)
214
0
    {
215
0
        NC_HDF5_GRP_INFO_T *hdf5_grp;
216
0
        hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
217
218
0
        if ((hdf5_var->hdf_datasetid = H5Dopen2(hdf5_grp->hdf_grpid,
219
0
                                                var->hdr.name, H5P_DEFAULT)) < 0)
220
0
            return NC_ENOTVAR;
221
0
    }
222
223
1.00M
    *dataset = hdf5_var->hdf_datasetid;
224
225
1.00M
    return NC_NOERR;
226
1.00M
}
227
228
/**
229
 * @internal What fill value should be used for a variable?
230
 *
231
 * @param h5 Pointer to HDF5 file info struct.
232
 * @param var Pointer to variable info struct.
233
 * @param fillp Pointer that gets pointer to fill value.
234
 *
235
 * @returns NC_NOERR No error.
236
 * @returns NC_ENOMEM Out of memory.
237
 * @author Ed Hartnett
238
 */
239
int
240
nc4_get_fill_value(NC_FILE_INFO_T *h5, NC_VAR_INFO_T *var, void **fillp)
241
70.6k
{
242
70.6k
    size_t size;
243
70.6k
    int retval;
244
245
    /* Find out how much space we need for this type's fill value. */
246
70.6k
    if (var->type_info->nc_type_class == NC_VLEN)
247
0
        size = sizeof(nc_vlen_t);
248
70.6k
    else if (var->type_info->nc_type_class == NC_STRING)
249
0
        size = sizeof(char *);
250
70.6k
    else
251
70.6k
    {
252
70.6k
        if ((retval = nc4_get_typelen_mem(h5, var->type_info->hdr.id, &size)))
253
0
            return retval;
254
70.6k
    }
255
70.6k
    assert(size);
256
257
    /* Allocate the space. */
258
70.6k
    if (!((*fillp) = calloc(1, size)))
259
0
        return NC_ENOMEM;
260
261
    /* If the user has set a fill_value for this var, use, otherwise
262
     * find the default fill value. */
263
70.6k
    if (var->fill_value)
264
464
    {
265
464
        LOG((4, "Found a fill value for var %s", var->hdr.name));
266
464
        if (var->type_info->nc_type_class == NC_VLEN)
267
0
        {
268
0
            nc_vlen_t *in_vlen = (nc_vlen_t *)(var->fill_value), *fv_vlen = (nc_vlen_t *)(*fillp);
269
0
            size_t basetypesize = 0;
270
271
0
            if((retval=nc4_get_typelen_mem(h5, var->type_info->u.v.base_nc_typeid, &basetypesize)))
272
0
                return retval;
273
274
0
            fv_vlen->len = in_vlen->len;
275
0
            if (!(fv_vlen->p = malloc(basetypesize * in_vlen->len)))
276
0
            {
277
0
                free(*fillp);
278
0
                *fillp = NULL;
279
0
                return NC_ENOMEM;
280
0
            }
281
0
            memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * basetypesize);
282
0
        }
283
464
        else if (var->type_info->nc_type_class == NC_STRING)
284
0
        {
285
0
            if (*(char **)var->fill_value)
286
0
                if (!(**(char ***)fillp = strdup(*(char **)var->fill_value)))
287
0
                {
288
0
                    free(*fillp);
289
0
                    *fillp = NULL;
290
0
                    return NC_ENOMEM;
291
0
                }
292
0
        }
293
464
        else
294
464
            memcpy((*fillp), var->fill_value, size);
295
464
    }
296
70.2k
    else
297
70.2k
    {
298
70.2k
        if (nc4_get_default_fill_value(var->type_info, *fillp))
299
0
        {
300
            /* Note: release memory, but don't return error on failure */
301
0
            free(*fillp);
302
0
            *fillp = NULL;
303
0
        }
304
70.2k
    }
305
306
70.6k
    return NC_NOERR;
307
70.6k
}
308
309
/**
310
 * @internal Given a netcdf type, return appropriate HDF typeid.  (All
311
 * hdf_typeid's returned from this routine must be H5Tclosed by the
312
 * caller).
313
 *
314
 * @param h5 Pointer to HDF5 file info struct.
315
 * @param xtype NetCDF type ID.
316
 * @param hdf_typeid Pointer that gets the HDF5 type ID.
317
 * @param endianness Desired endianness in HDF5 type.
318
 *
319
 * @return NC_NOERR No error.
320
 * @return NC_ECHAR Conversions of NC_CHAR forbidden.
321
 * @return NC_EVARMETA HDF5 returning error creating datatype.
322
 * @return NC_EHDFERR HDF5 returning error.
323
 * @return NC_EBADTYE Type not found.
324
 * @author Ed Hartnett
325
 */
326
int
327
nc4_get_hdf_typeid(NC_FILE_INFO_T *h5, nc_type xtype,
328
                   hid_t *hdf_typeid, int endianness)
329
276k
{
330
276k
    NC_TYPE_INFO_T *type;
331
276k
    hid_t typeid = 0;
332
276k
    int retval = NC_NOERR;
333
334
276k
    assert(hdf_typeid && h5);
335
336
276k
    *hdf_typeid = -1;
337
338
    /* Determine an appropriate HDF5 datatype */
339
276k
    if (xtype == NC_NAT)
340
0
        return NC_EBADTYPE;
341
276k
    else if (xtype == NC_CHAR || xtype == NC_STRING)
342
44.2k
    {
343
        /* NC_CHAR & NC_STRING types create a new HDF5 datatype */
344
44.2k
        if (xtype == NC_CHAR)
345
44.2k
        {
346
44.2k
            if ((typeid = H5Tcopy(H5T_C_S1)) < 0)
347
0
                return NC_EHDFERR;
348
44.2k
            if (H5Tset_strpad(typeid, H5T_STR_NULLTERM) < 0)
349
0
                BAIL(NC_EVARMETA);
350
44.2k
            if(H5Tset_cset(typeid, H5T_CSET_ASCII) < 0)
351
0
                BAIL(NC_EVARMETA);
352
353
            /* Take ownership of the newly created HDF5 datatype */
354
44.2k
            *hdf_typeid = typeid;
355
44.2k
            typeid = 0;
356
44.2k
        }
357
0
        else
358
0
        {
359
0
            if ((typeid = H5Tcopy(H5T_C_S1)) < 0)
360
0
                return NC_EHDFERR;
361
0
            if (H5Tset_size(typeid, H5T_VARIABLE) < 0)
362
0
                BAIL(NC_EVARMETA);
363
0
            if(H5Tset_cset(typeid, H5T_CSET_UTF8) < 0)
364
0
                BAIL(NC_EVARMETA);
365
366
            /* Take ownership of the newly created HDF5 datatype */
367
0
            *hdf_typeid = typeid;
368
0
            typeid = 0;
369
0
        }
370
44.2k
    }
371
232k
    else
372
232k
    {
373
        /* All other types use an existing HDF5 datatype */
374
232k
        switch (xtype)
375
232k
        {
376
185k
        case NC_BYTE: /* signed 1 byte integer */
377
185k
            if (endianness == NC_ENDIAN_LITTLE)
378
0
                typeid = H5T_STD_I8LE;
379
185k
            else if (endianness == NC_ENDIAN_BIG)
380
0
                typeid = H5T_STD_I8BE;
381
185k
            else
382
185k
                typeid = H5T_NATIVE_SCHAR;
383
185k
            break;
384
385
3.47k
        case NC_SHORT: /* signed 2 byte integer */
386
3.47k
            if (endianness == NC_ENDIAN_LITTLE)
387
0
                typeid = H5T_STD_I16LE;
388
3.47k
            else if (endianness == NC_ENDIAN_BIG)
389
0
                typeid = H5T_STD_I16BE;
390
3.47k
            else
391
3.47k
                typeid = H5T_NATIVE_SHORT;
392
3.47k
            break;
393
394
30.1k
        case NC_INT:
395
30.1k
            if (endianness == NC_ENDIAN_LITTLE)
396
0
                typeid = H5T_STD_I32LE;
397
30.1k
            else if (endianness == NC_ENDIAN_BIG)
398
0
                typeid = H5T_STD_I32BE;
399
30.1k
            else
400
30.1k
                typeid = H5T_NATIVE_INT;
401
30.1k
            break;
402
403
348
        case NC_UBYTE:
404
348
            if (endianness == NC_ENDIAN_LITTLE)
405
0
                typeid = H5T_STD_U8LE;
406
348
            else if (endianness == NC_ENDIAN_BIG)
407
0
                typeid = H5T_STD_U8BE;
408
348
            else
409
348
                typeid = H5T_NATIVE_UCHAR;
410
348
            break;
411
412
93
        case NC_USHORT:
413
93
            if (endianness == NC_ENDIAN_LITTLE)
414
0
                typeid = H5T_STD_U16LE;
415
93
            else if (endianness == NC_ENDIAN_BIG)
416
0
                typeid = H5T_STD_U16BE;
417
93
            else
418
93
                typeid = H5T_NATIVE_USHORT;
419
93
            break;
420
421
1.56k
        case NC_UINT:
422
1.56k
            if (endianness == NC_ENDIAN_LITTLE)
423
0
                typeid = H5T_STD_U32LE;
424
1.56k
            else if (endianness == NC_ENDIAN_BIG)
425
0
                typeid = H5T_STD_U32BE;
426
1.56k
            else
427
1.56k
                typeid = H5T_NATIVE_UINT;
428
1.56k
            break;
429
430
837
        case NC_INT64:
431
837
            if (endianness == NC_ENDIAN_LITTLE)
432
0
                typeid = H5T_STD_I64LE;
433
837
            else if (endianness == NC_ENDIAN_BIG)
434
0
                typeid = H5T_STD_I64BE;
435
837
            else
436
837
                typeid = H5T_NATIVE_LLONG;
437
837
            break;
438
439
9
        case NC_UINT64:
440
9
            if (endianness == NC_ENDIAN_LITTLE)
441
0
                typeid = H5T_STD_U64LE;
442
9
            else if (endianness == NC_ENDIAN_BIG)
443
0
                typeid = H5T_STD_U64BE;
444
9
            else
445
9
                typeid = H5T_NATIVE_ULLONG;
446
9
            break;
447
448
2.23k
        case NC_FLOAT:
449
2.23k
            if (endianness == NC_ENDIAN_LITTLE)
450
0
                typeid = H5T_IEEE_F32LE;
451
2.23k
            else if (endianness == NC_ENDIAN_BIG)
452
0
                typeid = H5T_IEEE_F32BE;
453
2.23k
            else
454
2.23k
                typeid = H5T_NATIVE_FLOAT;
455
2.23k
            break;
456
457
8.44k
        case NC_DOUBLE:
458
8.44k
            if (endianness == NC_ENDIAN_LITTLE)
459
0
                typeid = H5T_IEEE_F64LE;
460
8.44k
            else if (endianness == NC_ENDIAN_BIG)
461
0
                typeid = H5T_IEEE_F64BE;
462
8.44k
            else
463
8.44k
                typeid = H5T_NATIVE_DOUBLE;
464
8.44k
            break;
465
466
0
        default:
467
            /* Maybe this is a user defined type? */
468
0
            if (nc4_find_type(h5, xtype, &type))
469
0
                return NC_EBADTYPE;
470
0
            if (!type)
471
0
                return NC_EBADTYPE;
472
0
            typeid = ((NC_HDF5_TYPE_INFO_T *)type->format_type_info)->hdf_typeid;
473
0
            break;
474
232k
        }
475
232k
        assert(typeid);
476
477
        /* Copy the HDF5 datatype, so the function operates uniformly */
478
232k
        if ((*hdf_typeid = H5Tcopy(typeid)) < 0)
479
0
            return NC_EHDFERR;
480
232k
        typeid = 0;
481
232k
    }
482
276k
    assert(*hdf_typeid != -1);
483
484
276k
exit:
485
276k
    if (typeid > 0 && H5Tclose(typeid) < 0)
486
0
        BAIL2(NC_EHDFERR);
487
276k
    return retval;
488
276k
}
489
490
/**
491
 * @internal Write an attribute.
492
 *
493
 * @param grp Pointer to group info struct.
494
 * @param varid Variable ID or NC_GLOBAL.
495
 * @param att Pointer to attribute info struct.
496
 *
497
 * @returns ::NC_NOERR No error.
498
 * @returns ::NC_ENOTVAR Variable not found.
499
 * @returns ::NC_EPERM Read-only file.
500
 * @returns ::NC_EHDFERR HDF5 returned error.
501
 * @returns ::NC_EATTMETA HDF5 returned error with attribute calls.
502
 * @author Ed Hartnett
503
 */
504
static int
505
put_att_grpa(NC_GRP_INFO_T *grp, int varid, NC_ATT_INFO_T *att)
506
65.5k
{
507
65.5k
    NC_HDF5_GRP_INFO_T *hdf5_grp;
508
65.5k
    hid_t datasetid = 0, locid;
509
65.5k
    hid_t attid = 0, spaceid = 0, file_typeid = 0;
510
65.5k
    hid_t existing_att_typeid = 0, existing_attid = 0, existing_spaceid = 0;
511
65.5k
    hsize_t dims[1]; /* netcdf attributes always 1-D. */
512
65.5k
    htri_t attr_exists;
513
65.5k
    void *data;
514
65.5k
    int phoney_data = 99;
515
65.5k
    int retval = NC_NOERR;
516
517
65.5k
    assert(att->hdr.name && grp && grp->format_grp_info);
518
65.5k
    LOG((3, "%s: varid %d att->hdr.id %d att->hdr.name %s att->nc_typeid %d "
519
65.5k
         "att->len %d", __func__, varid, att->hdr.id, att->hdr.name,
520
65.5k
         att->nc_typeid, att->len));
521
522
    /* Get HDF5-specific group info. */
523
65.5k
    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
524
525
    /* If the file is read-only, return an error. */
526
65.5k
    if (grp->nc4_info->no_write)
527
0
        BAIL(NC_EPERM);
528
529
    /* Get the hid to attach the attribute to, or read it from. */
530
65.5k
    if (varid == NC_GLOBAL)
531
53.3k
        locid = hdf5_grp->hdf_grpid;
532
12.1k
    else
533
12.1k
    {
534
12.1k
        if ((retval = nc4_open_var_grp2(grp, varid, &datasetid)))
535
0
            BAIL(retval);
536
12.1k
        locid = datasetid;
537
12.1k
    }
538
539
    /* Get the length ready, and find the HDF type we'll be
540
     * writing. */
541
65.5k
    dims[0] = att->len;
542
65.5k
    if ((retval = nc4_get_hdf_typeid(grp->nc4_info, att->nc_typeid,
543
65.5k
                                     &file_typeid, 0)))
544
0
        BAIL(retval);
545
546
    /* Even if the length is zero, HDF5 won't let me write with a
547
     * NULL pointer. So if the length of the att is zero, point to
548
     * some phoney data (which won't be written anyway.)*/
549
65.5k
    if (!dims[0])
550
11.5k
        data = &phoney_data;
551
54.0k
    else if (att->data)
552
54.0k
        data = att->data;
553
0
    else if (att->stdata)
554
0
        data = att->stdata;
555
0
    else
556
0
        data = att->vldata;
557
558
    /* NC_CHAR types require some extra work. The space ID is set to
559
     * scalar, and the type is told how long the string is. If it's
560
     * really zero length, set the size to 1. (The fact that it's
561
     * really zero will be marked by the NULL dataspace, but HDF5
562
     * doesn't allow me to set the size of the type to zero.)*/
563
65.5k
    if (att->nc_typeid == NC_CHAR)
564
25.7k
    {
565
25.7k
        size_t string_size = dims[0];
566
25.7k
        if (!string_size)
567
11.5k
        {
568
11.5k
            string_size = 1;
569
11.5k
            if ((spaceid = H5Screate(H5S_NULL)) < 0)
570
0
                BAIL(NC_EATTMETA);
571
11.5k
        }
572
14.2k
        else
573
14.2k
        {
574
14.2k
            if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
575
0
                BAIL(NC_EATTMETA);
576
14.2k
        }
577
25.7k
        if (H5Tset_size(file_typeid, string_size) < 0)
578
0
            BAIL(NC_EATTMETA);
579
25.7k
        if (H5Tset_strpad(file_typeid, H5T_STR_NULLTERM) < 0)
580
0
            BAIL(NC_EATTMETA);
581
25.7k
    }
582
39.7k
    else
583
39.7k
    {
584
39.7k
        if (!att->len)
585
0
        {
586
0
            if ((spaceid = H5Screate(H5S_NULL)) < 0)
587
0
                BAIL(NC_EATTMETA);
588
0
        }
589
39.7k
        else
590
39.7k
        {
591
39.7k
            if ((spaceid = H5Screate_simple(1, dims, NULL)) < 0)
592
0
                BAIL(NC_EATTMETA);
593
39.7k
        }
594
39.7k
    }
595
596
    /* Does the att exists already? */
597
65.5k
    if ((attr_exists = H5Aexists(locid, att->hdr.name)) < 0)
598
0
        BAIL(NC_EHDFERR);
599
65.5k
    if (attr_exists)
600
0
    {
601
0
        hssize_t npoints;
602
603
        /* Open the attribute. */
604
0
        if ((existing_attid = H5Aopen(locid, att->hdr.name, H5P_DEFAULT)) < 0)
605
0
            BAIL(NC_EATTMETA);
606
607
        /* Find the type of the existing attribute. */
608
0
        if ((existing_att_typeid = H5Aget_type(existing_attid)) < 0)
609
0
            BAIL(NC_EATTMETA);
610
611
        /* How big is the attribute? */
612
0
        if ((existing_spaceid = H5Aget_space(existing_attid)) < 0)
613
0
            BAIL(NC_EATTMETA);
614
0
        if ((npoints = H5Sget_simple_extent_npoints(existing_spaceid)) < 0)
615
0
            BAIL(NC_EATTMETA);
616
617
        /* For text attributes the size is specified in the datatype
618
           and it is enough to compare types using H5Tequal(). */
619
0
        if (!H5Tequal(file_typeid, existing_att_typeid) ||
620
0
            (att->nc_typeid != NC_CHAR && npoints != att->len))
621
0
        {
622
            /* The attribute exists but we cannot re-use it. */
623
624
            /* Delete the attribute. */
625
0
            if (H5Adelete(locid, att->hdr.name) < 0)
626
0
                BAIL(NC_EHDFERR);
627
628
            /* Re-create the attribute with the type and length
629
               reflecting the new value (or values). */
630
0
            if ((attid = H5Acreate(locid, att->hdr.name, file_typeid, spaceid,
631
0
                                   H5P_DEFAULT)) < 0)
632
0
                BAIL(NC_EATTMETA);
633
634
            /* Write the values, (even if length is zero). */
635
0
            if (H5Awrite(attid, file_typeid, data) < 0)
636
0
                BAIL(NC_EATTMETA);
637
0
        }
638
0
        else
639
0
        {
640
            /* The attribute exists and we can re-use it. */
641
642
            /* Write the values, re-using the existing attribute. */
643
0
            if (H5Awrite(existing_attid, file_typeid, data) < 0)
644
0
                BAIL(NC_EATTMETA);
645
0
        }
646
0
    }
647
65.5k
    else
648
65.5k
    {
649
        /* The attribute does not exist yet. */
650
651
        /* Create the attribute. */
652
65.5k
        if ((attid = H5Acreate(locid, att->hdr.name, file_typeid, spaceid,
653
65.5k
                               H5P_DEFAULT)) < 0)
654
0
            BAIL(NC_EATTMETA);
655
656
        /* Write the values, (even if length is zero). */
657
65.5k
        if (H5Awrite(attid, file_typeid, data) < 0)
658
0
            BAIL(NC_EATTMETA);
659
65.5k
    }
660
661
65.5k
exit:
662
65.5k
    if (file_typeid && H5Tclose(file_typeid))
663
0
        BAIL2(NC_EHDFERR);
664
65.5k
    if (attid > 0 && H5Aclose(attid) < 0)
665
0
        BAIL2(NC_EHDFERR);
666
65.5k
    if (existing_att_typeid && H5Tclose(existing_att_typeid))
667
0
        BAIL2(NC_EHDFERR);
668
65.5k
    if (existing_attid > 0 && H5Aclose(existing_attid) < 0)
669
0
        BAIL2(NC_EHDFERR);
670
65.5k
    if (spaceid > 0 && H5Sclose(spaceid) < 0)
671
0
        BAIL2(NC_EHDFERR);
672
65.5k
    if (existing_spaceid > 0 && H5Sclose(existing_spaceid) < 0)
673
0
        BAIL2(NC_EHDFERR);
674
65.5k
    return retval;
675
65.5k
}
676
677
/**
678
 * @internal Write all the dirty atts in an attlist.
679
 *
680
 * @param attlist Pointer to the list if attributes.
681
 * @param varid Variable ID.
682
 * @param grp Pointer to group info struct.
683
 *
684
 * @returns NC_NOERR No error.
685
 * @returns NC_EHDFERR HDF5 returned an error.
686
 * @author Ed Hartnett
687
 */
688
static int
689
write_attlist(NCindex *attlist, int varid, NC_GRP_INFO_T *grp)
690
78.2k
{
691
78.2k
    NC_ATT_INFO_T *att;
692
78.2k
    int retval;
693
78.2k
    int i;
694
695
148k
    for(i = 0; i < ncindexsize(attlist); i++)
696
70.3k
    {
697
70.3k
        att = (NC_ATT_INFO_T *)ncindexith(attlist, i);
698
70.3k
        assert(att);
699
70.3k
        if (att->dirty)
700
65.5k
        {
701
65.5k
            LOG((4, "%s: writing att %s to varid %d", __func__, att->hdr.name, varid));
702
65.5k
            if ((retval = put_att_grpa(grp, varid, att)))
703
0
                return retval;
704
65.5k
            att->dirty = NC_FALSE;
705
65.5k
            att->created = NC_TRUE;
706
65.5k
        }
707
70.3k
    }
708
78.2k
    return NC_NOERR;
709
78.2k
}
710
711
/**
712
 * @internal HDF5 dimension scales cannot themselves have scales
713
 * attached. This is a problem for multidimensional coordinate
714
 * variables. So this function writes a special attribute for such a
715
 * variable, which has the ids of all the dimensions for that
716
 * coordinate variable.
717
 *
718
 * @param var Pointer to var info struct.
719
 *
720
 * @returns NC_NOERR No error.
721
 * @returns NC_EHDFERR HDF5 returned an error.
722
 * @author Ed Hartnett
723
 */
724
static int
725
write_coord_dimids(NC_VAR_INFO_T *var)
726
10.8k
{
727
10.8k
    NC_HDF5_VAR_INFO_T *hdf5_var;
728
10.8k
    hsize_t coords_len[1];
729
10.8k
    hid_t c_spaceid = -1, c_attid = -1;
730
10.8k
    int retval = NC_NOERR;
731
732
10.8k
    assert(var && var->format_var_info);
733
734
    /* Get HDF5-specific var info. */
735
10.8k
    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
736
737
    /* Set up space for attribute. */
738
10.8k
    coords_len[0] = var->ndims;
739
10.8k
    if ((c_spaceid = H5Screate_simple(1, coords_len, coords_len)) < 0)
740
0
        BAIL(NC_EHDFERR);
741
742
    /* Create the attribute. */
743
10.8k
    if ((c_attid = H5Acreate(hdf5_var->hdf_datasetid, COORDINATES,
744
10.8k
                             H5T_NATIVE_INT, c_spaceid, H5P_DEFAULT)) < 0)
745
0
        BAIL(NC_EHDFERR);
746
747
    /* Write our attribute. */
748
10.8k
    if (H5Awrite(c_attid, H5T_NATIVE_INT, var->dimids) < 0)
749
0
        BAIL(NC_EHDFERR);
750
751
10.8k
exit:
752
10.8k
    if (c_spaceid >= 0 && H5Sclose(c_spaceid) < 0)
753
0
        BAIL2(NC_EHDFERR);
754
10.8k
    if (c_attid >= 0 && H5Aclose(c_attid) < 0)
755
0
        BAIL2(NC_EHDFERR);
756
10.8k
    return retval;
757
10.8k
}
758
759
/**
760
 * @internal Write a special attribute for the netCDF-4 dimension ID.
761
 *
762
 * @param datasetid HDF5 datasset ID.
763
 * @param dimid NetCDF dimension ID.
764
 *
765
 * @returns NC_NOERR No error.
766
 * @returns NC_EHDFERR HDF5 returned an error.
767
 * @author Ed Hartnett
768
 */
769
static int
770
write_netcdf4_dimid(hid_t datasetid, int dimid)
771
46.6k
{
772
46.6k
    hid_t dimid_spaceid = -1, dimid_attid = -1;
773
46.6k
    htri_t attr_exists;
774
46.6k
    int retval = NC_NOERR;
775
776
    /* Create the space. */
777
46.6k
    if ((dimid_spaceid = H5Screate(H5S_SCALAR)) < 0)
778
0
        BAIL(NC_EHDFERR);
779
780
    /* Does the attribute already exist? If so, don't try to create it. */
781
46.6k
    if ((attr_exists = H5Aexists(datasetid, NC_DIMID_ATT_NAME)) < 0)
782
0
        BAIL(NC_EHDFERR);
783
46.6k
    if (attr_exists)
784
12.1k
        dimid_attid = H5Aopen_by_name(datasetid, ".", NC_DIMID_ATT_NAME,
785
12.1k
                                      H5P_DEFAULT, H5P_DEFAULT);
786
34.4k
    else
787
        /* Create the attribute if needed. */
788
34.4k
        dimid_attid = H5Acreate(datasetid, NC_DIMID_ATT_NAME,
789
34.4k
                                H5T_NATIVE_INT, dimid_spaceid, H5P_DEFAULT);
790
46.6k
    if (dimid_attid  < 0)
791
0
        BAIL(NC_EHDFERR);
792
793
794
    /* Write it. */
795
46.6k
    LOG((4, "%s: writing secret dimid %d", __func__, dimid));
796
46.6k
    if (H5Awrite(dimid_attid, H5T_NATIVE_INT, &dimid) < 0)
797
0
        BAIL(NC_EHDFERR);
798
799
46.6k
exit:
800
    /* Close stuff*/
801
46.6k
    if (dimid_spaceid >= 0 && H5Sclose(dimid_spaceid) < 0)
802
0
        BAIL2(NC_EHDFERR);
803
46.6k
    if (dimid_attid >= 0 && H5Aclose(dimid_attid) < 0)
804
0
        BAIL2(NC_EHDFERR);
805
806
46.6k
    return retval;
807
46.6k
}
808
809
/**
810
 * @internal This function creates the HDF5 dataset for a variable.
811
 *
812
 * @param grp Pointer to group info struct.
813
 * @param var Pointer to variable info struct.
814
 * @param write_dimid True to write dimid.
815
 *
816
 * @return ::NC_NOERR
817
 * @returns NC_ECHAR Conversions of NC_CHAR forbidden.
818
 * @returns NC_EVARMETA HDF5 returning error creating datatype.
819
 * @returns NC_EHDFERR HDF5 returning error.
820
 * @returns NC_EBADTYE Type not found.
821
 * @author Ed Hartnett
822
 */
823
static int
824
var_create_dataset(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, nc_bool_t write_dimid)
825
70.3k
{
826
70.3k
    NC_HDF5_GRP_INFO_T *hdf5_grp;
827
70.3k
    NC_HDF5_VAR_INFO_T *hdf5_var;
828
70.3k
    hid_t plistid = 0, access_plistid = 0, typeid = 0, spaceid = 0;
829
70.3k
    hsize_t chunksize[H5S_MAX_RANK], dimsize[H5S_MAX_RANK], maxdimsize[H5S_MAX_RANK];
830
70.3k
    int d;
831
70.3k
    void *fillp = NULL;
832
70.3k
    NC_DIM_INFO_T *dim = NULL;
833
70.3k
    char *name_to_use;
834
70.3k
    int retval;
835
836
70.3k
    assert(grp && grp->format_grp_info && var && var->format_var_info);
837
838
70.3k
    LOG((3, "%s:: name %s", __func__, var->hdr.name));
839
840
    /* Get HDF5-specific group and var info. */
841
70.3k
    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
842
70.3k
    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
843
844
    /* Scalar or not, we need a creation property list. */
845
70.3k
    if ((plistid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
846
0
        BAIL(NC_EHDFERR);
847
70.3k
    if ((access_plistid = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
848
0
        BAIL(NC_EHDFERR);
849
850
    /* Turn off object tracking times in HDF5. */
851
70.3k
    if (H5Pset_obj_track_times(plistid, 0) < 0)
852
0
        BAIL(NC_EHDFERR);
853
854
    /* Find the HDF5 type of the dataset. */
855
70.3k
    if ((retval = nc4_get_hdf_typeid(grp->nc4_info, var->type_info->hdr.id, &typeid,
856
70.3k
                                     var->type_info->endianness)))
857
0
        BAIL(retval);
858
859
    /* Figure out what fill value to set, if any. */
860
70.3k
    if (var->no_fill)
861
0
    {
862
        /* Required to truly turn HDF5 fill values off */
863
0
        if (H5Pset_fill_time(plistid, H5D_FILL_TIME_NEVER) < 0)
864
0
            BAIL(NC_EHDFERR);
865
0
    }
866
70.3k
    else
867
70.3k
    {
868
70.3k
        if ((retval = nc4_get_fill_value(grp->nc4_info, var, &fillp)))
869
0
            BAIL(retval);
870
871
        /* If there is a fill value, set it. */
872
70.3k
        if (fillp)
873
70.3k
        {
874
70.3k
            if (var->type_info->nc_type_class == NC_STRING)
875
0
            {
876
0
                if (H5Pset_fill_value(plistid, typeid, fillp) < 0)
877
0
                    BAIL(NC_EHDFERR);
878
0
            }
879
70.3k
            else
880
70.3k
            {
881
                /* The fill value set in HDF5 must always be presented as
882
                 * a native type, even if the endianness for this dataset
883
                 * is non-native. HDF5 will translate the fill value to
884
                 * the target endiannesss. */
885
70.3k
                hid_t fill_typeid = 0;
886
887
70.3k
                if ((retval = nc4_get_hdf_typeid(grp->nc4_info, var->type_info->hdr.id, &fill_typeid,
888
70.3k
                                                 NC_ENDIAN_NATIVE)))
889
0
                    BAIL(retval);
890
70.3k
                if (H5Pset_fill_value(plistid, fill_typeid, fillp) < 0)
891
0
                {
892
0
                    if (H5Tclose(fill_typeid) < 0)
893
0
                        BAIL(NC_EHDFERR);
894
0
                    BAIL(NC_EHDFERR);
895
0
                }
896
70.3k
                if (H5Tclose(fill_typeid) < 0)
897
0
                    BAIL(NC_EHDFERR);
898
70.3k
            }
899
70.3k
        }
900
70.3k
    }
901
902
    /* If the user wants to shuffle the data, set that up now. */
903
70.3k
    if (var->shuffle) {
904
0
        if (H5Pset_shuffle(plistid) < 0)
905
0
            BAIL(NC_EHDFERR);
906
0
    }
907
908
    /* If the user wants to compress the data, using either zlib
909
     * (a.k.a deflate) or szip, or another filter, set that up now.
910
     * Szip and zip can be turned on
911
     * either directly with nc_def_var_szip/deflate(), or using
912
     * nc_def_var_filter(). If the user
913
     * has specified a filter, it will be applied here. */
914
70.3k
    if(var->filters != NULL) {
915
0
  int j;
916
0
  for(j=0;j<nclistlength(var->filters);j++) {
917
0
      NC_FILTER_SPEC_HDF5* fi = (NC_FILTER_SPEC_HDF5*)nclistget(var->filters,j);
918
0
      size_t nparams;
919
0
      unsigned int* params;
920
0
      nparams = fi->nparams;
921
0
      params = fi->params;
922
0
            if(fi->filterid == H5Z_FILTER_DEFLATE) {/* Handle zip case here */
923
0
                unsigned level;
924
0
                if(nparams != 1)
925
0
                    BAIL(NC_EFILTER);
926
0
                level = (int)params[0];
927
0
                if(H5Pset_deflate(plistid, level) < 0)
928
0
                    BAIL(NC_EFILTER);
929
0
            } else if(fi->filterid == H5Z_FILTER_SZIP) {/* Handle szip case here */
930
0
                int options_mask;
931
0
                int bits_per_pixel;
932
0
                if(nparams != 2)
933
0
                    BAIL(NC_EFILTER);
934
0
                options_mask = (int)params[0];
935
0
                bits_per_pixel = (int)params[1];
936
0
                if(H5Pset_szip(plistid, options_mask, bits_per_pixel) < 0)
937
0
                    BAIL(NC_EFILTER);
938
0
            } else {
939
0
                herr_t code = H5Pset_filter(plistid, (unsigned int)fi->filterid, H5Z_FLAG_MANDATORY, nparams, params);
940
0
                if(code < 0) {
941
0
                    BAIL(NC_EFILTER);
942
0
                }
943
0
            }
944
0
  }
945
0
    }
946
947
    /* If the user wants to fletcher error correction, set that up now. */
948
70.3k
    if (var->fletcher32)
949
0
        if (H5Pset_fletcher32(plistid) < 0)
950
0
            BAIL(NC_EHDFERR);
951
952
    /* If ndims non-zero, get info for all dimensions. We look up the
953
       dimids and get the len of each dimension. We need this to create
954
       the space for the dataset. In netCDF a dimension length of zero
955
       means an unlimited dimension. */
956
70.3k
    if (var->ndims)
957
28.0k
    {
958
28.0k
        int unlimdim = 0;
959
960
        /* Check to see if any unlimited dimensions are used in this var. */
961
71.9k
        for (d = 0; d < var->ndims; d++) {
962
43.9k
            dim = var->dim[d];
963
43.9k
            assert(dim && dim->hdr.id == var->dimids[d]);
964
43.9k
            if (dim->unlimited)
965
21.6k
                unlimdim++;
966
43.9k
        }
967
968
        /* If there are no unlimited dims, and no filters, and the user
969
         * has not specified chunksizes, use contiguous variable for
970
         * better performance. */
971
28.0k
        if (!var->shuffle && !var->fletcher32 && nclistlength(var->filters) == 0 &&
972
28.0k
            (var->chunksizes == NULL || !var->chunksizes[0]) && !unlimdim)
973
0
      var->storage = NC_CONTIGUOUS;
974
975
        /* Gather current & maximum dimension sizes, along with chunk
976
         * sizes. */
977
71.9k
        for (d = 0; d < var->ndims; d++)
978
43.9k
        {
979
43.9k
            dim = var->dim[d];
980
43.9k
            assert(dim && dim->hdr.id == var->dimids[d]);
981
43.9k
            dimsize[d] = dim->unlimited ? NC_HDF5_UNLIMITED_DIMSIZE : dim->len;
982
43.9k
            maxdimsize[d] = dim->unlimited ? H5S_UNLIMITED : (hsize_t)dim->len;
983
43.9k
            if (var->storage == NC_CHUNKED)
984
23.4k
            {
985
23.4k
                if (var->chunksizes[d])
986
23.4k
                    chunksize[d] = var->chunksizes[d];
987
0
                else
988
0
                {
989
0
                    size_t type_size;
990
0
                    if (var->type_info->nc_type_class == NC_STRING)
991
0
                        type_size = sizeof(char *);
992
0
                    else
993
0
                        type_size = var->type_info->size;
994
995
                    /* Unlimited dim always gets chunksize of 1. */
996
0
                    if (dim->unlimited)
997
0
                        chunksize[d] = 1;
998
0
                    else
999
0
                        chunksize[d] = pow((double)DEFAULT_CHUNK_SIZE/type_size,
1000
0
                                           1/(double)(var->ndims - unlimdim));
1001
1002
                    /* If the chunksize is greater than the dim
1003
                     * length, make it the dim length. */
1004
0
                    if (!dim->unlimited && chunksize[d] > dim->len)
1005
0
                        chunksize[d] = dim->len;
1006
1007
                    /* Remember the computed chunksize */
1008
0
                    var->chunksizes[d] = chunksize[d];
1009
0
                }
1010
23.4k
            }
1011
43.9k
        }
1012
1013
        /* Create the dataspace. */
1014
28.0k
        if ((spaceid = H5Screate_simple(var->ndims, dimsize, maxdimsize)) < 0)
1015
0
            BAIL(NC_EHDFERR);
1016
28.0k
    }
1017
42.2k
    else
1018
42.2k
    {
1019
42.2k
        if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
1020
0
            BAIL(NC_EHDFERR);
1021
42.2k
    }
1022
1023
    /* Set the var storage to contiguous, compact, or chunked. Don't
1024
     * try to set chunking for scalar vars, they will default to
1025
     * contiguous if not set to compact. */
1026
70.3k
    if (var->storage == NC_CONTIGUOUS)
1027
57.5k
    {
1028
57.5k
        if (H5Pset_layout(plistid, H5D_CONTIGUOUS) < 0)
1029
0
            BAIL(NC_EHDFERR);
1030
57.5k
    }
1031
12.7k
    else if (var->storage == NC_COMPACT)
1032
0
    {
1033
0
        if (H5Pset_layout(plistid, H5D_COMPACT) < 0)
1034
0
            BAIL(NC_EHDFERR);
1035
0
    }
1036
12.7k
    else if (var->ndims)
1037
12.7k
    {
1038
12.7k
        if (H5Pset_chunk(plistid, var->ndims, chunksize) < 0)
1039
0
            BAIL(NC_EHDFERR);
1040
12.7k
    }
1041
1042
    /* Turn on creation order tracking. */
1043
70.3k
    if (H5Pset_attr_creation_order(plistid, H5P_CRT_ORDER_TRACKED|
1044
70.3k
                                   H5P_CRT_ORDER_INDEXED) < 0)
1045
0
        BAIL(NC_EHDFERR);
1046
1047
    /* Set per-var chunk cache, for chunked datasets. */
1048
70.3k
    if (var->storage == NC_CHUNKED && var->chunk_cache_size)
1049
12.7k
        if (H5Pset_chunk_cache(access_plistid, var->chunk_cache_nelems,
1050
12.7k
                               var->chunk_cache_size, var->chunk_cache_preemption) < 0)
1051
0
            BAIL(NC_EHDFERR);
1052
1053
    /* At long last, create the dataset. */
1054
70.3k
    name_to_use = var->hdf5_name ? var->hdf5_name : var->hdr.name;
1055
70.3k
    LOG((4, "%s: about to H5Dcreate2 dataset %s of type 0x%x", __func__,
1056
70.3k
         name_to_use, typeid));
1057
70.3k
    if ((hdf5_var->hdf_datasetid = H5Dcreate2(hdf5_grp->hdf_grpid, name_to_use, typeid,
1058
70.3k
                                              spaceid, H5P_DEFAULT, plistid, access_plistid)) < 0)
1059
0
        BAIL(NC_EHDFERR);
1060
70.3k
    var->created = NC_TRUE;
1061
70.3k
    var->is_new_var = NC_FALSE;
1062
1063
    /* Always write the hidden coordinates attribute, which lists the
1064
     * dimids of this var. When present, this speeds opens. When no
1065
     * present, dimscale matching is used. */
1066
70.3k
    if (var->ndims > 1)
1067
10.8k
        if ((retval = write_coord_dimids(var)))
1068
0
            BAIL(retval);
1069
1070
    /* If this is a dimscale, mark it as such in the HDF5 file. Also
1071
     * find the dimension info and store the dataset id of the dimscale
1072
     * dataset. */
1073
70.3k
    if (var->dimscale)
1074
1.61k
    {
1075
1.61k
        if (H5DSset_scale(hdf5_var->hdf_datasetid, var->hdr.name) < 0)
1076
0
            BAIL(NC_EHDFERR);
1077
1078
        /* If this is a multidimensional coordinate variable, write a
1079
         * coordinates attribute. */
1080
        /* if (var->ndims > 1) */
1081
        /*    if ((retval = write_coord_dimids(var))) */
1082
        /*       BAIL(retval); */
1083
1084
        /* If desired, write the netCDF dimid. */
1085
1.61k
        if (write_dimid)
1086
1.61k
            if ((retval = write_netcdf4_dimid(hdf5_var->hdf_datasetid, var->dimids[0])))
1087
0
                BAIL(retval);
1088
1.61k
    }
1089
1090
    /* Write attributes for this var. */
1091
70.3k
    if ((retval = write_attlist(var->att, var->hdr.id, grp)))
1092
0
        BAIL(retval);
1093
70.3k
    var->attr_dirty = NC_FALSE;
1094
1095
70.3k
exit:
1096
70.3k
    if (typeid > 0 && H5Tclose(typeid) < 0)
1097
0
        BAIL2(NC_EHDFERR);
1098
70.3k
    if (plistid > 0 && H5Pclose(plistid) < 0)
1099
0
        BAIL2(NC_EHDFERR);
1100
70.3k
    if (access_plistid > 0 && H5Pclose(access_plistid) < 0)
1101
0
        BAIL2(NC_EHDFERR);
1102
70.3k
    if (spaceid > 0 && H5Sclose(spaceid) < 0)
1103
0
        BAIL2(NC_EHDFERR);
1104
70.3k
    if (fillp)
1105
70.3k
    {
1106
70.3k
        if (var->type_info->nc_type_class == NC_VLEN)
1107
0
            nc_free_vlen((nc_vlen_t *)fillp);
1108
70.3k
        else if (var->type_info->nc_type_class == NC_STRING && *(char **)fillp)
1109
0
            free(*(char **)fillp);
1110
70.3k
        free(fillp);
1111
70.3k
    }
1112
1113
70.3k
    return retval;
1114
70.3k
}
1115
1116
/**
1117
 * @internal Adjust the chunk cache of a var for better
1118
 * performance.
1119
 *
1120
 * @note For contiguous and compact storage vars, or when parallel I/O
1121
 * is in use, this function will do nothing and return ::NC_NOERR;
1122
 *
1123
 * @param grp Pointer to group info struct.
1124
 * @param var Pointer to var info struct.
1125
 *
1126
 * @return ::NC_NOERR No error.
1127
 * @author Ed Hartnett
1128
 */
1129
int
1130
nc4_adjust_var_cache(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
1131
98.3k
{
1132
98.3k
    size_t chunk_size_bytes = 1;
1133
98.3k
    int d;
1134
98.3k
    int retval;
1135
1136
    /* Nothing to be done for contiguous or compact data. */
1137
98.3k
    if (var->storage != NC_CHUNKED)
1138
72.8k
        return NC_NOERR;
1139
1140
#ifdef USE_PARALLEL4
1141
    /* Don't set cache for files using parallel I/O. */
1142
    if (grp->nc4_info->parallel)
1143
        return NC_NOERR;
1144
#endif
1145
1146
    /* How many bytes in the chunk? */
1147
72.5k
    for (d = 0; d < var->ndims; d++)
1148
46.9k
        chunk_size_bytes *= var->chunksizes[d];
1149
25.5k
    if (var->type_info->size)
1150
25.5k
        chunk_size_bytes *= var->type_info->size;
1151
0
    else
1152
0
        chunk_size_bytes *= sizeof(char *);
1153
1154
    /* If the chunk cache is too small, and the user has not changed
1155
     * the default value of the chunk cache size, then increase the
1156
     * size of the cache. */
1157
25.5k
    if (var->chunk_cache_size == CHUNK_CACHE_SIZE)
1158
25.5k
        if (chunk_size_bytes > var->chunk_cache_size)
1159
0
        {
1160
0
            var->chunk_cache_size = chunk_size_bytes * DEFAULT_CHUNKS_IN_CACHE;
1161
0
            if (var->chunk_cache_size > MAX_DEFAULT_CACHE_SIZE)
1162
0
                var->chunk_cache_size = MAX_DEFAULT_CACHE_SIZE;
1163
0
            if ((retval = nc4_reopen_dataset(grp, var)))
1164
0
                return retval;
1165
0
        }
1166
1167
25.5k
    return NC_NOERR;
1168
25.5k
}
1169
1170
/**
1171
 * @internal Create a HDF5 defined type from a NC_TYPE_INFO_T struct,
1172
 * and commit it to the file.
1173
 *
1174
 * @param grp Pointer to group info struct.
1175
 * @param type Pointer to type info struct.
1176
 *
1177
 * @return NC_NOERR No error.
1178
 * @return NC_EHDFERR HDF5 error.
1179
 * @return NC_ECHAR Conversions of NC_CHAR forbidden.
1180
 * @return NC_EVARMETA HDF5 returning error creating datatype.
1181
 * @return NC_EHDFERR HDF5 returning error.
1182
 * @return NC_EBADTYE Type not found.
1183
 * @author Ed Hartnett
1184
 */
1185
static int
1186
commit_type(NC_GRP_INFO_T *grp, NC_TYPE_INFO_T *type)
1187
0
{
1188
0
    NC_HDF5_GRP_INFO_T *hdf5_grp;
1189
0
    NC_HDF5_TYPE_INFO_T *hdf5_type;
1190
0
    hid_t base_hdf_typeid;
1191
0
    int retval;
1192
1193
0
    assert(grp && grp->format_grp_info && type && type->format_type_info);
1194
1195
    /* Get HDF5-specific group and type info. */
1196
0
    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
1197
0
    hdf5_type = (NC_HDF5_TYPE_INFO_T *)type->format_type_info;
1198
1199
    /* Did we already record this type? */
1200
0
    if (type->committed)
1201
0
        return NC_NOERR;
1202
1203
    /* Is this a compound type? */
1204
0
    if (type->nc_type_class == NC_COMPOUND)
1205
0
    {
1206
0
        NC_FIELD_INFO_T *field;
1207
0
        hid_t hdf_base_typeid, hdf_typeid;
1208
0
        int i;
1209
1210
0
        if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_COMPOUND, type->size)) < 0)
1211
0
            return NC_EHDFERR;
1212
0
        LOG((4, "creating compound type %s hdf_typeid 0x%x", type->hdr.name,
1213
0
             hdf5_type->hdf_typeid));
1214
1215
0
        for(i=0;i<nclistlength(type->u.c.field);i++)
1216
0
        {
1217
0
            field = (NC_FIELD_INFO_T *)nclistget(type->u.c.field, i);
1218
0
            assert(field);
1219
0
            if ((retval = nc4_get_hdf_typeid(grp->nc4_info, field->nc_typeid,
1220
0
                                             &hdf_base_typeid, type->endianness)))
1221
0
                return retval;
1222
1223
            /* If this is an array, create a special array type. */
1224
0
            if (field->ndims)
1225
0
            {
1226
0
                int d;
1227
0
                hsize_t dims[NC_MAX_VAR_DIMS];
1228
1229
0
                for (d = 0; d < field->ndims; d++)
1230
0
                    dims[d] = field->dim_size[d];
1231
0
                if ((hdf_typeid = H5Tarray_create(hdf_base_typeid, field->ndims,
1232
0
                                                  dims, NULL)) < 0)
1233
0
                {
1234
0
                    if (H5Tclose(hdf_base_typeid) < 0)
1235
0
                        return NC_EHDFERR;
1236
0
                    return NC_EHDFERR;
1237
0
                }
1238
0
                if (H5Tclose(hdf_base_typeid) < 0)
1239
0
                    return NC_EHDFERR;
1240
0
            }
1241
0
            else
1242
0
                hdf_typeid = hdf_base_typeid;
1243
0
            LOG((4, "inserting field %s offset %d hdf_typeid 0x%x", field->hdr.name,
1244
0
                 field->offset, hdf_typeid));
1245
0
            if (H5Tinsert(hdf5_type->hdf_typeid, field->hdr.name, field->offset,
1246
0
                          hdf_typeid) < 0)
1247
0
                return NC_EHDFERR;
1248
0
            if (H5Tclose(hdf_typeid) < 0)
1249
0
                return NC_EHDFERR;
1250
0
        }
1251
0
    }
1252
0
    else if (type->nc_type_class == NC_VLEN)
1253
0
    {
1254
        /* Find the HDF typeid of the base type of this vlen. */
1255
0
        if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.v.base_nc_typeid,
1256
0
                                         &base_hdf_typeid, type->endianness)))
1257
0
            return retval;
1258
1259
        /* Create a vlen type. */
1260
0
        if ((hdf5_type->hdf_typeid = H5Tvlen_create(base_hdf_typeid)) < 0)
1261
0
            return NC_EHDFERR;
1262
0
    }
1263
0
    else if (type->nc_type_class == NC_OPAQUE)
1264
0
    {
1265
        /* Create the opaque type. */
1266
0
        if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_OPAQUE, type->size)) < 0)
1267
0
            return NC_EHDFERR;
1268
0
    }
1269
0
    else if (type->nc_type_class == NC_ENUM)
1270
0
    {
1271
0
        NC_ENUM_MEMBER_INFO_T *enum_m;
1272
0
        int i;
1273
1274
0
        if (nclistlength(type->u.e.enum_member) == 0)
1275
0
            return NC_EINVAL;
1276
1277
        /* Find the HDF typeid of the base type of this enum. */
1278
0
        if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.e.base_nc_typeid,
1279
0
                                         &base_hdf_typeid, type->endianness)))
1280
0
            return retval;
1281
1282
        /* Create an enum type. */
1283
0
        if ((hdf5_type->hdf_typeid =  H5Tenum_create(base_hdf_typeid)) < 0)
1284
0
            return NC_EHDFERR;
1285
1286
        /* Add all the members to the HDF5 type. */
1287
0
        for(i=0;i<nclistlength(type->u.e.enum_member);i++) {
1288
0
            enum_m = (NC_ENUM_MEMBER_INFO_T*)nclistget(type->u.e.enum_member,i);
1289
0
            if (H5Tenum_insert(hdf5_type->hdf_typeid, enum_m->name, enum_m->value) < 0)
1290
0
                return NC_EHDFERR;
1291
0
        }
1292
0
    }
1293
0
    else
1294
0
    {
1295
0
        LOG((0, "Unknown class: %d", type->nc_type_class));
1296
0
        return NC_EBADTYPE;
1297
0
    }
1298
1299
    /* Commit the type. */
1300
0
    if (H5Tcommit(hdf5_grp->hdf_grpid, type->hdr.name, hdf5_type->hdf_typeid) < 0)
1301
0
        return NC_EHDFERR;
1302
0
    type->committed = NC_TRUE;
1303
0
    LOG((4, "just committed type %s, HDF typeid: 0x%x", type->hdr.name,
1304
0
         hdf5_type->hdf_typeid));
1305
1306
    /* Later we will always use the native typeid. In this case, it is
1307
     * a copy of the same type pointed to by hdf_typeid, but it's
1308
     * easier to maintain a copy. */
1309
0
    if ((hdf5_type->native_hdf_typeid = H5Tget_native_type(hdf5_type->hdf_typeid,
1310
0
                                                           H5T_DIR_DEFAULT)) < 0)
1311
0
        return NC_EHDFERR;
1312
1313
0
    return NC_NOERR;
1314
0
}
1315
1316
/**
1317
 * @internal Write an attribute, with value 1, to indicate that strict
1318
 * NC3 rules apply to this file.
1319
 *
1320
 * @param hdf_grpid HDF5 group ID.
1321
 *
1322
 * @returns NC_NOERR No error.
1323
 * @returns NC_EHDFERR HDF5 returned an error.
1324
 * @author Ed Hartnett
1325
 */
1326
static int
1327
write_nc3_strict_att(hid_t hdf_grpid)
1328
2.52k
{
1329
2.52k
    hid_t attid = 0, spaceid = 0;
1330
2.52k
    int one = 1;
1331
2.52k
    int retval = NC_NOERR;
1332
2.52k
    htri_t attr_exists;
1333
1334
    /* If the attribute already exists, call that a success and return
1335
     * NC_NOERR. */
1336
2.52k
    if ((attr_exists = H5Aexists(hdf_grpid, NC3_STRICT_ATT_NAME)) < 0)
1337
0
        return NC_EHDFERR;
1338
2.52k
    if (attr_exists)
1339
414
        return NC_NOERR;
1340
1341
    /* Create the attribute to mark this as a file that needs to obey
1342
     * strict netcdf-3 rules. */
1343
2.11k
    if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
1344
0
        BAIL(NC_EFILEMETA);
1345
2.11k
    if ((attid = H5Acreate(hdf_grpid, NC3_STRICT_ATT_NAME,
1346
2.11k
                           H5T_NATIVE_INT, spaceid, H5P_DEFAULT)) < 0)
1347
0
        BAIL(NC_EFILEMETA);
1348
2.11k
    if (H5Awrite(attid, H5T_NATIVE_INT, &one) < 0)
1349
0
        BAIL(NC_EFILEMETA);
1350
1351
2.11k
exit:
1352
2.11k
    if (spaceid > 0 && (H5Sclose(spaceid) < 0))
1353
0
        BAIL2(NC_EFILEMETA);
1354
2.11k
    if (attid > 0 && (H5Aclose(attid) < 0))
1355
0
        BAIL2(NC_EFILEMETA);
1356
2.11k
    return retval;
1357
2.11k
}
1358
1359
/**
1360
 * @internal Create a HDF5 group that is not the root group. HDF5
1361
 * properties are set in the group to ensure that objects and
1362
 * attributes are kept in creation order, instead of alphebetical
1363
 * order (the HDF5 default).
1364
 *
1365
 * @param grp Pointer to group info struct.
1366
 *
1367
 * @return NC_NOERR No error.
1368
 * @return NC_EHDFERR HDF5 error.
1369
 * @author Ed Hartnett
1370
 */
1371
static int
1372
create_group(NC_GRP_INFO_T *grp)
1373
0
{
1374
0
    NC_HDF5_GRP_INFO_T *hdf5_grp, *parent_hdf5_grp;
1375
0
    hid_t gcpl_id = -1;
1376
0
    int retval = NC_NOERR;;
1377
1378
0
    assert(grp && grp->format_grp_info && grp->parent &&
1379
0
           grp->parent->format_grp_info);
1380
1381
    /* Get HDF5 specific group info for group and parent. */
1382
0
    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
1383
0
    parent_hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->parent->format_grp_info;
1384
0
    assert(parent_hdf5_grp->hdf_grpid);
1385
1386
    /* Create group, with link_creation_order set in the group
1387
     * creation property list. */
1388
0
    if ((gcpl_id = H5Pcreate(H5P_GROUP_CREATE)) < 0)
1389
0
        BAIL(NC_EHDFERR);
1390
1391
    /* Set track_times to be FALSE. */
1392
0
    if (H5Pset_obj_track_times(gcpl_id, 0) < 0)
1393
0
        BAIL(NC_EHDFERR);
1394
1395
    /* Tell HDF5 to keep track of objects in creation order. */
1396
0
    if (H5Pset_link_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0)
1397
0
        BAIL(NC_EHDFERR);
1398
1399
    /* Tell HDF5 to keep track of attributes in creation order. */
1400
0
    if (H5Pset_attr_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0)
1401
0
        BAIL(NC_EHDFERR);
1402
1403
    /* Create the group. */
1404
0
    if ((hdf5_grp->hdf_grpid = H5Gcreate2(parent_hdf5_grp->hdf_grpid, grp->hdr.name,
1405
0
                                          H5P_DEFAULT, gcpl_id, H5P_DEFAULT)) < 0)
1406
0
        BAIL(NC_EHDFERR);
1407
1408
0
exit:
1409
0
    if (gcpl_id > -1 && H5Pclose(gcpl_id) < 0)
1410
0
        BAIL2(NC_EHDFERR);
1411
0
    if (retval)
1412
0
        if (hdf5_grp->hdf_grpid > 0 && H5Gclose(hdf5_grp->hdf_grpid) < 0)
1413
0
            BAIL2(NC_EHDFERR);
1414
0
    return retval;
1415
0
}
1416
1417
/**
1418
 * @internal After all the datasets of the file have been read, it's
1419
 * time to sort the wheat from the chaff. Which of the datasets are
1420
 * netCDF dimensions, and which are coordinate variables, and which
1421
 * are non-coordinate variables.
1422
 *
1423
 * @param grp Pointer to group info struct.
1424
 *
1425
 * @return ::NC_NOERR No error.
1426
 * @author Ed Hartnett
1427
 */
1428
static int
1429
attach_dimscales(NC_GRP_INFO_T *grp)
1430
7.89k
{
1431
7.89k
    NC_VAR_INFO_T *var;
1432
7.89k
    NC_HDF5_VAR_INFO_T *hdf5_var;
1433
7.89k
    int d, v;
1434
1435
    /* Attach dimension scales. */
1436
91.5k
    for (v = 0; v < ncindexsize(grp->vars); v++)
1437
83.6k
    {
1438
        /* Get pointer to var and HDF5-specific var info. */
1439
83.6k
        var = (NC_VAR_INFO_T *)ncindexith(grp->vars, v);
1440
83.6k
        assert(var && var->format_var_info);
1441
83.6k
        hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
1442
1443
        /* Scales themselves do not attach. But I really wish they
1444
         * would. */
1445
83.6k
        if (var->dimscale)
1446
2.36k
            continue;
1447
1448
        /* Find the scale for each dimension, if any, and attach it. */
1449
130k
        for (d = 0; d < var->ndims; d++)
1450
49.3k
        {
1451
            /* Is there a dimscale for this dimension? */
1452
49.3k
            if (var->dimscale_attached)
1453
49.3k
            {
1454
49.3k
                if (!var->dimscale_attached[d])
1455
41.3k
                {
1456
41.3k
                    hid_t dsid;  /* Dataset ID for dimension */
1457
41.3k
                    assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] &&
1458
41.3k
                           var->dim[d]->format_dim_info);
1459
1460
41.3k
                    LOG((2, "%s: attaching scale for dimid %d to var %s",
1461
41.3k
                         __func__, var->dimids[d], var->hdr.name));
1462
1463
                    /* Find dataset ID for dimension */
1464
41.3k
                    if (var->dim[d]->coord_var)
1465
13.5k
                        dsid = ((NC_HDF5_VAR_INFO_T *)(var->dim[d]->coord_var->format_var_info))->hdf_datasetid;
1466
27.8k
                    else
1467
27.8k
                        dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid;
1468
41.3k
                    assert(dsid > 0);
1469
1470
                    /* Attach the scale. */
1471
41.3k
                    if (H5DSattach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0)
1472
0
                        return NC_EHDFERR;
1473
41.3k
                    var->dimscale_attached[d] = NC_TRUE;
1474
41.3k
                }
1475
49.3k
            }
1476
49.3k
        }
1477
81.3k
    }
1478
1479
7.89k
    return NC_NOERR;
1480
7.89k
}
1481
1482
/**
1483
 * @internal Does a variable exist?
1484
 *
1485
 * @param grpid HDF5 group ID.
1486
 * @param name Name of variable.
1487
 * @param exists Pointer that gets 1 of the variable exists, 0 otherwise.
1488
 *
1489
 * @return ::NC_NOERR No error.
1490
 * @author Ed Hartnett
1491
 */
1492
static int
1493
var_exists(hid_t grpid, char *name, nc_bool_t *exists)
1494
0
{
1495
0
    htri_t link_exists;
1496
1497
    /* Reset the boolean */
1498
0
    *exists = NC_FALSE;
1499
1500
    /* Check if the object name exists in the group */
1501
0
    if ((link_exists = H5Lexists(grpid, name, H5P_DEFAULT)) < 0)
1502
0
        return NC_EHDFERR;
1503
0
    if (link_exists)
1504
0
    {
1505
0
        H5G_stat_t statbuf;
1506
1507
        /* Get info about the object */
1508
0
        if (H5Gget_objinfo(grpid, name, 1, &statbuf) < 0)
1509
0
            return NC_EHDFERR;
1510
1511
0
        if (H5G_DATASET == statbuf.type)
1512
0
            *exists = NC_TRUE;
1513
0
    }
1514
1515
0
    return NC_NOERR;
1516
0
}
1517
1518
/**
1519
 * @internal Convert a coordinate variable HDF5 dataset into one that
1520
 * is not a coordinate variable. This happens during renaming of vars
1521
 * and dims. This function removes the HDF5 NAME and CLASS attributes
1522
 * associated with dimension scales, and also the NC_DIMID_ATT_NAME
1523
 * attribute which may be present, and, if it does, holds the dimid of
1524
 * the coordinate variable.
1525
 *
1526
 * @param hdf_datasetid The HDF5 dataset ID of the coordinate variable
1527
 * dataset.
1528
 *
1529
 * @return ::NC_NOERR No error.
1530
 * @return ::NC_EHDFERR HDF5 error.
1531
 * @author Ed Hartnett
1532
 */
1533
static int
1534
remove_coord_atts(hid_t hdf_datasetid)
1535
0
{
1536
0
    htri_t attr_exists;
1537
1538
    /* If the variable dataset has an optional NC_DIMID_ATT_NAME
1539
     * attribute, delete it. */
1540
0
    if ((attr_exists = H5Aexists(hdf_datasetid, NC_DIMID_ATT_NAME)) < 0)
1541
0
        return NC_EHDFERR;
1542
0
    if (attr_exists)
1543
0
    {
1544
0
        if (H5Adelete(hdf_datasetid, NC_DIMID_ATT_NAME) < 0)
1545
0
            return NC_EHDFERR;
1546
0
    }
1547
1548
    /* Remove the dimension scale 'CLASS' & 'NAME' attributes. */
1549
0
    if ((attr_exists = H5Aexists(hdf_datasetid,
1550
0
                                 HDF5_DIMSCALE_CLASS_ATT_NAME)) < 0)
1551
0
        return NC_EHDFERR;
1552
0
    if (attr_exists)
1553
0
    {
1554
0
        if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_CLASS_ATT_NAME) < 0)
1555
0
            return NC_EHDFERR;
1556
0
    }
1557
0
    if ((attr_exists = H5Aexists(hdf_datasetid,
1558
0
                                 HDF5_DIMSCALE_NAME_ATT_NAME)) < 0)
1559
0
        return NC_EHDFERR;
1560
0
    if (attr_exists)
1561
0
    {
1562
0
        if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_NAME_ATT_NAME) < 0)
1563
0
            return NC_EHDFERR;
1564
0
    }
1565
0
    return NC_NOERR;
1566
0
}
1567
1568
/**
1569
 * @internal This function writes a variable. The principle difficulty
1570
 * comes from the possibility that this is a coordinate variable, and
1571
 * was already written to the file as a dimension-only dimscale. If
1572
 * this occurs, then it must be deleted and recreated.
1573
 *
1574
 * @param var Pointer to variable info struct.
1575
 * @param grp Pointer to group info struct.
1576
 * @param write_dimid
1577
 *
1578
 * @returns NC_NOERR No error.
1579
 * @returns NC_EHDFERR HDF5 returned an error.
1580
 * @author Ed Hartnett, Quincey Koziol
1581
 */
1582
static int
1583
write_var(NC_VAR_INFO_T *var, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
1584
83.6k
{
1585
83.6k
    NC_HDF5_GRP_INFO_T *hdf5_grp;
1586
83.6k
    NC_HDF5_VAR_INFO_T *hdf5_var;
1587
83.6k
    nc_bool_t replace_existing_var = NC_FALSE;
1588
83.6k
    int retval;
1589
1590
83.6k
    assert(var && var->format_var_info && grp && grp->format_grp_info);
1591
1592
83.6k
    LOG((4, "%s: writing var %s", __func__, var->hdr.name));
1593
1594
    /* Get HDF5-specific group and var info. */
1595
83.6k
    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
1596
83.6k
    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
1597
1598
    /* If the variable has already been created & the fill value changed,
1599
     * indicate that the existing variable should be replaced. */
1600
83.6k
    if (var->created && var->fill_val_changed)
1601
0
    {
1602
0
        replace_existing_var = NC_TRUE;
1603
0
        var->fill_val_changed = NC_FALSE;
1604
        /* If the variable is going to be replaced, we need to flag any
1605
           other attributes associated with the variable as 'dirty', or
1606
           else *only* the fill value attribute will be copied over and
1607
           the rest will be lost.  See
1608
           https://github.com/Unidata/netcdf-c/issues/239 */
1609
0
        flag_atts_dirty(var->att);
1610
0
    }
1611
1612
    /* Is this a coordinate var that has already been created in
1613
     * the HDF5 file as a dimscale dataset? Check for dims with the
1614
     * same name in this group. If there is one, check to see if
1615
     * this object exists in the HDF group. */
1616
83.6k
    if (var->became_coord_var)
1617
0
    {
1618
0
        if ((NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name))
1619
0
        {
1620
0
            nc_bool_t exists;
1621
1622
0
            if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists)))
1623
0
                return retval;
1624
0
            if (exists)
1625
0
            {
1626
                /* Indicate that the variable already exists, and should
1627
                 * be replaced. */
1628
0
                replace_existing_var = NC_TRUE;
1629
0
                flag_atts_dirty(var->att);
1630
0
            }
1631
0
        }
1632
0
    }
1633
1634
    /* Check dims if the variable will be replaced, so that the
1635
     * dimensions will be de-attached and re-attached correctly. */
1636
83.6k
    if (replace_existing_var)
1637
0
    {
1638
0
        NC_DIM_INFO_T *d1;
1639
1640
        /* Is there a dim with this var's name? */
1641
0
        if ((d1 = (NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name)))
1642
0
        {
1643
0
            nc_bool_t exists;
1644
0
            assert(d1->format_dim_info && d1->hdr.name);
1645
1646
0
            if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists)))
1647
0
                return retval;
1648
0
            if (exists)
1649
0
            {
1650
0
                hid_t dsid;
1651
1652
                /* Find dataset ID for dimension */
1653
0
                if (d1->coord_var)
1654
0
                    dsid = ((NC_HDF5_VAR_INFO_T *)d1->coord_var->format_var_info)->hdf_datasetid;
1655
0
                else
1656
0
                    dsid = ((NC_HDF5_DIM_INFO_T *)d1->format_dim_info)->hdf_dimscaleid;
1657
0
                assert(dsid > 0);
1658
1659
                /* If we're replacing an existing dimscale dataset, go to
1660
                 * every var in the file and detach this dimension scale,
1661
                 * because we have to delete it. */
1662
0
                if ((retval = rec_detach_scales(grp->nc4_info->root_grp,
1663
0
                                                var->dimids[0], dsid)))
1664
0
                    return retval;
1665
0
            }
1666
0
        }
1667
0
    }
1668
1669
    /* If this is not a dimension scale, remove any attached scales,
1670
     * and delete dimscale attributes from the var. */
1671
83.6k
    if (var->was_coord_var && var->dimscale_attached)
1672
0
    {
1673
0
        int d;
1674
1675
        /* If the variable already exists in the file, Remove any dimension scale
1676
         * attributes from it, if they exist. */
1677
0
        if (var->created)
1678
0
            if ((retval = remove_coord_atts(hdf5_var->hdf_datasetid)))
1679
0
                return retval;
1680
1681
        /* If this is a regular var, detach all its dim scales. */
1682
0
        for (d = 0; d < var->ndims; d++)
1683
0
        {
1684
0
            if (var->dimscale_attached[d])
1685
0
            {
1686
0
                hid_t dsid;  /* Dataset ID for dimension */
1687
0
                assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] &&
1688
0
                       var->dim[d]->format_dim_info);
1689
1690
                /* Find dataset ID for dimension */
1691
0
                if (var->dim[d]->coord_var)
1692
0
                    dsid = ((NC_HDF5_VAR_INFO_T *)var->dim[d]->coord_var->format_var_info)->hdf_datasetid;
1693
0
                else
1694
0
                    dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid;
1695
0
                assert(dsid > 0);
1696
1697
                /* Detach this dim scale. */
1698
0
                if (H5DSdetach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0)
1699
0
                    return NC_EHDFERR;
1700
0
                var->dimscale_attached[d] = NC_FALSE;
1701
0
            }
1702
0
        }
1703
0
    }
1704
1705
    /* Delete the HDF5 dataset that is to be replaced. */
1706
83.6k
    if (replace_existing_var)
1707
0
    {
1708
        /* Free the HDF5 dataset id. */
1709
0
        if (hdf5_var->hdf_datasetid && H5Dclose(hdf5_var->hdf_datasetid) < 0)
1710
0
            return NC_EHDFERR;
1711
0
        hdf5_var->hdf_datasetid = 0;
1712
1713
        /* Now delete the variable. */
1714
0
        if (H5Gunlink(hdf5_grp->hdf_grpid, var->hdr.name) < 0)
1715
0
            return NC_EDIMMETA;
1716
0
    }
1717
1718
    /* Create the dataset. */
1719
83.6k
    if (var->is_new_var || replace_existing_var)
1720
70.3k
    {
1721
70.3k
        if ((retval = var_create_dataset(grp, var, write_dimid)))
1722
0
            return retval;
1723
70.3k
    }
1724
13.3k
    else
1725
13.3k
    {
1726
13.3k
        if (write_dimid && var->ndims)
1727
2.45k
            if ((retval = write_netcdf4_dimid(hdf5_var->hdf_datasetid,
1728
2.45k
                                              var->dimids[0])))
1729
0
                return retval;
1730
13.3k
    }
1731
1732
83.6k
    if (replace_existing_var)
1733
0
    {
1734
        /* If this is a dimension scale, reattach the scale everywhere it
1735
         * is used. (Recall that netCDF dimscales are always 1-D). */
1736
0
        if(var->dimscale)
1737
0
        {
1738
0
            if ((retval = rec_reattach_scales(grp->nc4_info->root_grp,
1739
0
                                              var->dimids[0], hdf5_var->hdf_datasetid)))
1740
0
                return retval;
1741
0
        }
1742
        /* If it's not a dimension scale, clear the dimscale attached flags,
1743
         * so the dimensions are re-attached. */
1744
0
        else
1745
0
        {
1746
0
            if (var->dimscale_attached)
1747
0
                memset(var->dimscale_attached, 0, sizeof(nc_bool_t) * var->ndims);
1748
0
        }
1749
0
    }
1750
1751
    /* Clear coord. var state transition flags */
1752
83.6k
    var->was_coord_var = NC_FALSE;
1753
83.6k
    var->became_coord_var = NC_FALSE;
1754
1755
    /* Now check the attributes for this var. */
1756
83.6k
    if (var->attr_dirty)
1757
0
    {
1758
        /* Write attributes for this var. */
1759
0
        if ((retval = write_attlist(var->att, var->hdr.id, grp)))
1760
0
            return retval;
1761
0
        var->attr_dirty = NC_FALSE;
1762
0
    }
1763
1764
83.6k
    return NC_NOERR;
1765
83.6k
}
1766
1767
/**
1768
 * @internal Write a HDF5 dataset which is a dimension without a
1769
 * coordinate variable. This is a special 1-D dataset.
1770
 *
1771
 * @param dim Pointer to dim info struct.
1772
 * @param grp Pointer to group info struct.
1773
 * @param write_dimid
1774
 *
1775
 * @returns ::NC_NOERR No error.
1776
 * @returns ::NC_EPERM Read-only file.
1777
 * @returns ::NC_EHDFERR HDF5 returned error.
1778
 * @author Ed Hartnett
1779
 */
1780
int
1781
nc4_create_dim_wo_var(NC_DIM_INFO_T *dim)
1782
30.7k
{
1783
30.7k
    NC_HDF5_DIM_INFO_T *hdf5_dim;
1784
30.7k
    NC_HDF5_GRP_INFO_T *hdf5_grp;
1785
30.7k
    hid_t spaceid = -1, create_propid = -1;
1786
30.7k
    hsize_t dims[1], max_dims[1], chunk_dims[1] = {1};
1787
30.7k
    char dimscale_wo_var[NC_MAX_NAME];
1788
30.7k
    int retval = NC_NOERR;
1789
1790
30.7k
    LOG((4, "%s: creating dim %s", __func__, dim->hdr.name));
1791
1792
    /* Sanity check */
1793
30.7k
    assert(!dim->coord_var);
1794
1795
    /* Get HDF5-specific dim and group info. */
1796
30.7k
    hdf5_grp = (NC_HDF5_GRP_INFO_T *)dim->container->format_grp_info;
1797
30.7k
    hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
1798
1799
    /* Create a property list. */
1800
30.7k
    if ((create_propid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
1801
0
        BAIL(NC_EHDFERR);
1802
1803
    /* Turn off recording of times associated with this object. */
1804
30.7k
    if (H5Pset_obj_track_times(create_propid, 0) < 0)
1805
0
        BAIL(NC_EHDFERR);
1806
1807
    /* Set size of dataset to size of dimension. */
1808
30.7k
    dims[0] = dim->len;
1809
30.7k
    max_dims[0] = dim->len;
1810
1811
    /* If this dimension scale is unlimited (i.e. it's an unlimited
1812
     * dimension), then set up chunking, with a chunksize of 1. */
1813
30.7k
    if (dim->unlimited)
1814
18.2k
    {
1815
18.2k
        max_dims[0] = H5S_UNLIMITED;
1816
18.2k
        if (H5Pset_chunk(create_propid, 1, chunk_dims) < 0)
1817
0
            BAIL(NC_EHDFERR);
1818
18.2k
    }
1819
1820
    /* Set up space. */
1821
30.7k
    if ((spaceid = H5Screate_simple(1, dims, max_dims)) < 0)
1822
0
        BAIL(NC_EHDFERR);
1823
1824
    /* Turn on creation-order tracking. */
1825
30.7k
    if (H5Pset_attr_creation_order(create_propid, H5P_CRT_ORDER_TRACKED|
1826
30.7k
                                   H5P_CRT_ORDER_INDEXED) < 0)
1827
0
        BAIL(NC_EHDFERR);
1828
1829
    /* Create the dataset that will be the dimension scale. */
1830
30.7k
    LOG((4, "%s: about to H5Dcreate1 a dimscale dataset %s", __func__,
1831
30.7k
         dim->hdr.name));
1832
30.7k
    if ((hdf5_dim->hdf_dimscaleid = H5Dcreate2(hdf5_grp->hdf_grpid, dim->hdr.name,
1833
30.7k
                                               H5T_IEEE_F32BE, spaceid,
1834
30.7k
                                               H5P_DEFAULT, create_propid,
1835
30.7k
                                               H5P_DEFAULT)) < 0)
1836
0
        BAIL(NC_EHDFERR);
1837
1838
    /* Indicate that this is a scale. Also indicate that not
1839
     * be shown to the user as a variable. It is hidden. It is
1840
     * a DIM WITHOUT A VARIABLE! */
1841
30.7k
    sprintf(dimscale_wo_var, "%s%10d", DIM_WITHOUT_VARIABLE, (int)dim->len);
1842
30.7k
    if (H5DSset_scale(hdf5_dim->hdf_dimscaleid, dimscale_wo_var) < 0)
1843
0
        BAIL(NC_EHDFERR);
1844
1845
    /* Since this dimension was created out of order, we cannot rely on
1846
     * it getting the correct dimid on file open. We must assign it
1847
     * explicitly. */
1848
30.7k
    if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id)))
1849
0
        BAIL(retval);
1850
1851
30.7k
exit:
1852
30.7k
    if (spaceid > 0 && H5Sclose(spaceid) < 0)
1853
0
        BAIL2(NC_EHDFERR);
1854
30.7k
    if (create_propid > 0 && H5Pclose(create_propid) < 0)
1855
0
        BAIL2(NC_EHDFERR);
1856
30.7k
    return retval;
1857
30.7k
}
1858
1859
/**
1860
 * @internal Write a dimension.
1861
 *
1862
 * @param dim Pointer to dim info struct.
1863
 * @param grp Pointer to group info struct.
1864
 * @param write_dimid
1865
 *
1866
 * @returns ::NC_NOERR No error.
1867
 * @returns ::NC_EPERM Read-only file.
1868
 * @returns ::NC_EHDFERR HDF5 returned error.
1869
 * @author Ed Hartnett
1870
 */
1871
static int
1872
write_dim(NC_DIM_INFO_T *dim, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
1873
37.2k
{
1874
37.2k
    NC_HDF5_DIM_INFO_T *hdf5_dim;
1875
37.2k
    int retval = NC_NOERR;
1876
1877
37.2k
    assert(dim && dim->format_dim_info && grp && grp->format_grp_info);
1878
1879
    /* Get HDF5-specific dim and group info. */
1880
37.2k
    hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
1881
1882
    /* If there's no dimscale dataset for this dim, create one,
1883
     * and mark that it should be hidden from netCDF as a
1884
     * variable. (That is, it should appear as a dimension
1885
     * without an associated variable.) */
1886
37.2k
    if (!hdf5_dim->hdf_dimscaleid)
1887
30.7k
        if ((retval = nc4_create_dim_wo_var(dim)))
1888
0
            BAIL(retval);
1889
1890
    /* Did we extend an unlimited dimension? */
1891
37.2k
    if (dim->extended)
1892
0
    {
1893
0
        NC_VAR_INFO_T *v1 = NULL;
1894
1895
0
        assert(dim->unlimited);
1896
1897
        /* If this is a dimension with an associated coordinate var,
1898
         * then update the length of that coord var. */
1899
0
        v1 = dim->coord_var;
1900
0
        if (v1)
1901
0
        {
1902
0
            NC_HDF5_VAR_INFO_T *hdf5_v1;
1903
0
            hsize_t *new_size;
1904
0
            int d1;
1905
1906
0
            hdf5_v1 = (NC_HDF5_VAR_INFO_T *)v1->format_var_info;
1907
1908
            /* Extend the dimension scale dataset to reflect the new
1909
             * length of the dimension. */
1910
0
            if (!(new_size = malloc(v1->ndims * sizeof(hsize_t))))
1911
0
                BAIL(NC_ENOMEM);
1912
0
            for (d1 = 0; d1 < v1->ndims; d1++)
1913
0
            {
1914
0
                assert(v1->dim[d1] && v1->dim[d1]->hdr.id == v1->dimids[d1]);
1915
0
                new_size[d1] = v1->dim[d1]->len;
1916
0
            }
1917
0
            if (H5Dset_extent(hdf5_v1->hdf_datasetid, new_size) < 0)
1918
0
                BAIL(NC_EHDFERR);
1919
0
            free(new_size);
1920
0
        }
1921
0
    }
1922
1923
    /* If desired, write the secret dimid. This will be used instead of
1924
     * the dimid that the dimension would otherwise receive based on
1925
     * creation order. This can be necessary when dims and their
1926
     * coordinate variables were created in different order. */
1927
37.2k
    if (write_dimid && hdf5_dim->hdf_dimscaleid)
1928
11.8k
        if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id)))
1929
0
            BAIL(retval);
1930
1931
37.2k
exit:
1932
1933
37.2k
    return retval;
1934
37.2k
}
1935
1936
/**
1937
 * @internal Recursively write all the metadata in a group. Groups and
1938
 * types have all already been written. Propagate bad cooordinate
1939
 * order to subgroups, if detected.
1940
 *
1941
 * @param grp Pointer to group info struct.
1942
 * @param bad_coord_order 1 if there is a bad coordinate order.
1943
 *
1944
 * @returns NC_NOERR No error.
1945
 * @returns NC_EHDFERR HDF5 returned an error.
1946
 * @author Ed Hartnett
1947
 */
1948
int
1949
nc4_rec_write_metadata(NC_GRP_INFO_T *grp, nc_bool_t bad_coord_order)
1950
7.89k
{
1951
7.89k
    NC_DIM_INFO_T *dim = NULL;
1952
7.89k
    NC_VAR_INFO_T *var = NULL;
1953
7.89k
    NC_GRP_INFO_T *child_grp = NULL;
1954
7.89k
    int coord_varid = -1;
1955
7.89k
    int var_index = 0;
1956
7.89k
    int dim_index = 0;
1957
7.89k
    int retval;
1958
7.89k
    int i;
1959
1960
7.89k
    assert(grp && grp->hdr.name &&
1961
7.89k
           ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid);
1962
7.89k
    LOG((3, "%s: grp->hdr.name %s, bad_coord_order %d", __func__, grp->hdr.name,
1963
7.89k
         bad_coord_order));
1964
1965
    /* Write global attributes for this group. */
1966
7.89k
    if ((retval = write_attlist(grp->att, NC_GLOBAL, grp)))
1967
0
        return retval;
1968
1969
    /* Set the pointers to the beginning of the list of dims & vars in this
1970
     * group. */
1971
7.89k
    dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, dim_index);
1972
7.89k
    var = (NC_VAR_INFO_T *)ncindexith(grp->vars, var_index);
1973
1974
    /* Because of HDF5 ordering the dims and vars have to be stored in
1975
     * this way to ensure that the dims and coordinate vars come out in
1976
     * the correct order. */
1977
17.6k
    while (dim || var)
1978
9.76k
    {
1979
9.76k
        nc_bool_t found_coord, wrote_coord;
1980
1981
        /* Write non-coord dims in order, stopping at the first one that
1982
         * has an associated coord var. */
1983
49.3k
        for (found_coord = NC_FALSE; dim && !found_coord; )
1984
39.5k
        {
1985
39.5k
            if (!dim->coord_var)
1986
37.2k
            {
1987
37.2k
                if ((retval = write_dim(dim, grp, bad_coord_order)))
1988
0
                    return retval;
1989
37.2k
            }
1990
2.36k
            else
1991
2.36k
            {
1992
2.36k
                coord_varid = dim->coord_var->hdr.id;
1993
2.36k
                found_coord = NC_TRUE;
1994
2.36k
            }
1995
39.5k
            dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, ++dim_index);
1996
39.5k
        }
1997
1998
        /* Write each var. When we get to the coord var we are waiting
1999
         * for (if any), then we break after writing it. */
2000
93.4k
        for (wrote_coord = NC_FALSE; var && !wrote_coord; )
2001
83.6k
        {
2002
83.6k
            if ((retval = write_var(var, grp, bad_coord_order)))
2003
0
                return retval;
2004
83.6k
            if (found_coord && var->hdr.id == coord_varid)
2005
2.15k
                wrote_coord = NC_TRUE;
2006
83.6k
            var = (NC_VAR_INFO_T *)ncindexith(grp->vars, ++var_index);
2007
83.6k
        }
2008
9.76k
    } /* end while */
2009
2010
    /* Attach dimscales to vars in this group. */
2011
7.89k
    if ((retval = attach_dimscales(grp)))
2012
0
        return retval;
2013
2014
    /* If there are any child groups, write their metadata. */
2015
7.89k
    for (i = 0; i < ncindexsize(grp->children); i++)
2016
0
    {
2017
0
        child_grp = (NC_GRP_INFO_T *)ncindexith(grp->children, i);
2018
0
        assert(child_grp);
2019
0
        if ((retval = nc4_rec_write_metadata(child_grp, bad_coord_order)))
2020
0
            return retval;
2021
0
    }
2022
7.89k
    return NC_NOERR;
2023
7.89k
}
2024
2025
/**
2026
 * @internal Recursively write all groups and types.
2027
 *
2028
 * @param grp Pointer to group info struct.
2029
 *
2030
 * @returns NC_NOERR No error.
2031
 * @returns NC_EHDFERR HDF5 returned an error.
2032
 * @author Ed Hartnett
2033
 */
2034
int
2035
nc4_rec_write_groups_types(NC_GRP_INFO_T *grp)
2036
7.89k
{
2037
7.89k
    NC_GRP_INFO_T *child_grp;
2038
7.89k
    NC_HDF5_GRP_INFO_T *hdf5_grp;
2039
7.89k
    NC_TYPE_INFO_T *type;
2040
7.89k
    int retval;
2041
7.89k
    int i;
2042
2043
7.89k
    assert(grp && grp->hdr.name && grp->format_grp_info);
2044
7.89k
    LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
2045
2046
    /* Get HDF5-specific group info. */
2047
7.89k
    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
2048
2049
    /* Create the group in the HDF5 file if it doesn't exist. */
2050
7.89k
    if (!hdf5_grp->hdf_grpid)
2051
0
        if ((retval = create_group(grp)))
2052
0
            return retval;
2053
2054
    /* If this is the root group of a file with strict NC3 rules, write
2055
     * an attribute. But don't leave the attribute open. */
2056
7.89k
    if (!grp->parent && (grp->nc4_info->cmode & NC_CLASSIC_MODEL))
2057
2.52k
        if ((retval = write_nc3_strict_att(hdf5_grp->hdf_grpid)))
2058
0
            return retval;
2059
2060
    /* If there are any user-defined types, write them now. */
2061
7.89k
    for(i=0;i<ncindexsize(grp->type);i++) {
2062
0
        type = (NC_TYPE_INFO_T *)ncindexith(grp->type, i);
2063
0
        assert(type);
2064
0
        if ((retval = commit_type(grp, type)))
2065
0
            return retval;
2066
0
    }
2067
2068
    /* If there are any child groups, write their groups and types. */
2069
7.89k
    for(i=0;i<ncindexsize(grp->children);i++) {
2070
0
        if((child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children,i)) == NULL) continue;
2071
0
        if ((retval = nc4_rec_write_groups_types(child_grp)))
2072
0
            return retval;
2073
0
    }
2074
7.89k
    return NC_NOERR;
2075
7.89k
}
2076
2077
/**
2078
 * @internal In our first pass through the data, we may have
2079
 * encountered variables before encountering their dimscales, so go
2080
 * through the vars in this file and make sure we've got a dimid for
2081
 * each.
2082
 *
2083
 * @param grp Pointer to group info struct.
2084
 *
2085
 * @returns NC_NOERR No error.
2086
 * @returns NC_EHDFERR HDF5 returned an error.
2087
 * @returns NC_ENOMEM Out of memory.
2088
 * @author Ed Hartnett
2089
 */
2090
int
2091
nc4_rec_match_dimscales(NC_GRP_INFO_T *grp)
2092
0
{
2093
0
    NC_GRP_INFO_T *g;
2094
0
    NC_VAR_INFO_T *var;
2095
0
    NC_DIM_INFO_T *dim;
2096
0
    int retval = NC_NOERR;
2097
0
    int i;
2098
2099
0
    assert(grp && grp->hdr.name);
2100
0
    LOG((4, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
2101
2102
    /* Perform var dimscale match for child groups. */
2103
0
    for (i = 0; i < ncindexsize(grp->children); i++)
2104
0
    {
2105
0
        g = (NC_GRP_INFO_T*)ncindexith(grp->children, i);
2106
0
        assert(g);
2107
0
        if ((retval = nc4_rec_match_dimscales(g)))
2108
0
            return retval;
2109
0
    }
2110
2111
    /* Check all the vars in this group. If they have dimscale info,
2112
     * try and find a dimension for them. */
2113
0
    for (i = 0; i < ncindexsize(grp->vars); i++)
2114
0
    {
2115
0
        NC_HDF5_VAR_INFO_T *hdf5_var;
2116
0
        int ndims;
2117
0
        int d;
2118
2119
        /* Get pointer to var and to the HDF5-specific var info. */
2120
0
        var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i);
2121
0
        assert(var && var->format_var_info);
2122
0
        hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
2123
2124
        /* Check all vars and see if dim[i] != NULL if dimids[i] valid. */
2125
        /* This loop is very odd. Under normal circumstances, var->dimid[d] is zero
2126
           (from the initial calloc) which is a legitimate dimid. The code does not
2127
           distinquish this case from the dimscale case where the id might actually
2128
           be defined.
2129
           The original nc4_find_dim searched up the group tree looking for the given
2130
           dimid in one of the dim lists associated with each ancestor group.
2131
           I changed nc4_fnd_dim to use the dimid directly using h5->alldims.
2132
           However, here that is incorrect because it will find the dimid 0 always
2133
           (if any dimensions were defined). Except that when dimscale dimids have
2134
           been defined, one or more of the values in var->dimids will have a
2135
           legitimate value.
2136
           The solution I choose is to modify nc4_var_list_add to initialize dimids to
2137
           illegal values (-1). This is another example of the problems with dimscales.
2138
        */
2139
0
        ndims = var->ndims;
2140
0
        for (d = 0; d < ndims; d++)
2141
0
        {
2142
0
            if (var->dim[d] == NULL) {
2143
0
                nc4_find_dim(grp, var->dimids[d], &var->dim[d], NULL);
2144
0
            }
2145
            /*       assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d]); */
2146
0
        }
2147
2148
        /* Skip dimension scale variables */
2149
0
        if (!var->dimscale)
2150
0
        {
2151
0
            int d;
2152
0
            int j;
2153
2154
            /* Are there dimscales for this variable? */
2155
0
            if (hdf5_var->dimscale_hdf5_objids)
2156
0
            {
2157
0
                for (d = 0; d < var->ndims; d++)
2158
0
                {
2159
0
                    nc_bool_t finished = NC_FALSE;
2160
0
                    LOG((5, "%s: var %s has dimscale info...", __func__, var->hdr.name));
2161
2162
                    /* Check this and parent groups. */
2163
0
                    for (g = grp; g && !finished; g = g->parent)
2164
0
                    {
2165
                        /* Check all dims in this group. */
2166
0
                        for (j = 0; j < ncindexsize(g->dim); j++)
2167
0
                        {
2168
                            /* Get the HDF5 specific dim info. */
2169
0
                            NC_HDF5_DIM_INFO_T *hdf5_dim;
2170
0
                            dim = (NC_DIM_INFO_T *)ncindexith(g->dim, j);
2171
0
                            assert(dim && dim->format_dim_info);
2172
0
                            hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
2173
2174
                            /* Check for exact match of fileno/objid arrays
2175
                             * to find identical objects in HDF5 file. */
2176
0
                            if (hdf5_var->dimscale_hdf5_objids[d].fileno[0] == hdf5_dim->hdf5_objid.fileno[0] &&
2177
0
                                hdf5_var->dimscale_hdf5_objids[d].objno[0] == hdf5_dim->hdf5_objid.objno[0] &&
2178
0
                                hdf5_var->dimscale_hdf5_objids[d].fileno[1] == hdf5_dim->hdf5_objid.fileno[1] &&
2179
0
                                hdf5_var->dimscale_hdf5_objids[d].objno[1] == hdf5_dim->hdf5_objid.objno[1])
2180
0
                            {
2181
0
                                LOG((4, "%s: for dimension %d, found dim %s", __func__,
2182
0
                                     d, dim->hdr.name));
2183
0
                                var->dimids[d] = dim->hdr.id;
2184
0
                                var->dim[d] = dim;
2185
0
                                finished = NC_TRUE;
2186
0
                                break;
2187
0
                            }
2188
0
                        } /* next dim */
2189
0
                    } /* next grp */
2190
0
                    LOG((5, "%s: dimid for this dimscale is %d", __func__,
2191
0
                         var->type_info->hdr.id));
2192
0
                } /* next var->dim */
2193
0
            }
2194
            /* No dimscales for this var! Invent phony dimensions. */
2195
0
            else
2196
0
            {
2197
0
                hid_t spaceid = 0;
2198
0
                hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL;
2199
0
                int dataset_ndims;
2200
2201
                /* Find the space information for this dimension. */
2202
0
                if ((spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0)
2203
0
                    return NC_EHDFERR;
2204
2205
                /* Get the len of each dim in the space. */
2206
0
                if (var->ndims)
2207
0
                {
2208
0
                    if (!(h5dimlen = malloc(var->ndims * sizeof(hsize_t))))
2209
0
                        return NC_ENOMEM;
2210
0
                    if (!(h5dimlenmax = malloc(var->ndims * sizeof(hsize_t))))
2211
0
                    {
2212
0
                        free(h5dimlen);
2213
0
                        return NC_ENOMEM;
2214
0
                    }
2215
0
                    if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, h5dimlen,
2216
0
                                                                   h5dimlenmax)) < 0) {
2217
0
                        free(h5dimlenmax);
2218
0
                        free(h5dimlen);
2219
0
                        return NC_EHDFERR;
2220
0
                    }
2221
0
                    if (dataset_ndims != var->ndims) {
2222
0
                        free(h5dimlenmax);
2223
0
                        free(h5dimlen);
2224
0
                        return NC_EHDFERR;
2225
0
                    }
2226
0
                }
2227
0
                else
2228
0
                {
2229
                    /* Make sure it's scalar. */
2230
0
                    if (H5Sget_simple_extent_type(spaceid) != H5S_SCALAR)
2231
0
                        return NC_EHDFERR;
2232
0
                }
2233
2234
                /* Release the space object. */
2235
0
                if (H5Sclose(spaceid) < 0) {
2236
0
                    free(h5dimlen);
2237
0
                    free(h5dimlenmax);
2238
0
                    return NC_EHDFERR;
2239
0
                }
2240
2241
                /* Create a phony dimension for each dimension in the
2242
                 * dataset, unless there already is one the correct
2243
                 * size. */
2244
0
                for (d = 0; d < var->ndims; d++)
2245
0
                {
2246
0
                    int k;
2247
0
                    int match;
2248
                    /* Is there already a phony dimension of the correct size? */
2249
0
                    for(match=-1,k=0;k<ncindexsize(grp->dim);k++) {
2250
0
                        if((dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,k)) == NULL) continue;
2251
0
                        if ((dim->len == h5dimlen[d]) &&
2252
0
                            ((h5dimlenmax[d] == H5S_UNLIMITED && dim->unlimited) ||
2253
0
                             (h5dimlenmax[d] != H5S_UNLIMITED && !dim->unlimited)))
2254
0
                        {match = k; break;}
2255
0
                    }
2256
2257
                    /* Didn't find a phony dim? Then create one. */
2258
0
                    if (match < 0)
2259
0
                    {
2260
0
                        char phony_dim_name[NC_MAX_NAME + 1];
2261
0
                        sprintf(phony_dim_name, "phony_dim_%d", grp->nc4_info->next_dimid);
2262
0
                        LOG((3, "%s: creating phony dim for var %s", __func__, var->hdr.name));
2263
0
                        if ((retval = nc4_dim_list_add(grp, phony_dim_name, h5dimlen[d], -1, &dim)))
2264
0
                        {
2265
0
                            free(h5dimlenmax);
2266
0
                            free(h5dimlen);
2267
0
                            return retval;
2268
0
                        }
2269
                        /* Create struct for HDF5-specific dim info. */
2270
0
                        if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T))))
2271
0
                            return NC_ENOMEM;
2272
0
                        if (h5dimlenmax[d] == H5S_UNLIMITED)
2273
0
                            dim->unlimited = NC_TRUE;
2274
0
                    }
2275
2276
                    /* The variable must remember the dimid. */
2277
0
                    var->dimids[d] = dim->hdr.id;
2278
0
                    var->dim[d] = dim;
2279
0
                } /* next dim */
2280
2281
                /* Free the memory we malloced. */
2282
0
                free(h5dimlen);
2283
0
                free(h5dimlenmax);
2284
0
            }
2285
0
        }
2286
0
    }
2287
2288
0
    return retval;
2289
0
}
2290
2291
/**
2292
 * @internal Get the class of a type
2293
 *
2294
 * @param h5 Pointer to the HDF5 file info struct.
2295
 * @param xtype NetCDF type ID.
2296
 * @param type_class Pointer that gets class of type, NC_INT,
2297
 * NC_FLOAT, NC_CHAR, or NC_STRING, NC_ENUM, NC_VLEN, NC_COMPOUND, or
2298
 * NC_OPAQUE.
2299
 *
2300
 * @return ::NC_NOERR No error.
2301
 * @author Ed Hartnett
2302
 */
2303
int
2304
nc4_get_typeclass(const NC_FILE_INFO_T *h5, nc_type xtype, int *type_class)
2305
54.0k
{
2306
54.0k
    int retval = NC_NOERR;
2307
2308
54.0k
    LOG((4, "%s xtype: %d", __func__, xtype));
2309
54.0k
    assert(type_class);
2310
2311
    /* If this is an atomic type, the answer is easy. */
2312
54.0k
    if (xtype <= NC_STRING)
2313
54.0k
    {
2314
54.0k
        switch (xtype)
2315
54.0k
        {
2316
4.69k
        case NC_BYTE:
2317
4.69k
        case NC_UBYTE:
2318
8.16k
        case NC_SHORT:
2319
8.16k
        case NC_USHORT:
2320
34.3k
        case NC_INT:
2321
34.3k
        case NC_UINT:
2322
34.3k
        case NC_INT64:
2323
34.3k
        case NC_UINT64:
2324
            /* NC_INT is class used for all integral types */
2325
34.3k
            *type_class = NC_INT;
2326
34.3k
            break;
2327
2328
0
        case NC_FLOAT:
2329
5.49k
        case NC_DOUBLE:
2330
            /* NC_FLOAT is class used for all floating-point types */
2331
5.49k
            *type_class = NC_FLOAT;
2332
5.49k
            break;
2333
2334
14.2k
        case NC_CHAR:
2335
14.2k
            *type_class = NC_CHAR;
2336
14.2k
            break;
2337
2338
0
        case NC_STRING:
2339
0
            *type_class = NC_STRING;
2340
0
            break;
2341
2342
0
        default:
2343
0
            BAIL(NC_EBADTYPE);
2344
54.0k
        }
2345
54.0k
    }
2346
0
    else
2347
0
    {
2348
0
        NC_TYPE_INFO_T *type;
2349
2350
        /* See if it's a used-defined type */
2351
0
        if ((retval = nc4_find_type(h5, xtype, &type)))
2352
0
            BAIL(retval);
2353
0
        if (!type)
2354
0
            BAIL(NC_EBADTYPE);
2355
2356
0
        *type_class = type->nc_type_class;
2357
0
    }
2358
2359
54.0k
exit:
2360
54.0k
    return retval;
2361
54.0k
}
2362
2363
/**
2364
 * @internal Report information about an open HDF5 object. This is
2365
 * called on any still-open objects when a HDF5 file close is
2366
 * attempted.
2367
 *
2368
 * @param uselog If true, send output to LOG not stderr.
2369
 * @param id HDF5 ID of open object.
2370
 * @param type Type of HDF5 object, file, dataset, etc.
2371
 *
2372
 * @author Dennis Heimbigner
2373
 */
2374
void
2375
reportobject(int uselog, hid_t id, unsigned int type)
2376
0
{
2377
0
    char name[NC_HDF5_MAX_NAME];
2378
0
    ssize_t len;
2379
0
    const char* typename = NULL;
2380
0
    long long printid = (long long)id;
2381
2382
0
    len = H5Iget_name(id, name, NC_HDF5_MAX_NAME);
2383
0
    if(len < 0) return;
2384
0
    name[len] = '\0';
2385
2386
0
    switch (type) {
2387
0
    case H5F_OBJ_FILE: typename = "File"; break;
2388
0
    case H5F_OBJ_DATASET: typename = "Dataset"; break;
2389
0
    case H5F_OBJ_GROUP: typename = "Group"; break;
2390
0
    case H5F_OBJ_DATATYPE: typename = "Datatype"; break;
2391
0
    case H5F_OBJ_ATTR:
2392
0
        typename = "Attribute";
2393
0
        len = H5Aget_name(id, NC_HDF5_MAX_NAME, name);
2394
0
        if(len < 0) len = 0;
2395
0
        name[len] = '\0';
2396
0
        break;
2397
0
    default: typename = "<unknown>"; break;
2398
0
    }
2399
#ifdef LOGGING
2400
    if(uselog) {
2401
        LOG((0,"Type = %s(%lld) name='%s'",typename,printid,name));
2402
    } else
2403
#endif
2404
0
    {
2405
0
        fprintf(stderr,"Type = %s(%lld) name='%s'",typename,printid,name);
2406
0
    }
2407
2408
0
}
2409
2410
/**
2411
 * @internal
2412
 *
2413
 * @param uselog
2414
 * @param fid HDF5 ID.
2415
 * @param ntypes Number of types.
2416
 * @param otypes Pointer that gets number of open types.
2417
 *
2418
 * @author Dennis Heimbigner
2419
 */
2420
static void
2421
reportopenobjectsT(int uselog, hid_t fid, int ntypes, unsigned int* otypes)
2422
0
{
2423
0
    int t,i;
2424
0
    ssize_t ocount;
2425
0
    size_t maxobjs = -1;
2426
0
    hid_t* idlist = NULL;
2427
2428
    /* Always report somehow */
2429
#ifdef LOGGING
2430
    if(uselog)
2431
        LOG((0,"\nReport: open objects on %lld",(long long)fid));
2432
    else
2433
#endif
2434
0
        fprintf(stdout,"\nReport: open objects on %lld\n",(long long)fid);
2435
0
    maxobjs = H5Fget_obj_count(fid,H5F_OBJ_ALL);
2436
0
    if(idlist != NULL) free(idlist);
2437
0
    idlist = (hid_t*)malloc(sizeof(hid_t)*maxobjs);
2438
0
    for(t=0;t<ntypes;t++) {
2439
0
        unsigned int ot = otypes[t];
2440
0
        ocount = H5Fget_obj_ids(fid,ot,maxobjs,idlist);
2441
0
        for(i=0;i<ocount;i++) {
2442
0
            hid_t o = idlist[i];
2443
0
            reportobject(uselog,o,ot);
2444
0
        }
2445
0
    }
2446
0
    if(idlist != NULL) free(idlist);
2447
0
}
2448
2449
/**
2450
 * @internal Report open objects.
2451
 *
2452
 * @param uselog
2453
 * @param fid HDF5 file ID.
2454
 *
2455
 * @author Dennit Heimbigner
2456
 */
2457
void
2458
reportopenobjects(int uselog, hid_t fid)
2459
0
{
2460
0
    unsigned int OTYPES[5] = {H5F_OBJ_FILE, H5F_OBJ_DATASET, H5F_OBJ_GROUP,
2461
0
                              H5F_OBJ_DATATYPE, H5F_OBJ_ATTR};
2462
2463
0
    reportopenobjectsT(uselog, fid ,5, OTYPES);
2464
0
}
2465
2466
/**
2467
 * @internal Report open objects given a pointer to NC_FILE_INFO_T object
2468
 *
2469
 * @param h5 file object
2470
 *
2471
 * @author Dennis Heimbigner
2472
 */
2473
void
2474
showopenobjects5(NC_FILE_INFO_T* h5)
2475
0
{
2476
0
    NC_HDF5_FILE_INFO_T *hdf5_info;
2477
2478
0
    assert(h5 && h5->format_file_info);
2479
0
    hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
2480
2481
0
    fprintf(stderr,"===== begin showopenobjects =====\n");
2482
0
    reportopenobjects(0,hdf5_info->hdfid);
2483
0
    fprintf(stderr,"===== end showopenobjects =====\n");
2484
0
    fflush(stderr);
2485
0
}
2486
2487
/**
2488
 * @internal Report open objects given an ncid
2489
 * Defined to support user or gdb level call.
2490
 *
2491
 * @param ncid file id
2492
 *
2493
 * @author Dennis Heimbigner
2494
 */
2495
void
2496
showopenobjects(int ncid)
2497
0
{
2498
0
    NC_FILE_INFO_T* h5 = NULL;
2499
2500
    /* Find our metadata for this file. */
2501
0
    if (nc4_find_nc_grp_h5(ncid, NULL, NULL, &h5) != NC_NOERR)
2502
0
        fprintf(stderr,"failed\n");
2503
0
    else
2504
0
        showopenobjects5(h5);
2505
0
    fflush(stderr);
2506
0
}
2507
2508
/**
2509
 * @internal Get HDF5 library version.
2510
 *
2511
 * @param major Pointer that gets major version number.
2512
 * @param minor Pointer that gets minor version number.
2513
 * @param release Pointer that gets release version number.
2514
 *
2515
 * @returns NC_NOERR No error.
2516
 * @returns NC_EHDFERR HDF5 returned error.
2517
 * @author Dennis Heimbigner
2518
 */
2519
int
2520
NC4_hdf5get_libversion(unsigned* major,unsigned* minor,unsigned* release)
2521
9
{
2522
9
    if(H5get_libversion(major,minor,release) < 0)
2523
0
        return NC_EHDFERR;
2524
9
    return NC_NOERR;
2525
9
}
2526
2527
/**
2528
 * @internal Get HDF5 superblock version.
2529
 *
2530
 * @param h5 Pointer to HDF5 file info struct.
2531
 * @param idp Pointer that gets superblock version.
2532
 *
2533
 * @returns NC_NOERR No error.
2534
 * @returns NC_EHDFERR HDF5 returned error.
2535
 * @author Dennis Heimbigner
2536
 */
2537
int
2538
NC4_hdf5get_superblock(struct NC_FILE_INFO* h5, int* idp)
2539
13.1k
{
2540
13.1k
    NC_HDF5_FILE_INFO_T *hdf5_info;
2541
13.1k
    int stat = NC_NOERR;
2542
13.1k
    unsigned super;
2543
13.1k
    hid_t plist = -1;
2544
2545
13.1k
    assert(h5 && h5->format_file_info);
2546
13.1k
    hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
2547
2548
13.1k
    if((plist = H5Fget_create_plist(hdf5_info->hdfid)) < 0)
2549
0
    {stat = NC_EHDFERR; goto done;}
2550
13.1k
    if(H5Pget_version(plist, &super, NULL, NULL, NULL) < 0)
2551
0
    {stat = NC_EHDFERR; goto done;}
2552
13.1k
    if(idp) *idp = (int)super;
2553
13.1k
done:
2554
13.1k
    if(plist >= 0) H5Pclose(plist);
2555
13.1k
    return stat;
2556
13.1k
}
2557
2558
static int NC4_strict_att_exists(NC_FILE_INFO_T*);
2559
static int NC4_walk(hid_t, int*);
2560
2561
/**
2562
 * @internal Determine whether file is netCDF-4.
2563
 *
2564
 * We define a file as being from netcdf-4 if any of the following
2565
 * are true:
2566
 * 1. NCPROPS attribute exists in root group
2567
 * 2. NC3_STRICT_ATT_NAME exists in root group
2568
 * 3. any of NC_ATT_REFERENCE_LIST, NC_ATT_CLASS,
2569
 * NC_ATT_DIMENSION_LIST, NC_ATT_NAME,
2570
 * NC_ATT_COORDINATES, NC_DIMID_ATT_NAME
2571
 * exist anywhere in the file; note that this
2572
 * requires walking the file.
2573
2574
 * @note WARNINGS:
2575
 *   1. False negatives are possible for a small subset of netcdf-4
2576
 *   created files.
2577
 *   2. Deliberate falsification in the file can be used to cause
2578
 *   a false positive.
2579
 *
2580
 * @param h5 Pointer to HDF5 file info struct.
2581
 *
2582
 * @returns NC_NOERR No error.
2583
 * @author Dennis Heimbigner.
2584
 */
2585
int
2586
NC4_isnetcdf4(struct NC_FILE_INFO* h5)
2587
0
{
2588
0
    int stat;
2589
0
    int isnc4 = 0;
2590
0
    int exists;
2591
0
    int count;
2592
2593
    /* Look for NC3_STRICT_ATT_NAME */
2594
0
    exists = NC4_strict_att_exists(h5);
2595
0
    if(exists)
2596
0
        goto done;
2597
    /* attribute did not exist */
2598
    /* => last resort: walk the HDF5 file looking for markers */
2599
0
    count = 0;
2600
0
    stat = NC4_walk(((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid,
2601
0
                    &count);
2602
0
    if(stat != NC_NOERR)
2603
0
        isnc4 = 0;
2604
0
    else /* Threshold is at least two matches */
2605
0
        isnc4 = (count >= 2);
2606
2607
0
done:
2608
0
    return isnc4;
2609
0
}
2610
2611
/**
2612
 * @internal See if the NC3 strict attribute exists.
2613
 *
2614
 * @param h5 Pointer to HDF5 file info struct.
2615
 *
2616
 * @returns 1 if error || exists; 0 otherwise
2617
 * @author Dennis Heimbigner.
2618
 */
2619
static int
2620
NC4_strict_att_exists(NC_FILE_INFO_T *h5)
2621
0
{
2622
0
    hid_t grpid = -1;
2623
0
    htri_t attr_exists;
2624
    
2625
    /* Get root group ID. */
2626
0
    grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid;
2627
2628
    /* See if the NC3_STRICT_ATT_NAME attribute exists */
2629
0
    if ((attr_exists = H5Aexists(grpid, NC3_STRICT_ATT_NAME)) < 0)
2630
0
        return 1;
2631
0
    return (attr_exists?1:0);
2632
0
}
2633
2634
/**
2635
 * @internal Walk group struct.
2636
 *
2637
 * @param gid HDF5 ID of starting group.
2638
 * @param countp Pointer that gets count.
2639
 *
2640
 * @returns NC_NOERR No error.
2641
 * @author Dennis Heimbigner
2642
 */
2643
static int
2644
NC4_walk(hid_t gid, int* countp)
2645
0
{
2646
0
    int ncstat = NC_NOERR;
2647
0
    int i,j,na;
2648
0
    ssize_t len;
2649
0
    hsize_t nobj;
2650
0
    herr_t err;
2651
0
    int otype;
2652
0
    hid_t grpid, dsid;
2653
0
    char name[NC_HDF5_MAX_NAME];
2654
2655
    /* walk group members of interest */
2656
0
    err = H5Gget_num_objs(gid, &nobj);
2657
0
    if(err < 0) return err;
2658
2659
0
    for(i = 0; i < nobj; i++) {
2660
        /* Get name & kind of object in the group */
2661
0
        len = H5Gget_objname_by_idx(gid,(hsize_t)i,name,(size_t)NC_HDF5_MAX_NAME);
2662
0
        if(len < 0) return len;
2663
2664
0
        otype =  H5Gget_objtype_by_idx(gid,(size_t)i);
2665
0
        switch(otype) {
2666
0
        case H5G_GROUP:
2667
0
            grpid = H5Gopen(gid,name);
2668
0
            NC4_walk(grpid,countp);
2669
0
            H5Gclose(grpid);
2670
0
            break;
2671
0
        case H5G_DATASET: /* variables */
2672
            /* Check for phony_dim */
2673
0
            if(strcmp(name,"phony_dim")==0)
2674
0
                *countp = *countp + 1;
2675
0
            dsid = H5Dopen(gid,name);
2676
0
            na = H5Aget_num_attrs(dsid);
2677
0
            for(j = 0; j < na; j++) {
2678
0
                hid_t aid =  H5Aopen_idx(dsid,(unsigned int)    j);
2679
0
                if(aid >= 0) {
2680
0
                    const NC_reservedatt* ra;
2681
0
                    ssize_t len = H5Aget_name(aid, NC_HDF5_MAX_NAME, name);
2682
0
                    if(len < 0) return len;
2683
                    /* Is this a netcdf-4 marker attribute */
2684
                    /* Is this a netcdf-4 marker attribute */
2685
0
                    ra = NC_findreserved(name);
2686
0
                    if(ra != NULL)
2687
0
                        *countp = *countp + 1;
2688
0
                }
2689
0
                H5Aclose(aid);
2690
0
            }
2691
0
            H5Dclose(dsid);
2692
0
            break;
2693
0
        default:/* ignore */
2694
0
            break;
2695
0
        }
2696
0
    }
2697
0
    return ncstat;
2698
0
}
2699
2700
int
2701
NC4_hdf5_remove_filter(NC_VAR_INFO_T* var, unsigned int filterid)
2702
0
{
2703
0
    int stat = NC_NOERR;
2704
0
    NC_HDF5_VAR_INFO_T *hdf5_var;
2705
0
    hid_t propid = -1;
2706
0
    herr_t herr = -1;
2707
0
    H5Z_filter_t hft;
2708
2709
0
    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
2710
0
    if ((propid = H5Dget_create_plist(hdf5_var->hdf_datasetid)) < 0)
2711
0
  {stat = NC_EHDFERR; goto done;}
2712
2713
0
    hft = filterid;
2714
0
    if((herr = H5Premove_filter(propid,hft)) < 0)
2715
0
  {stat = NC_EHDFERR; goto done;}
2716
0
done:
2717
0
    return stat;
2718
0
}