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