Coverage Report

Created: 2025-06-09 07:02

/src/gdal/netcdf-c-4.7.4/libhdf5/hdf5dim.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright 2003-2019, University Corporation for Atmospheric
2
 * Research. See the COPYRIGHT file for copying and redistribution
3
 * conditions. */
4
/**
5
 * @file @internal This file is part of netcdf-4, a netCDF-like
6
 * interface for HDF5, or a HDF5 backend for netCDF, depending on your
7
 * point of view.
8
 *
9
 * This file includes the HDF5 code to deal with dimensions.
10
 *
11
 * @author Ed Hartnett
12
 */
13
14
#include "config.h"
15
#include "hdf5internal.h"
16
17
/**
18
 * @internal Dimensions are defined in attributes attached to the
19
 * appropriate group in the data file.
20
 *
21
 * @param ncid File and group ID.
22
 * @param name Name of the new dimension.
23
 * @param len Length of the new dimension.
24
 * @param idp Pointer that gets the ID of the new dimension. Ignored
25
 * if NULL.
26
 *
27
 * @return ::NC_NOERR No error.
28
 * @return ::NC_EBADID Bad ncid.
29
 * @return ::NC_EMAXNAME Name is too long.
30
 * @return ::NC_EBADNAME Name breaks netCDF name rules.
31
 * @return ::NC_EINVAL Invalid input.
32
 * @return ::NC_EPERM Read-only file.
33
 * @return ::NC_EUNLIMIT Only one unlimited dim for classic model.
34
 * @return ::NC_ENOTINDEFINE Not in define mode.
35
 * @return ::NC_EDIMSIZE Dim length too large.
36
 * @return ::NC_ENAMEINUSE Name already in use in group.
37
 * @return ::NC_ENOMEM Out of memory.
38
 * @author Ed Hartnett
39
 */
40
int
41
NC4_def_dim(int ncid, const char *name, size_t len, int *idp)
42
18.2k
{
43
18.2k
    NC *nc;
44
18.2k
    NC_GRP_INFO_T *grp;
45
18.2k
    NC_FILE_INFO_T *h5;
46
18.2k
    NC_DIM_INFO_T *dim;
47
18.2k
    char norm_name[NC_MAX_NAME + 1];
48
18.2k
    int retval = NC_NOERR;
49
18.2k
    int i;
50
51
18.2k
    LOG((2, "%s: ncid 0x%x name %s len %d", __func__, ncid, name,
52
18.2k
         (int)len));
53
54
    /* Find our global metadata structure. */
55
18.2k
    if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
56
0
        return retval;
57
18.2k
    assert(h5 && nc && grp);
58
59
    /* If the file is read-only, return an error. */
60
18.2k
    if (h5->no_write)
61
0
        return NC_EPERM;
62
63
    /* Check some stuff if strict nc3 rules are in effect. */
64
18.2k
    if (h5->cmode & NC_CLASSIC_MODEL)
65
5.64k
    {
66
        /* Only one limited dimenson for strict nc3. */
67
5.64k
        if (len == NC_UNLIMITED) {
68
6.94k
            for(i=0;i<ncindexsize(grp->dim);i++) {
69
6.33k
                dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
70
6.33k
                if(dim == NULL) continue;
71
6.33k
                if (dim->unlimited)
72
2.68k
                    return NC_EUNLIMIT;
73
6.33k
            }
74
3.29k
        }
75
        /* Must be in define mode for stict nc3. */
76
2.95k
        if (!(h5->flags & NC_INDEF))
77
0
            return NC_ENOTINDEFINE;
78
2.95k
    }
79
80
    /* Make sure this is a valid netcdf name. */
81
15.6k
    if ((retval = nc4_check_name(name, norm_name)))
82
5.00k
        return retval;
83
84
    /* For classic model: dim length has to fit in a 32-bit unsigned
85
     * int, as permitted for 64-bit offset format. */
86
10.6k
    if (h5->cmode & NC_CLASSIC_MODEL)
87
2.50k
        if(len > X_UINT_MAX) /* Backward compat */
88
0
            return NC_EDIMSIZE;
89
90
    /* Make sure the name is not already in use. */
91
10.6k
    dim = (NC_DIM_INFO_T*)ncindexlookup(grp->dim,norm_name);
92
10.6k
    if(dim != NULL)
93
6.40k
        return NC_ENAMEINUSE;
94
95
    /* If it's not in define mode, enter define mode. Do this only
96
     * after checking all input data, so we only enter define mode if
97
     * input is good. */
98
4.20k
    if (!(h5->flags & NC_INDEF))
99
0
        if ((retval = NC4_redef(ncid)))
100
0
            return retval;
101
102
    /* Add a dimension to the list. The ID must come from the file
103
     * information, since dimids are visible in more than one group. */
104
4.20k
    if ((retval = nc4_dim_list_add(grp, norm_name, len, -1, &dim)))
105
0
        return retval;
106
107
    /* Create struct for HDF5-specific dim info. */
108
4.20k
    if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T))))
