/src/netcdf-c/libnczarr/zdim.c
Line | Count | Source |
1 | | /* Copyright 2003-2019, University Corporation for Atmospheric |
2 | | * Research. See the COPYRIGHT file for copying and redistribution |
3 | | * conditions. */ |
4 | | |
5 | | /** |
6 | | * @file @internal This file is part of netcdf-4, a netCDF-like |
7 | | * interface for NCZ, or a ZARR backend for netCDF, depending on your |
8 | | * point of view. |
9 | | * |
10 | | * This file includes the ZARR code to deal with dimensions. |
11 | | * |
12 | | * @author Dennis Heimbigner, Ed Hartnett |
13 | | */ |
14 | | |
15 | | #include "zincludes.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 Dennis Heimbigner, Ed Hartnett |
39 | | */ |
40 | | int |
41 | | NCZ_def_dim(int ncid, const char *name, size_t len, int *idp) |
42 | 0 | { |
43 | 0 | NC *nc; |
44 | 0 | NC_GRP_INFO_T *grp; |
45 | 0 | NC_FILE_INFO_T *h5; |
46 | 0 | NC_DIM_INFO_T *dim; |
47 | 0 | char norm_name[NC_MAX_NAME + 1]; |
48 | 0 | int stat = NC_NOERR; |
49 | |
|
50 | 0 | LOG((2, "%s: ncid 0x%x name %s len %d", __func__, ncid, name, |
51 | 0 | (int)len)); |
52 | | |
53 | | /* Find our global metadata structure. */ |
54 | 0 | if ((stat = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) |
55 | 0 | return stat; |
56 | 0 | assert(h5 && nc && grp); |
57 | | |
58 | | /* If the file is read-only, return an error. */ |
59 | 0 | if (h5->no_write) |
60 | 0 | return NC_EPERM; |
61 | | |
62 | | /* Check some stuff if strict nc3 rules are in effect. */ |
63 | 0 | if (h5->cmode & NC_CLASSIC_MODEL) |
64 | 0 | { |
65 | | #ifdef LOOK |
66 | | /* Only one limited dimenson for strict nc3. */ |
67 | | if (len == NC_UNLIMITED) { |
68 | | int i; |
69 | | for(i=0;i<ncindexsize(grp->dim);i++) { |
70 | | dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i); |
71 | | if(dim == NULL) continue; |
72 | | if (dim->unlimited) |
73 | | return NC_EUNLIMIT; |
74 | | } |
75 | | } |
76 | | /* Must be in define mode for stict nc3. */ |
77 | | if (!(h5->flags & NC_INDEF)) |
78 | | return NC_ENOTINDEFINE; |
79 | | #endif |
80 | 0 | } |
81 | | |
82 | | /* Make sure this is a valid netcdf name. */ |
83 | 0 | if ((stat = nc4_check_name(name, norm_name))) |
84 | 0 | return stat; |
85 | | |
86 | | /* Since unlimited is supported, len >= 0 */ |
87 | 0 | if(len < 0) |
88 | 0 | return NC_EDIMSIZE; |
89 | | |
90 | | /* For classic model: dim length has to fit in a 32-bit unsigned |
91 | | * int, as permitted for 64-bit offset format. */ |
92 | 0 | if (h5->cmode & NC_CLASSIC_MODEL) |
93 | 0 | if(len > X_UINT_MAX) /* Backward compat */ |
94 | 0 | return NC_EDIMSIZE; |
95 | | |
96 | | /* Make sure the name is not already in use. */ |
97 | 0 | dim = (NC_DIM_INFO_T*)ncindexlookup(grp->dim,norm_name); |
98 | 0 | if(dim != NULL) |
99 | 0 | return NC_ENAMEINUSE; |
100 | | |
101 | | /* If it's not in define mode, enter define mode. Do this only |
102 | | * after checking all input data, so we only enter define mode if |
103 | | * input is good. */ |
104 | 0 | if (!(h5->flags & NC_INDEF)) |
105 | 0 | if ((stat = NCZ_redef(ncid))) |
106 | 0 | return stat; |
107 | | |
108 | | /* Add a dimension to the list. The ID must come from the file |
109 | | * information, since dimids are visible in more than one group. */ |
110 | 0 | if ((stat = nc4_dim_list_add(grp, norm_name, len, -1, &dim))) |
111 | 0 | return stat; |
112 | | |
113 | 0 | { |
114 | 0 | NCZ_DIM_INFO_T* diminfo = NULL; |
115 | | /* Create struct for NCZ-specific dim info. */ |
116 | 0 | if (!(diminfo = calloc(1, sizeof(NCZ_DIM_INFO_T)))) |
117 | 0 | return NC_ENOMEM; |
118 | 0 | dim->format_dim_info = diminfo; |
119 | 0 | diminfo->common.file = h5; |
120 | 0 | } |
121 | | |
122 | | /* Pass back the dimid. */ |
123 | 0 | if (idp) |
124 | 0 | *idp = dim->hdr.id; |
125 | |
|
126 | 0 | return stat; |
127 | 0 | } |
128 | | |
129 | | /** |
130 | | * @internal Find out name and len of a dim. For an unlimited |
131 | | * dimension, the length is the largest length so far written. If the |
132 | | * name of lenp pointers are NULL, they will be ignored. |
133 | | * |
134 | | * @param ncid File and group ID. |
135 | | * @param dimid Dimension ID. |
136 | | * @param name Pointer that gets name of the dimension. |
137 | | * @param lenp Pointer that gets length of dimension. |
138 | | * |
139 | | * @return ::NC_NOERR No error. |
140 | | * @return ::NC_EBADID Bad ncid. |
141 | | * @return ::NC_EDIMSIZE Dimension length too large. |
142 | | * @return ::NC_EBADDIM Dimension not found. |
143 | | * @author Dennis Heimbigner, Ed Hartnett |
144 | | */ |
145 | | int |
146 | | NCZ_inq_dim(int ncid, int dimid, char *name, size_t *lenp) |
147 | 0 | { |
148 | 0 | NC *nc; |
149 | 0 | NC_FILE_INFO_T *h5; |
150 | 0 | NC_GRP_INFO_T *grp, *dim_grp; |
151 | 0 | NC_DIM_INFO_T *dim; |
152 | 0 | int stat = NC_NOERR; |
153 | |
|
154 | 0 | LOG((2, "%s: ncid 0x%x dimid %d", __func__, ncid, dimid)); |
155 | | |
156 | | /* Find our global metadata structure. */ |
157 | 0 | if ((stat = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) |
158 | 0 | return stat; |
159 | 0 | assert(h5 && nc && grp); |
160 | | |
161 | | /* Find the dimension and its home group. */ |
162 | 0 | if ((stat = nc4_find_dim(grp, dimid, &dim, &dim_grp))) |
163 | 0 | return stat; |
164 | 0 | assert(dim); |
165 | | |
166 | | /* Return the dimension name, if the caller wants it. */ |
167 | 0 | if (name && dim->hdr.name) |
168 | 0 | strcpy(name, dim->hdr.name); |
169 | | |
170 | | /* Return the dimension length, if the caller wants it. */ |
171 | 0 | if (lenp) |
172 | 0 | { |
173 | | #ifdef LOOK |
174 | | if (dim->unlimited) |
175 | | { |
176 | | /* Since this is an unlimited dimension, go to the file |
177 | | and see how many records there are. Take the max number |
178 | | of records from all the vars that share this |
179 | | dimension. */ |
180 | | *lenp = 0; |
181 | | if ((stat = ncz_find_dim_len(dim_grp, dimid, &lenp))) |
182 | | return stat; |
183 | | } |
184 | | else |
185 | | #endif |
186 | 0 | { |
187 | 0 | if (dim->too_long) |
188 | 0 | { |
189 | 0 | stat = NC_EDIMSIZE; |
190 | 0 | *lenp = NC_MAX_UINT; |
191 | 0 | } |
192 | 0 | else |
193 | 0 | *lenp = dim->len; |
194 | 0 | } |
195 | 0 | } |
196 | |
|
197 | 0 | return stat; |
198 | 0 | } |
199 | | |
200 | | /** |
201 | | * @internal Rename a dimension, for those who like to prevaricate. |
202 | | * |
203 | | * @note If we're not in define mode, new name must be of equal or |
204 | | * less size, if strict nc3 rules are in effect for this file. But we |
205 | | * don't check this because reproducing the exact classic behavior |
206 | | * would be too difficult. See github issue #1340. |
207 | | * |
208 | | * @param ncid File and group ID. |
209 | | * @param dimid Dimension ID. |
210 | | * @param name New dimension name. |
211 | | * |
212 | | * @return ::NC_NOERR No error. |
213 | | * @return ::NC_EBADID Bad ncid. |
214 | | * @return ::NC_EHDFERR ZARR returned error. |
215 | | * @return ::NC_ENOMEM Out of memory. |
216 | | * @return ::NC_EINVAL Name must be provided. |
217 | | * @return ::NC_ENAMEINUSE Name is already in use in group. |
218 | | * @return ::NC_EMAXNAME Name is too long. |
219 | | * @return ::NC_EBADDIM Dimension not found. |
220 | | * @return ::NC_EBADNAME Name breaks netCDF name rules. |
221 | | * @return ::NC_EDIMMETA Unable to delete ZARR dataset. |
222 | | * @author Dennis Heimbigner, Ed Hartnett |
223 | | */ |
224 | | int |
225 | | NCZ_rename_dim(int ncid, int dimid, const char *name) |
226 | 0 | { |
227 | 0 | NC_GRP_INFO_T *grp; |
228 | 0 | NC_DIM_INFO_T *dim; |
229 | 0 | NC_FILE_INFO_T *h5; |
230 | 0 | char norm_name[NC_MAX_NAME + 1]; |
231 | 0 | int stat; |
232 | | |
233 | | /* Note: name is new name */ |
234 | 0 | if (!name) |
235 | 0 | return NC_EINVAL; |
236 | | |
237 | 0 | LOG((2, "%s: ncid 0x%x dimid %d name %s", __func__, ncid, |
238 | 0 | dimid, name)); |
239 | | |
240 | | /* Find info for this file and group, and set pointer to each. */ |
241 | 0 | if ((stat = nc4_find_grp_h5(ncid, &grp, &h5))) |
242 | 0 | return stat; |
243 | 0 | assert(h5 && grp); |
244 | | |
245 | | /* Trying to write to a read-only file? No way, Jose! */ |
246 | 0 | if (h5->no_write) |
247 | 0 | return NC_EPERM; |
248 | | |
249 | | /* Make sure this is a valid netcdf name. */ |
250 | 0 | if ((stat = nc4_check_name(name, norm_name))) |
251 | 0 | return stat; |
252 | | |
253 | | /* Get the original dim. */ |
254 | 0 | if ((stat = nc4_find_dim(grp, dimid, &dim, NULL))) |
255 | 0 | return stat; |
256 | 0 | assert(dim && dim->format_dim_info); |
257 | | |
258 | | /* Check if new name is in use. */ |
259 | 0 | if (ncindexlookup(grp->dim, norm_name)) |
260 | 0 | return NC_ENAMEINUSE; |
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 | | /* rebuild index. */ |
271 | 0 | if (!ncindexrebuild(grp->dim)) |
272 | 0 | return NC_EINTERNAL; |
273 | | |
274 | 0 | return NC_NOERR; |
275 | 0 | } |