/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 | 813 | { |
43 | 813 | NC *nc; |
44 | 813 | NC_GRP_INFO_T *grp; |
45 | 813 | NC_FILE_INFO_T *h5; |
46 | 813 | NC_DIM_INFO_T *dim; |
47 | 813 | char norm_name[NC_MAX_NAME + 1]; |
48 | 813 | int retval = NC_NOERR; |
49 | 813 | int i; |
50 | | |
51 | 813 | LOG((2, "%s: ncid 0x%x name %s len %d", __func__, ncid, name, |
52 | 813 | (int)len)); |
53 | | |
54 | | /* Find our global metadata structure. */ |
55 | 813 | if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) |
56 | 0 | return retval; |
57 | 813 | assert(h5 && nc && grp); |
58 | | |
59 | | /* If the file is read-only, return an error. */ |
60 | 813 | if (h5->no_write) |
61 | 0 | return NC_EPERM; |
62 | | |
63 | | /* Check some stuff if strict nc3 rules are in effect. */ |
64 | 813 | if (h5->cmode & NC_CLASSIC_MODEL) |
65 | 379 | { |
66 | | /* Only one limited dimenson for strict nc3. */ |
67 | 379 | if (len == NC_UNLIMITED) { |
68 | 325 | for(i=0;i<ncindexsize(grp->dim);i++) { |
69 | 210 | dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i); |
70 | 210 | if(dim == NULL) continue; |
71 | 210 | if (dim->unlimited) |
72 | 66 | return NC_EUNLIMIT; |
73 | 210 | } |
74 | 181 | } |
75 | | /* Must be in define mode for stict nc3. */ |
76 | 313 | if (!(h5->flags & NC_INDEF)) |
77 | 0 | return NC_ENOTINDEFINE; |
78 | 313 | } |
79 | | |
80 | | /* Make sure this is a valid netcdf name. */ |
81 | 747 | if ((retval = nc4_check_name(name, norm_name))) |
82 | 51 | 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 | 696 | if (h5->cmode & NC_CLASSIC_MODEL) |
87 | 300 | if(len > X_UINT_MAX) /* Backward compat */ |
88 | 0 | return NC_EDIMSIZE; |
89 | | |
90 | | /* Make sure the name is not already in use. */ |
91 | 696 | dim = (NC_DIM_INFO_T*)ncindexlookup(grp->dim,norm_name); |
92 | 696 | if(dim != NULL) |
93 | 339 | 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 | 357 | 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 | 357 | 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 | 357 | 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 | 357 | if (idp) |
113 | 357 | *idp = dim->hdr.id; |
114 | | |
115 | 357 | return retval; |
116 | 357 | } |
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 | 5.08k | { |
137 | 5.08k | NC *nc; |
138 | 5.08k | NC_FILE_INFO_T *h5; |
139 | 5.08k | NC_GRP_INFO_T *grp, *dim_grp; |
140 | 5.08k | NC_DIM_INFO_T *dim; |
141 | 5.08k | int ret = NC_NOERR; |
142 | | |
143 | 5.08k | LOG((2, "%s: ncid 0x%x dimid %d", __func__, ncid, dimid)); |
144 | | |
145 | | /* Find our global metadata structure. */ |
146 | 5.08k | if ((ret = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) |
147 | 0 | return ret; |
148 | 5.08k | assert(h5 && nc && grp); |
149 | | |
150 | | /* Find the dimension and its home group. */ |
151 | 5.08k | if ((ret = nc4_find_dim(grp, dimid, &dim, &dim_grp))) |
152 | 0 | return ret; |
153 | 5.08k | assert(dim); |
154 | | |
155 | | /* Return the dimension name, if the caller wants it. */ |
156 | 5.08k | if (name && dim->hdr.name) |
157 | 21 | strcpy(name, dim->hdr.name); |
158 | | |
159 | | /* Return the dimension length, if the caller wants it. */ |
160 | 5.08k | if (lenp) |
161 | 5.06k | { |
162 | 5.06k | if (dim->unlimited) |
163 | 3 | { |
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 | 3 | *lenp = 0; |
169 | 3 | if ((ret = nc4_find_dim_len(dim_grp, dimid, &lenp))) |
170 | 0 | return ret; |
171 | 3 | } |
172 | 5.06k | else |
173 | 5.06k | { |
174 | 5.06k | if (dim->too_long) |
175 | 0 | { |
176 | 0 | ret = NC_EDIMSIZE; |
177 | 0 | *lenp = NC_MAX_UINT; |
178 | 0 | } |
179 | 5.06k | else |
180 | 5.06k | *lenp = dim->len; |
181 | 5.06k | } |
182 | 5.06k | } |
183 | | |
184 | 5.08k | return ret; |
185 | 5.08k | } |
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 | } |