109
0
        return NC_ENOMEM;
110
111
    /* Pass back the dimid. */
112
4.20k
    if (idp)
113
4.20k
        *idp = dim->hdr.id;
114
115
4.20k
    return retval;
116
4.20k
}
117
118
/**
119
 * @internal Find out name and len of a dim. For an unlimited
120
 * dimension, the length is the largest length so far written. If the
121
 * name of lenp pointers are NULL, they will be ignored.
122
 *
123
 * @param ncid File and group ID.
124
 * @param dimid Dimension ID.
125
 * @param name Pointer that gets name of the dimension.
126
 * @param lenp Pointer that gets length of dimension.
127
 *
128
 * @return ::NC_NOERR No error.
129
 * @return ::NC_EBADID Bad ncid.
130
 * @return ::NC_EDIMSIZE Dimension length too large.
131
 * @return ::NC_EBADDIM Dimension not found.
132
 * @author Ed Hartnett
133
 */
134
int
135
NC4_inq_dim(int ncid, int dimid, char *name, size_t *lenp)
136
3.62k
{
137
3.62k
    NC *nc;
138
3.62k
    NC_FILE_INFO_T *h5;
139
3.62k
    NC_GRP_INFO_T *grp, *dim_grp;
140
3.62k
    NC_DIM_INFO_T *dim;
141
3.62k
    int ret = NC_NOERR;
142
143
3.62k
    LOG((2, "%s: ncid 0x%x dimid %d", __func__, ncid, dimid));
144
145
    /* Find our global metadata structure. */
146
3.62k
    if ((ret = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
147
0
        return ret;
148
3.62k
    assert(h5 && nc && grp);
149
150
    /* Find the dimension and its home group. */
151
3.62k
    if ((ret = nc4_find_dim(grp, dimid, &dim, &dim_grp)))
152
0
        return ret;
153
3.62k
    assert(dim);
154
155
    /* Return the dimension name, if the caller wants it. */
156
3.62k
    if (name && dim->hdr.name)
157
2.14k
        strcpy(name, dim->hdr.name);
158
159
    /* Return the dimension length, if the caller wants it. */
160
3.62k
    if (lenp)
161
1.48k
    {
162
1.48k
        if (dim->unlimited)
163
714
        {
164
            /* Since this is an unlimited dimension, go to the file
165
               and see how many records there are. Take the max number
166
               of records from all the vars that share this
167
               dimension. */
168
714
            *lenp = 0;
169
714
            if ((ret = nc4_find_dim_len(dim_grp, dimid, &lenp)))
170
0
                return ret;
171
714
        }
172
766
        else
173
766
        {
174
766
            if (dim->too_long)
175
0
            {
176
0
                ret = NC_EDIMSIZE;
177
0
                *lenp = NC_MAX_UINT;
178
0
            }
179
766
            else
180
766
                *lenp = dim->len;
181
766
        }
182
1.48k
    }
183
184
3.62k
    return ret;
185
3.62k
}
186
187
/**
188
 * @internal Rename a dimension, for those who like to prevaricate.
189
 *
190
 * @note If we're not in define mode, new name must be of equal or
191
 * less size, if strict nc3 rules are in effect for this file. But we
192
 * don't check this because reproducing the exact classic behavior
193
 * would be too difficult. See github issue #1340.
194
 *
195
 * @param ncid File and group ID.
196
 * @param dimid Dimension ID.
197
 * @param name New dimension name.
198
 *
199
 * @return ::NC_NOERR No error.
200
 * @return ::NC_EBADID Bad ncid.
201
 * @return ::NC_EHDFERR HDF5 returned error.
202
 * @return ::NC_ENOMEM Out of memory.
203
 * @return ::NC_EINVAL Name must be provided.
204
 * @return ::NC_ENAMEINUSE Name is already in use in group.
205
 * @return ::NC_EMAXNAME Name is too long.
206
 * @return ::NC_EBADDIM Dimension not found.
207
 * @return ::NC_EBADNAME Name breaks netCDF name rules.
208
 * @return ::NC_EDIMMETA Unable to delete HDF5 dataset.
209
 * @author Ed Hartnett
210
 */
211
int
212
NC4_rename_dim(int ncid, int dimid, const char *name)
213
0
{
214
0
    NC_GRP_INFO_T *grp;
215
0
    NC_DIM_INFO_T *dim;
216
0
    NC_HDF5_DIM_INFO_T *hdf5_dim;
217
0
    NC_FILE_INFO_T *h5;
218
0
    char norm_name[NC_MAX_NAME + 1];
219
0
    int retval;
220
221
    /* Note: name is new name */
222
0
    if (!name)
223
0
        return NC_EINVAL;
224
225
0
    LOG((2, "%s: ncid 0x%x dimid %d name %s", __func__, ncid,
226
0
         dimid, name));
227
228
    /* Find info for this file and group, and set pointer to each. */
229
0
    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
230
0
        return retval;
231
0
    assert(h5 && grp);
232
233
    /* Trying to write to a read-only file? No way, Jose! */
234
0
    if (h5->no_write)
235
0
        return NC_EPERM;
236
237
    /* Make sure this is a valid netcdf name. */
238
0
    if ((retval = nc4_check_name(name, norm_name)))
239
0
        return retval;
240
241
    /* Get the original dim. */
242
0
    if ((retval = nc4_find_dim(grp, dimid, &dim, NULL)))
243
0
        return retval;
244
0
    assert(dim && dim->format_dim_info);
245
0
    hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
246
247
    /* Check if new name is in use. */
248
0
    if (ncindexlookup(grp->dim, norm_name))
249
0
        return NC_ENAMEINUSE;
250
251
    /* Check for renaming dimension w/o variable. */
252
0
    if (hdf5_dim->hdf_dimscaleid)
253
0
    {
254
0
        assert(!dim->coord_var);
255
0
        LOG((3, "dim %s is a dim without variable", dim->hdr.name));
256
257
        /* Delete the dimscale-only dataset. */
258
0
        if ((retval = delete_dimscale_dataset(grp, dimid, dim)))
259
0
            return retval;
260
0
    }
261
262
    /* Give the dimension its new name in metadata. UTF8 normalization
263
     * has been done. */
264
0
    assert(dim->hdr.name);
265
0
    free(dim->hdr.name);
266
0
    if (!(dim->hdr.name = strdup(norm_name)))
267
0
        return NC_ENOMEM;
268
0
    LOG((3, "dim is now named %s", dim->hdr.name));
269
270
    /* Fix hash key and rebuild index. */
271
0
    dim->hdr.hashkey = NC_hashmapkey(dim->hdr.name,strlen(dim->hdr.name));
272
0
    if (!ncindexrebuild(grp->dim))
273
0
        return NC_EINTERNAL;
274
275
    /* Check if dimension was a coordinate variable, but names are
276
     * different now. */
277
0
    if (dim->coord_var && strcmp(dim->hdr.name, dim->coord_var->hdr.name))
278
0
    {
279
        /* Break up the coordinate variable. */
280
0
        if ((retval = nc4_break_coord_var(grp, dim->coord_var, dim)))
281
0
            return retval;
282
0
    }
283
284
    /* Check if dimension should become a coordinate variable. */
285
0
    if (!dim->coord_var)
286
0
    {
287
0
        NC_VAR_INFO_T *var;
288
289
        /* Attempt to find a variable with the same name as the
290
         * dimension in the current group. */
291
0
        if ((retval = nc4_find_var(grp, dim->hdr.name, &var)))
292
0
            return retval;
293
294
        /* Check if we found a variable and the variable has the
295
         * dimension in index 0. */
296
0
        if (var && var->dim[0] == dim)
297
0
        {
298
            /* Sanity check */
299
0
            assert(var->dimids[0] == dim->hdr.id);
300
301
            /* Reform the coordinate variable. */
302
0
            if ((retval = nc4_reform_coord_var(grp, var, dim)))
303
0
                return retval;
304
0
        }
305
0
    }
306
307
0
    return NC_NOERR;
308
0
}