1 /*
2 * Copyright 1996, University Corporation for Atmospheric Research
3 * See netcdf/COPYRIGHT file for copying and redistribution conditions.
4 */
5 /* $Id: dim.c,v 1.83 2010/05/25 17:54:15 dmh Exp $ */
6
7 #if HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #include "nc3internal.h"
12 #include <stdlib.h>
13 #include <string.h>
14 #include <assert.h>
15 #include "ncx.h"
16 #include "fbits.h"
17 #include "ncutf8.h"
18
19 /*
20 * Free dim
21 * Formerly
22 NC_free_dim(dim)
23 */
24 void
free_NC_dim(NC_dim * dimp)25 free_NC_dim(NC_dim *dimp)
26 {
27 if(dimp == NULL)
28 return;
29 free_NC_string(dimp->name);
30 free(dimp);
31 }
32
33
34 NC_dim *
new_x_NC_dim(NC_string * name)35 new_x_NC_dim(NC_string *name)
36 {
37 NC_dim *dimp;
38
39 dimp = (NC_dim *) malloc(sizeof(NC_dim));
40 if(dimp == NULL)
41 return NULL;
42
43 dimp->name = name;
44 dimp->size = 0;
45
46 return(dimp);
47 }
48
49
50 /*
51 * Formerly
52 NC_new_dim(const char *uname, long size)
53 */
54 static NC_dim *
new_NC_dim(const char * uname,size_t size)55 new_NC_dim(const char *uname, size_t size)
56 {
57 NC_string *strp;
58 NC_dim *dimp = NULL;
59 int stat = NC_NOERR;
60 char* name = NULL;
61
62 stat = nc_utf8_normalize((const unsigned char *)uname,(unsigned char **)&name);
63 if(stat != NC_NOERR)
64 goto done;
65 strp = new_NC_string(strlen(name), name);
66 if(strp == NULL)
67 {stat = NC_ENOMEM; goto done;}
68
69 dimp = new_x_NC_dim(strp);
70 if(dimp == NULL)
71 {
72 free_NC_string(strp);
73 goto done;
74 }
75
76 dimp->size = size;
77
78 done:
79 if(name) free(name);
80 return (dimp);
81 }
82
83
84 static NC_dim *
dup_NC_dim(const NC_dim * dimp)85 dup_NC_dim(const NC_dim *dimp)
86 {
87 return new_NC_dim(dimp->name->cp, dimp->size);
88 }
89
90 /*
91 * Step thru NC_DIMENSION array, seeking the UNLIMITED dimension.
92 * Return dimid or -1 on not found.
93 * *dimpp is set to the appropriate NC_dim.
94 * The loop structure is odd. In order to parallelize,
95 * we moved a clearer 'break' inside the loop body to the loop test.
96 */
97 int
find_NC_Udim(const NC_dimarray * ncap,NC_dim ** dimpp)98 find_NC_Udim(const NC_dimarray *ncap, NC_dim **dimpp)
99 {
100 assert(ncap != NULL);
101
102 if(ncap->nelems == 0)
103 return -1;
104
105 {
106 int dimid = 0;
107 NC_dim **loc = ncap->value;
108
109 for(; (size_t) dimid < ncap->nelems
110 && (*loc)->size != NC_UNLIMITED; dimid++, loc++)
111 {
112 /*EMPTY*/
113 }
114 if(dimid >= ncap->nelems)
115 return(-1); /* not found */
116 /* else, normal return */
117 if(dimpp != NULL)
118 *dimpp = *loc;
119 return dimid;
120 }
121 }
122
123 /*
124 * Step thru NC_DIMENSION array, seeking match on uname.
125 * Return dimid or -1 on not found.
126 * *dimpp is set to the appropriate NC_dim.
127 * The loop structure is odd. In order to parallelize,
128 * we moved a clearer 'break' inside the loop body to the loop test.
129 */
130 static int
NC_finddim(const NC_dimarray * ncap,const char * uname,NC_dim ** dimpp)131 NC_finddim(const NC_dimarray *ncap, const char *uname, NC_dim **dimpp)
132 {
133 int dimid = -1;
134 char *name = NULL;
135 uintptr_t data;
136
137 assert(ncap != NULL);
138 if(ncap->nelems == 0)
139 goto done;
140 /* normalized version of uname */
141 if(nc_utf8_normalize((const unsigned char *)uname,(unsigned char **)&name))
142 goto done;
143 if(NC_hashmapget(ncap->hashmap, name, strlen(name), &data) == 0)
144 goto done;
145 dimid = (int)data;
146 if(dimpp) *dimpp = ncap->value[dimid];
147
148 done:
149 if(name) free(name);
150 return dimid;
151 }
152
153
154 /* dimarray */
155
156
157 /*
158 * Free the stuff "in" (referred to by) an NC_dimarray.
159 * Leaves the array itself allocated.
160 */
161 void
free_NC_dimarrayV0(NC_dimarray * ncap)162 free_NC_dimarrayV0(NC_dimarray *ncap)
163 {
164 assert(ncap != NULL);
165
166 if(ncap->nelems == 0)
167 return;
168
169 assert(ncap->value != NULL);
170
171 {
172 NC_dim **dpp = ncap->value;
173 NC_dim *const *const end = &dpp[ncap->nelems];
174 for( /*NADA*/; dpp < end; dpp++)
175 {
176 free_NC_dim(*dpp);
177 *dpp = NULL;
178 }
179 }
180 ncap->nelems = 0;
181 }
182
183
184 /*
185 * Free NC_dimarray values.
186 * formerly
187 NC_free_array()
188 */
189 void
free_NC_dimarrayV(NC_dimarray * ncap)190 free_NC_dimarrayV(NC_dimarray *ncap)
191 {
192 assert(ncap != NULL);
193
194 if(ncap->nalloc == 0)
195 return;
196
197 NC_hashmapfree(ncap->hashmap);
198 ncap->hashmap = NULL;
199
200 assert(ncap->value != NULL);
201
202 free_NC_dimarrayV0(ncap);
203
204 free(ncap->value);
205 ncap->value = NULL;
206 ncap->nalloc = 0;
207 }
208
209
210 int
dup_NC_dimarrayV(NC_dimarray * ncap,const NC_dimarray * ref)211 dup_NC_dimarrayV(NC_dimarray *ncap, const NC_dimarray *ref)
212 {
213 int status = NC_NOERR;
214
215 assert(ref != NULL);
216 assert(ncap != NULL);
217
218 if(ref->nelems != 0)
219 {
220 const size_t sz = ref->nelems * sizeof(NC_dim *);
221 ncap->value = (NC_dim **) malloc(sz);
222 if(ncap->value == NULL)
223 return NC_ENOMEM;
224 (void) memset(ncap->value, 0, sz);
225 ncap->nalloc = ref->nelems;
226 }
227
228 ncap->nelems = 0;
229 {
230 NC_dim **dpp = ncap->value;
231 const NC_dim **drpp = (const NC_dim **)ref->value;
232 NC_dim *const *const end = &dpp[ref->nelems];
233 for( /*NADA*/; dpp < end; drpp++, dpp++, ncap->nelems++)
234 {
235 *dpp = dup_NC_dim(*drpp);
236 if(*dpp == NULL)
237 {
238 status = NC_ENOMEM;
239 break;
240 }
241 }
242 }
243
244 if(status != NC_NOERR)
245 {
246 free_NC_dimarrayV(ncap);
247 return status;
248 }
249
250 assert(ncap->nelems == ref->nelems);
251
252 return NC_NOERR;
253 }
254
255
256 /*
257 * Add a new handle on the end of an array of handles
258 * Formerly
259 NC_incr_array(array, tail)
260 */
261 static int
incr_NC_dimarray(NC_dimarray * ncap,NC_dim * newelemp)262 incr_NC_dimarray(NC_dimarray *ncap, NC_dim *newelemp)
263 {
264 NC_dim **vp;
265
266 assert(ncap != NULL);
267
268 if(ncap->nalloc == 0)
269 {
270 assert(ncap->nelems == 0);
271 vp = (NC_dim **) malloc(NC_ARRAY_GROWBY * sizeof(NC_dim *));
272 if(vp == NULL)
273 return NC_ENOMEM;
274 ncap->value = vp;
275 ncap->nalloc = NC_ARRAY_GROWBY;
276 ncap->hashmap = NC_hashmapnew(0);
277 }
278 else if(ncap->nelems +1 > ncap->nalloc)
279 {
280 vp = (NC_dim **) realloc(ncap->value,
281 (ncap->nalloc + NC_ARRAY_GROWBY) * sizeof(NC_dim *));
282 if(vp == NULL)
283 return NC_ENOMEM;
284 ncap->value = vp;
285 ncap->nalloc += NC_ARRAY_GROWBY;
286 }
287
288 if(newelemp != NULL)
289 {
290 uintptr_t intdata = ncap->nelems;
291 NC_hashmapadd(ncap->hashmap, intdata, newelemp->name->cp, strlen(newelemp->name->cp));
292 ncap->value[ncap->nelems] = newelemp;
293 ncap->nelems++;
294 }
295 return NC_NOERR;
296 }
297
298
299 NC_dim *
elem_NC_dimarray(const NC_dimarray * ncap,size_t elem)300 elem_NC_dimarray(const NC_dimarray *ncap, size_t elem)
301 {
302 assert(ncap != NULL);
303 /* cast needed for braindead systems with signed size_t */
304 if(ncap->nelems == 0 || (unsigned long) elem >= ncap->nelems)
305 return NULL;
306
307 assert(ncap->value != NULL);
308
309 return ncap->value[elem];
310 }
311
312
313 /* Public */
314
315 int
NC3_def_dim(int ncid,const char * name,size_t size,int * dimidp)316 NC3_def_dim(int ncid, const char *name, size_t size, int *dimidp)
317 {
318 int status;
319 NC *nc;
320 NC3_INFO* ncp;
321 int dimid;
322 NC_dim *dimp;
323
324 status = NC_check_id(ncid, &nc);
325 if(status != NC_NOERR)
326 return status;
327 ncp = NC3_DATA(nc);
328
329 if(!NC_indef(ncp))
330 return NC_ENOTINDEFINE;
331
332 status = NC_check_name(name);
333 if(status != NC_NOERR)
334 return status;
335
336 if(ncp->flags & NC_64BIT_DATA) {/*CDF-5*/
337 if((sizeof(size_t) > 4) && (size > X_UINT64_MAX - 3)) /* "- 3" handles rounded-up size */
338 return NC_EDIMSIZE;
339 } else if(ncp->flags & NC_64BIT_OFFSET) {/* CDF2 format and LFS */
340 if((sizeof(size_t) > 4) && (size > X_UINT_MAX - 3)) /* "- 3" handles rounded-up size */
341 return NC_EDIMSIZE;
342 } else {/*CDF-1*/
343 if(size > X_INT_MAX - 3)
344 return NC_EDIMSIZE;
345 }
346
347 if(size == NC_UNLIMITED)
348 {
349 dimid = find_NC_Udim(&ncp->dims, &dimp);
350 if(dimid != -1)
351 {
352 assert(dimid != -1);
353 return NC_EUNLIMIT;
354 }
355 }
356
357 dimid = NC_finddim(&ncp->dims, name, &dimp);
358 if(dimid != -1)
359 return NC_ENAMEINUSE;
360
361 dimp = new_NC_dim(name, size);
362 if(dimp == NULL)
363 return NC_ENOMEM;
364 status = incr_NC_dimarray(&ncp->dims, dimp);
365 if(status != NC_NOERR)
366 {
367 free_NC_dim(dimp);
368 return status;
369 }
370
371 if(dimidp != NULL)
372 *dimidp = (int)ncp->dims.nelems -1;
373 return NC_NOERR;
374 }
375
376
377 int
NC3_inq_dimid(int ncid,const char * name,int * dimid_ptr)378 NC3_inq_dimid(int ncid, const char *name, int *dimid_ptr)
379 {
380 int status;
381 NC *nc;
382 NC3_INFO* ncp;
383 int dimid;
384
385 status = NC_check_id(ncid, &nc);
386 if(status != NC_NOERR)
387 return status;
388 ncp = NC3_DATA(nc);
389
390 dimid = NC_finddim(&ncp->dims, name, NULL);
391
392 if(dimid == -1)
393 return NC_EBADDIM;
394
395 if (dimid_ptr)
396 *dimid_ptr = dimid;
397 return NC_NOERR;
398 }
399
400 int
NC3_inq_dim(int ncid,int dimid,char * name,size_t * sizep)401 NC3_inq_dim(int ncid, int dimid, char *name, size_t *sizep)
402 {
403 int status;
404 NC *nc;
405 NC3_INFO* ncp;
406 NC_dim *dimp;
407
408 status = NC_check_id(ncid, &nc);
409 if(status != NC_NOERR)
410 return status;
411 ncp = NC3_DATA(nc);
412
413 dimp = elem_NC_dimarray(&ncp->dims, (size_t)dimid);
414 if(dimp == NULL)
415 return NC_EBADDIM;
416
417 if(name != NULL)
418 {
419 (void)strncpy(name, dimp->name->cp,
420 dimp->name->nchars);
421 name[dimp->name->nchars] = 0;
422 }
423 if(sizep != NULL)
424 {
425 if(dimp->size == NC_UNLIMITED)
426 *sizep = NC_get_numrecs(ncp);
427 else
428 *sizep = dimp->size;
429 }
430 return NC_NOERR;
431 }
432
433 int
NC3_rename_dim(int ncid,int dimid,const char * unewname)434 NC3_rename_dim( int ncid, int dimid, const char *unewname)
435 {
436 int status = NC_NOERR;
437 NC *nc;
438 NC3_INFO* ncp;
439 int existid;
440 NC_dim *dimp;
441 char *newname = NULL; /* normalized */
442 NC_string *old = NULL;
443 uintptr_t intdata;
444
445
446 status = NC_check_id(ncid, &nc);
447 if(status != NC_NOERR)
448 goto done;
449 ncp = NC3_DATA(nc);
450
451 if(NC_readonly(ncp))
452 {status = NC_EPERM; goto done;}
453
454 status = NC_check_name(unewname);
455 if(status != NC_NOERR)
456 goto done;
457
458 existid = NC_finddim(&ncp->dims, unewname, &dimp);
459 if(existid != -1)
460 {status = NC_ENAMEINUSE; goto done;}
461
462 dimp = elem_NC_dimarray(&ncp->dims, (size_t)dimid);
463 if(dimp == NULL)
464 {status = NC_EBADDIM; goto done;}
465
466 old = dimp->name;
467 status = nc_utf8_normalize((const unsigned char *)unewname,(unsigned char **)&newname);
468 if(status != NC_NOERR)
469 goto done;
470 if(NC_indef(ncp))
471 {
472 NC_string *newStr = new_NC_string(strlen(newname), newname);
473 if(newStr == NULL)
474 {status = NC_ENOMEM; goto done;}
475
476 /* Remove old name from hashmap; add new... */
477 NC_hashmapremove(ncp->dims.hashmap, old->cp, strlen(old->cp), NULL);
478 dimp->name = newStr;
479
480 intdata = dimid;
481 NC_hashmapadd(ncp->dims.hashmap, intdata, newStr->cp, strlen(newStr->cp));
482 free_NC_string(old);
483 goto done;
484 }
485
486 /* else, not in define mode */
487
488 /* If new name is longer than old, then complain,
489 but otherwise, no change (test is same as set_NC_string)*/
490 if(dimp->name->nchars < strlen(newname)) {
491 {status = NC_ENOTINDEFINE; goto done;}
492 }
493
494 /* Remove old name from hashmap; add new... */
495 /* WARNING: strlen(NC_string.cp) may be less than NC_string.nchars */
496 NC_hashmapremove(ncp->dims.hashmap, old->cp, strlen(old->cp), NULL);
497
498 /* WARNING: strlen(NC_string.cp) may be less than NC_string.nchars */
499 status = set_NC_string(dimp->name, newname);
500 if(status != NC_NOERR)
501 goto done;
502
503 intdata = (uintptr_t)dimid;
504 NC_hashmapadd(ncp->dims.hashmap, intdata, dimp->name->cp, strlen(dimp->name->cp));
505
506 set_NC_hdirty(ncp);
507
508 if(NC_doHsync(ncp))
509 {
510 status = NC_sync(ncp);
511 if(status != NC_NOERR)
512 goto done;
513 }
514
515 done:
516 if(newname) free(newname);
517 return status;
518 }
519