1 | /* |
---|
2 | |
---|
3 | This file is part of netcdf-4, a netCDF-like interface for HDF5, or a |
---|
4 | HDF5 backend for netCDF, depending on your point of view. |
---|
5 | |
---|
6 | This file handles the nc4 dimension functions. |
---|
7 | |
---|
8 | Copyright 2003-5, University Corporation for Atmospheric Research. See |
---|
9 | the COPYRIGHT file for copying and redistribution conditions. |
---|
10 | |
---|
11 | $Id: nc4dim.c,v 1.41 2010/05/25 17:54:23 dmh Exp $ |
---|
12 | */ |
---|
13 | |
---|
14 | #include "nc4internal.h" |
---|
15 | |
---|
16 | #ifdef USE_PNETCDF |
---|
17 | #include <pnetcdf.h> |
---|
18 | #endif |
---|
19 | |
---|
20 | /* Netcdf-4 files might have more than one unlimited dimension, but |
---|
21 | return the first one anyway. */ |
---|
22 | /* Note that this code is inconsistent with nc_inq */ |
---|
23 | int |
---|
24 | NC4_inq_unlimdim(int ncid, int *unlimdimidp) |
---|
25 | { |
---|
26 | NC_FILE_INFO_T *nc; |
---|
27 | NC_GRP_INFO_T *grp, *g; |
---|
28 | NC_HDF5_FILE_INFO_T *h5; |
---|
29 | NC_DIM_INFO_T *dim; |
---|
30 | int found = 0; |
---|
31 | int retval; |
---|
32 | |
---|
33 | LOG((2, "called nc_inq_unlimdim")); |
---|
34 | |
---|
35 | if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) |
---|
36 | return retval; |
---|
37 | |
---|
38 | #ifdef USE_PNETCDF |
---|
39 | /* Take care of files created/opened with parallel-netcdf library. */ |
---|
40 | if (nc->pnetcdf_file) |
---|
41 | return ncmpi_inq_unlimdim(nc->int_ncid, unlimdimidp); |
---|
42 | #endif /* USE_PNETCDF */ |
---|
43 | |
---|
44 | /* Take care of netcdf-3 files. */ |
---|
45 | assert(h5); |
---|
46 | |
---|
47 | /* According to netcdf-3 manual, return -1 if there is no unlimited |
---|
48 | dimension. */ |
---|
49 | *unlimdimidp = -1; |
---|
50 | for (g = grp; g && !found; g = g->parent) |
---|
51 | { |
---|
52 | for (dim = g->dim; dim; dim = dim->next) |
---|
53 | { |
---|
54 | if (dim->unlimited) |
---|
55 | { |
---|
56 | *unlimdimidp = dim->dimid; |
---|
57 | found++; |
---|
58 | break; |
---|
59 | } |
---|
60 | } |
---|
61 | } |
---|
62 | |
---|
63 | return NC_NOERR; |
---|
64 | } |
---|
65 | |
---|
66 | /* Dimensions are defined in attributes attached to the appropriate |
---|
67 | group in the data file. */ |
---|
68 | int |
---|
69 | NC4_def_dim(int ncid, const char *name, size_t len, int *idp) |
---|
70 | { |
---|
71 | NC_FILE_INFO_T *nc; |
---|
72 | NC_GRP_INFO_T *grp; |
---|
73 | NC_HDF5_FILE_INFO_T *h5; |
---|
74 | NC_DIM_INFO_T *dim; |
---|
75 | char norm_name[NC_MAX_NAME + 1]; |
---|
76 | int retval = NC_NOERR; |
---|
77 | |
---|
78 | LOG((2, "nc_def_dim: ncid 0x%x name %s len %d", ncid, name, |
---|
79 | (int)len)); |
---|
80 | |
---|
81 | /* Find our global metadata structure. */ |
---|
82 | if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) |
---|
83 | return retval; |
---|
84 | |
---|
85 | #ifdef USE_PNETCDF |
---|
86 | /* Take care of files created/opened with parallel-netcdf library. */ |
---|
87 | if (nc->pnetcdf_file) |
---|
88 | return ncmpi_def_dim(nc->int_ncid, name, len, idp); |
---|
89 | #endif /* USE_PNETCDF */ |
---|
90 | |
---|
91 | /* Take care of netcdf-3 files. */ |
---|
92 | assert(h5); |
---|
93 | |
---|
94 | assert(h5 && nc && grp); |
---|
95 | |
---|
96 | /* If the file is read-only, return an error. */ |
---|
97 | if (h5->no_write) |
---|
98 | return NC_EPERM; |
---|
99 | |
---|
100 | /* Check some stuff if strict nc3 rules are in effect. */ |
---|
101 | if (h5->cmode & NC_CLASSIC_MODEL) |
---|
102 | { |
---|
103 | /* Only one limited dimenson for strict nc3. */ |
---|
104 | if (len == NC_UNLIMITED) |
---|
105 | for (dim = grp->dim; dim; dim = dim->next) |
---|
106 | if (dim->unlimited) |
---|
107 | return NC_EUNLIMIT; |
---|
108 | |
---|
109 | /* Must be in define mode for stict nc3. */ |
---|
110 | if (!(h5->flags & NC_INDEF)) |
---|
111 | return NC_ENOTINDEFINE; |
---|
112 | } |
---|
113 | |
---|
114 | /* If it's not in define mode, enter define mode. */ |
---|
115 | if (!(h5->flags & NC_INDEF)) |
---|
116 | if ((retval = nc_redef(ncid))) |
---|
117 | return retval; |
---|
118 | |
---|
119 | /* Make sure this is a valid netcdf name. */ |
---|
120 | if ((retval = nc4_check_name(name, norm_name))) |
---|
121 | return retval; |
---|
122 | |
---|
123 | /* For classic model: dim length has to fit in a 32-bit unsigned |
---|
124 | * int, as permitted for 64-bit offset format. */ |
---|
125 | if (h5->cmode & NC_CLASSIC_MODEL) |
---|
126 | if(len > X_UINT_MAX) /* Backward compat */ |
---|
127 | return NC_EDIMSIZE; |
---|
128 | |
---|
129 | /* Make sure the name is not already in use. */ |
---|
130 | for (dim = grp->dim; dim; dim = dim->next) |
---|
131 | if (!strncmp(dim->name, norm_name, NC_MAX_NAME)) |
---|
132 | return NC_ENAMEINUSE; |
---|
133 | |
---|
134 | /* Add a dimension to the list. The ID must come from the file |
---|
135 | * information, since dimids are visible in more than one group. */ |
---|
136 | nc4_dim_list_add(&grp->dim); |
---|
137 | grp->dim->dimid = grp->file->nc4_info->next_dimid++; |
---|
138 | |
---|
139 | /* Initialize the metadata for this dimension. */ |
---|
140 | if (!(grp->dim->name = malloc((strlen(norm_name) + 1) * sizeof(char)))) |
---|
141 | return NC_ENOMEM; |
---|
142 | strcpy(grp->dim->name, norm_name); |
---|
143 | grp->dim->len = len; |
---|
144 | grp->dim->dirty++; |
---|
145 | if (len == NC_UNLIMITED) |
---|
146 | grp->dim->unlimited++; |
---|
147 | |
---|
148 | /* Pass back the dimid. */ |
---|
149 | if (idp) |
---|
150 | *idp = grp->dim->dimid; |
---|
151 | |
---|
152 | return retval; |
---|
153 | } |
---|
154 | |
---|
155 | /* Given dim name, find its id. */ |
---|
156 | int |
---|
157 | NC4_inq_dimid(int ncid, const char *name, int *idp) |
---|
158 | { |
---|
159 | NC_FILE_INFO_T *nc; |
---|
160 | NC_GRP_INFO_T *grp, *g; |
---|
161 | NC_HDF5_FILE_INFO_T *h5; |
---|
162 | NC_DIM_INFO_T *dim; |
---|
163 | char norm_name[NC_MAX_NAME + 1]; |
---|
164 | int finished = 0; |
---|
165 | int retval; |
---|
166 | |
---|
167 | LOG((2, "nc_inq_dimid: ncid 0x%x name %s", ncid, name)); |
---|
168 | |
---|
169 | /* Find metadata for this file. */ |
---|
170 | if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) |
---|
171 | return retval; |
---|
172 | |
---|
173 | #ifdef USE_PNETCDF |
---|
174 | /* Take care of files created/opened with parallel-netcdf library. */ |
---|
175 | if (nc->pnetcdf_file) |
---|
176 | return ncmpi_inq_dimid(nc->int_ncid, name, idp); |
---|
177 | #endif /* USE_PNETCDF */ |
---|
178 | |
---|
179 | /* Handle netcdf-3 files. */ |
---|
180 | assert(h5); |
---|
181 | |
---|
182 | assert(nc && grp); |
---|
183 | |
---|
184 | /* Normalize name. */ |
---|
185 | if ((retval = nc4_normalize_name(name, norm_name))) |
---|
186 | return retval; |
---|
187 | |
---|
188 | /* Go through each dim and check for a name match. */ |
---|
189 | for (g = grp; g && !finished; g = g->parent) |
---|
190 | for (dim = g->dim; dim; dim = dim->next) |
---|
191 | if (!strncmp(dim->name, norm_name, NC_MAX_NAME)) |
---|
192 | { |
---|
193 | if (idp) |
---|
194 | *idp = dim->dimid; |
---|
195 | return NC_NOERR; |
---|
196 | } |
---|
197 | |
---|
198 | return NC_EBADDIM; |
---|
199 | } |
---|
200 | |
---|
201 | /* Find out name and len of a dim. For an unlimited dimension, the |
---|
202 | length is the largest lenght so far written. If the name of lenp |
---|
203 | pointers are NULL, they will be ignored. */ |
---|
204 | int |
---|
205 | NC4_inq_dim(int ncid, int dimid, char *name, size_t *lenp) |
---|
206 | { |
---|
207 | NC_FILE_INFO_T *nc; |
---|
208 | NC_HDF5_FILE_INFO_T *h5; |
---|
209 | NC_GRP_INFO_T *grp, *dim_grp; |
---|
210 | NC_DIM_INFO_T *dim; |
---|
211 | int ret = NC_NOERR; |
---|
212 | |
---|
213 | LOG((2, "nc_inq_dim: ncid 0x%x dimid %d", ncid, dimid)); |
---|
214 | |
---|
215 | /* Find our global metadata structure. */ |
---|
216 | if ((ret = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) |
---|
217 | return ret; |
---|
218 | |
---|
219 | #ifdef USE_PNETCDF |
---|
220 | /* Take care of files created/opened with parallel-netcdf library. */ |
---|
221 | if (nc->pnetcdf_file) |
---|
222 | { |
---|
223 | MPI_Offset mpi_len; |
---|
224 | if ((ret = ncmpi_inq_dim(nc->int_ncid, dimid, name, &mpi_len))) |
---|
225 | return ret; |
---|
226 | if (lenp) |
---|
227 | *lenp = mpi_len; |
---|
228 | } |
---|
229 | #endif /* USE_PNETCDF */ |
---|
230 | |
---|
231 | /* Take care of netcdf-3 files. */ |
---|
232 | assert(h5); |
---|
233 | |
---|
234 | assert(nc && grp); |
---|
235 | |
---|
236 | /* Find the dimension and its home group. */ |
---|
237 | if ((ret = nc4_find_dim(grp, dimid, &dim, &dim_grp))) |
---|
238 | return ret; |
---|
239 | assert(dim); |
---|
240 | |
---|
241 | /* Return the dimension name, if the caller wants it. */ |
---|
242 | if (name && dim->name) |
---|
243 | strcpy(name, dim->name); |
---|
244 | |
---|
245 | /* Return the dimension length, if the caller wants it. */ |
---|
246 | if (lenp) |
---|
247 | { |
---|
248 | if (dim->unlimited) |
---|
249 | { |
---|
250 | /* Since this is an unlimited dimension, go to the file |
---|
251 | and see how many records there are. Take the max number |
---|
252 | of records from all the vars that share this |
---|
253 | dimension. */ |
---|
254 | *lenp = 0; |
---|
255 | if ((ret = nc4_find_dim_len(dim_grp, dimid, &lenp))) |
---|
256 | return ret; |
---|
257 | } |
---|
258 | else |
---|
259 | { |
---|
260 | if (dim->too_long) |
---|
261 | { |
---|
262 | ret = NC_EDIMSIZE; |
---|
263 | *lenp = NC_MAX_UINT; |
---|
264 | } |
---|
265 | else |
---|
266 | *lenp = dim->len; |
---|
267 | } |
---|
268 | } |
---|
269 | |
---|
270 | return ret; |
---|
271 | } |
---|
272 | |
---|
273 | /* Rename a dimension, for those who like to prevaricate. */ |
---|
274 | int |
---|
275 | NC4_rename_dim(int ncid, int dimid, const char *name) |
---|
276 | { |
---|
277 | NC_FILE_INFO_T *nc; |
---|
278 | NC_GRP_INFO_T *grp; |
---|
279 | NC_HDF5_FILE_INFO_T *h5; |
---|
280 | NC_DIM_INFO_T *dim; |
---|
281 | char norm_name[NC_MAX_NAME + 1]; |
---|
282 | int retval; |
---|
283 | |
---|
284 | if (!name) |
---|
285 | return NC_EINVAL; |
---|
286 | |
---|
287 | LOG((2, "nc_rename_dim: ncid 0x%x dimid %d name %s", ncid, |
---|
288 | dimid, name)); |
---|
289 | |
---|
290 | /* Find info for this file and group, and set pointer to each. */ |
---|
291 | if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) |
---|
292 | return retval; |
---|
293 | assert(nc); |
---|
294 | |
---|
295 | #ifdef USE_PNETCDF |
---|
296 | /* Take care of files created/opened with parallel-netcdf library. */ |
---|
297 | if (nc->pnetcdf_file) |
---|
298 | return ncmpi_rename_dim(nc->int_ncid, dimid, name); |
---|
299 | #endif /* USE_PNETCDF */ |
---|
300 | |
---|
301 | /* Handle netcdf-3 cases. */ |
---|
302 | assert(h5); |
---|
303 | assert(h5 && grp); |
---|
304 | |
---|
305 | /* Trying to write to a read-only file? No way, Jose! */ |
---|
306 | if (h5->no_write) |
---|
307 | return NC_EPERM; |
---|
308 | |
---|
309 | /* Make sure this is a valid netcdf name. */ |
---|
310 | if ((retval = nc4_check_name(name, norm_name))) |
---|
311 | return retval; |
---|
312 | |
---|
313 | /* Make sure the new name is not already in use in this group. */ |
---|
314 | for (dim = grp->dim; dim; dim = dim->next) |
---|
315 | if (!strncmp(dim->name, norm_name, NC_MAX_NAME)) |
---|
316 | return NC_ENAMEINUSE; |
---|
317 | |
---|
318 | /* Find the dim. */ |
---|
319 | for (dim = grp->dim; dim; dim = dim->next) |
---|
320 | if (dim->dimid == dimid) |
---|
321 | break; |
---|
322 | if (!dim) |
---|
323 | return NC_EBADDIM; |
---|
324 | |
---|
325 | /* If not in define mode, switch to it, unless the new name is |
---|
326 | * shorter. (This is in accordance with the v3 interface.) */ |
---|
327 | /* if (!(h5->flags & NC_INDEF) && strlen(name) > strlen(dim->name)) */ |
---|
328 | /* { */ |
---|
329 | /* if (h5->cmode & NC_CLASSIC_MODEL) */ |
---|
330 | /* return NC_ENOTINDEFINE; */ |
---|
331 | /* if ((retval = nc_redef(ncid))) */ |
---|
332 | /* return retval; */ |
---|
333 | /* } */ |
---|
334 | |
---|
335 | /* Save the old name, we'll need it to rename this object when we |
---|
336 | * sync to HDF5 file. But if there already is an old_name saved, |
---|
337 | * just stick with what we've got, since the user might be renaming |
---|
338 | * the crap out of this thing, without ever syncing with the |
---|
339 | * file. When the sync does take place, we only need the original |
---|
340 | * name of the dim, not any of the intermediate ones. If the user |
---|
341 | * could just make up his mind, we could all get on to writing some |
---|
342 | * data... */ |
---|
343 | if (!dim->old_name) |
---|
344 | { |
---|
345 | if (!(dim->old_name = malloc((strlen(dim->name) + 1) * sizeof(char)))) |
---|
346 | return NC_ENOMEM; |
---|
347 | strcpy(dim->old_name, dim->name); |
---|
348 | } |
---|
349 | |
---|
350 | /* Give the dimension its new name in metadata. UTF8 normalization |
---|
351 | * has been done. */ |
---|
352 | free(dim->name); |
---|
353 | if (!(dim->name = malloc((strlen(norm_name) + 1) * sizeof(char)))) |
---|
354 | return NC_ENOMEM; |
---|
355 | strcpy(dim->name, norm_name); |
---|
356 | |
---|
357 | return NC_NOERR; |
---|
358 | } |
---|
359 | |
---|
360 | /* Returns an array of unlimited dimension ids.The user can get the |
---|
361 | number of unlimited dimensions by first calling this with NULL for |
---|
362 | the second pointer. |
---|
363 | */ |
---|
364 | int |
---|
365 | NC4_inq_unlimdims(int ncid, int *nunlimdimsp, int *unlimdimidsp) |
---|
366 | { |
---|
367 | NC_DIM_INFO_T *dim; |
---|
368 | NC_GRP_INFO_T *grp; |
---|
369 | NC_FILE_INFO_T *nc; |
---|
370 | NC_HDF5_FILE_INFO_T *h5; |
---|
371 | int num_unlim = 0; |
---|
372 | int retval; |
---|
373 | |
---|
374 | LOG((2, "nc_inq_unlimdims: ncid 0x%x", ncid)); |
---|
375 | |
---|
376 | /* Find info for this file and group, and set pointer to each. */ |
---|
377 | if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) |
---|
378 | return retval; |
---|
379 | |
---|
380 | /* Get our dim info. */ |
---|
381 | assert(h5); |
---|
382 | { |
---|
383 | for (dim=grp->dim; dim; dim=dim->next) |
---|
384 | { |
---|
385 | if (dim->unlimited) |
---|
386 | { |
---|
387 | if (unlimdimidsp) |
---|
388 | unlimdimidsp[num_unlim] = dim->dimid; |
---|
389 | num_unlim++; |
---|
390 | } |
---|
391 | } |
---|
392 | } |
---|
393 | |
---|
394 | /* Give the number if the user wants it. */ |
---|
395 | if (nunlimdimsp) |
---|
396 | *nunlimdimsp = num_unlim; |
---|
397 | |
---|
398 | return NC_NOERR; |
---|
399 | } |
---|
400 | |
---|
401 | |
---|