mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-11-27 07:30:33 +08:00
dc1b8b9c4b
Any pointer arithmetic with NULL pointers is technically UB, even if you don't end up dereferencing the pointer.
522 lines
9.9 KiB
C
522 lines
9.9 KiB
C
/*
|
|
* Copyright 2018, University Corporation for Atmospheric Research
|
|
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
|
|
*/
|
|
/* $Id: dim.c,v 1.83 2010/05/25 17:54:15 dmh Exp $ */
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "nc3internal.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include "ncx.h"
|
|
#include "fbits.h"
|
|
#include "ncutf8.h"
|
|
|
|
/*
|
|
* Free dim
|
|
* Formerly
|
|
NC_free_dim(dim)
|
|
*/
|
|
void
|
|
free_NC_dim(NC_dim *dimp)
|
|
{
|
|
if(dimp == NULL)
|
|
return;
|
|
free_NC_string(dimp->name);
|
|
free(dimp);
|
|
}
|
|
|
|
|
|
NC_dim *
|
|
new_x_NC_dim(NC_string *name)
|
|
{
|
|
NC_dim *dimp;
|
|
|
|
dimp = (NC_dim *) malloc(sizeof(NC_dim));
|
|
if(dimp == NULL)
|
|
return NULL;
|
|
|
|
dimp->name = name;
|
|
dimp->size = 0;
|
|
|
|
return(dimp);
|
|
}
|
|
|
|
|
|
/*
|
|
* Formerly
|
|
NC_new_dim(const char *uname, long size)
|
|
*/
|
|
static NC_dim *
|
|
new_NC_dim(const char *uname, size_t size)
|
|
{
|
|
NC_string *strp;
|
|
NC_dim *dimp = NULL;
|
|
int stat = NC_NOERR;
|
|
char* name = NULL;
|
|
|
|
stat = nc_utf8_normalize((const unsigned char *)uname,(unsigned char **)&name);
|
|
if(stat != NC_NOERR)
|
|
goto done;
|
|
strp = new_NC_string(strlen(name), name);
|
|
if(strp == NULL)
|
|
{stat = NC_ENOMEM; goto done;}
|
|
|
|
dimp = new_x_NC_dim(strp);
|
|
if(dimp == NULL)
|
|
{
|
|
free_NC_string(strp);
|
|
goto done;
|
|
}
|
|
|
|
dimp->size = size;
|
|
|
|
done:
|
|
if(name) free(name);
|
|
return (dimp);
|
|
}
|
|
|
|
|
|
static NC_dim *
|
|
dup_NC_dim(const NC_dim *dimp)
|
|
{
|
|
return new_NC_dim(dimp->name->cp, dimp->size);
|
|
}
|
|
|
|
/*
|
|
* Step thru NC_DIMENSION array, seeking the UNLIMITED dimension.
|
|
* Return dimid or -1 on not found.
|
|
* *dimpp is set to the appropriate NC_dim.
|
|
* The loop structure is odd. In order to parallelize,
|
|
* we moved a clearer 'break' inside the loop body to the loop test.
|
|
*/
|
|
int
|
|
find_NC_Udim(const NC_dimarray *ncap, NC_dim **dimpp)
|
|
{
|
|
assert(ncap != NULL);
|
|
|
|
if(ncap->nelems == 0)
|
|
return -1;
|
|
|
|
{
|
|
int dimid = 0;
|
|
NC_dim **loc = ncap->value;
|
|
|
|
for(; (size_t) dimid < ncap->nelems
|
|
&& (*loc)->size != NC_UNLIMITED; dimid++, loc++)
|
|
{
|
|
/*EMPTY*/
|
|
}
|
|
if(dimid >= ncap->nelems)
|
|
return(-1); /* not found */
|
|
/* else, normal return */
|
|
if(dimpp != NULL)
|
|
*dimpp = *loc;
|
|
return dimid;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Step thru NC_DIMENSION array, seeking match on uname.
|
|
* Return dimid or -1 on not found.
|
|
* *dimpp is set to the appropriate NC_dim.
|
|
* The loop structure is odd. In order to parallelize,
|
|
* we moved a clearer 'break' inside the loop body to the loop test.
|
|
*/
|
|
static int
|
|
NC_finddim(const NC_dimarray *ncap, const char *uname, NC_dim **dimpp)
|
|
{
|
|
int dimid = -1;
|
|
char *name = NULL;
|
|
uintptr_t data;
|
|
|
|
assert(ncap != NULL);
|
|
if(ncap->nelems == 0)
|
|
goto done;
|
|
/* normalized version of uname */
|
|
if(nc_utf8_normalize((const unsigned char *)uname,(unsigned char **)&name))
|
|
goto done;
|
|
if(NC_hashmapget(ncap->hashmap, name, strlen(name), &data) == 0)
|
|
goto done;
|
|
dimid = (int)data;
|
|
if(dimpp) *dimpp = ncap->value[dimid];
|
|
|
|
done:
|
|
if(name) free(name);
|
|
return dimid;
|
|
}
|
|
|
|
|
|
/* dimarray */
|
|
|
|
|
|
/*
|
|
* Free the stuff "in" (referred to by) an NC_dimarray.
|
|
* Leaves the array itself allocated.
|
|
*/
|
|
void
|
|
free_NC_dimarrayV0(NC_dimarray *ncap)
|
|
{
|
|
assert(ncap != NULL);
|
|
|
|
if(ncap->nelems == 0)
|
|
return;
|
|
|
|
assert(ncap->value != NULL);
|
|
|
|
{
|
|
NC_dim **dpp = ncap->value;
|
|
NC_dim *const *const end = &dpp[ncap->nelems];
|
|
for( /*NADA*/; dpp < end; dpp++)
|
|
{
|
|
free_NC_dim(*dpp);
|
|
*dpp = NULL;
|
|
}
|
|
}
|
|
ncap->nelems = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Free NC_dimarray values.
|
|
* formerly
|
|
NC_free_array()
|
|
*/
|
|
void
|
|
free_NC_dimarrayV(NC_dimarray *ncap)
|
|
{
|
|
assert(ncap != NULL);
|
|
|
|
if(ncap->nalloc == 0)
|
|
return;
|
|
|
|
NC_hashmapfree(ncap->hashmap);
|
|
ncap->hashmap = NULL;
|
|
|
|
assert(ncap->value != NULL);
|
|
|
|
free_NC_dimarrayV0(ncap);
|
|
|
|
free(ncap->value);
|
|
ncap->value = NULL;
|
|
ncap->nalloc = 0;
|
|
}
|
|
|
|
|
|
int
|
|
dup_NC_dimarrayV(NC_dimarray *ncap, const NC_dimarray *ref)
|
|
{
|
|
int status = NC_NOERR;
|
|
|
|
assert(ref != NULL);
|
|
assert(ncap != NULL);
|
|
|
|
if(ref->nelems != 0)
|
|
{
|
|
const size_t sz = ref->nelems * sizeof(NC_dim *);
|
|
ncap->value = (NC_dim **) malloc(sz);
|
|
if(ncap->value == NULL)
|
|
return NC_ENOMEM;
|
|
(void) memset(ncap->value, 0, sz);
|
|
ncap->nalloc = ref->nelems;
|
|
}
|
|
|
|
ncap->nelems = 0;
|
|
{
|
|
NC_dim **dpp = ncap->value;
|
|
if(dpp != NULL)
|
|
{
|
|
const NC_dim **drpp = (const NC_dim **)ref->value;
|
|
NC_dim *const *const end = &dpp[ref->nelems];
|
|
for( /*NADA*/; dpp < end; drpp++, dpp++, ncap->nelems++)
|
|
{
|
|
*dpp = dup_NC_dim(*drpp);
|
|
if(*dpp == NULL)
|
|
{
|
|
status = NC_ENOMEM;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(status != NC_NOERR)
|
|
{
|
|
free_NC_dimarrayV(ncap);
|
|
return status;
|
|
}
|
|
|
|
assert(ncap->nelems == ref->nelems);
|
|
|
|
return NC_NOERR;
|
|
}
|
|
|
|
|
|
/*
|
|
* Add a new handle on the end of an array of handles
|
|
* Formerly
|
|
NC_incr_array(array, tail)
|
|
*/
|
|
static int
|
|
incr_NC_dimarray(NC_dimarray *ncap, NC_dim *newelemp)
|
|
{
|
|
NC_dim **vp;
|
|
|
|
assert(ncap != NULL);
|
|
|
|
if(ncap->nalloc == 0)
|
|
{
|
|
assert(ncap->nelems == 0);
|
|
vp = (NC_dim **) malloc(NC_ARRAY_GROWBY * sizeof(NC_dim *));
|
|
if(vp == NULL)
|
|
return NC_ENOMEM;
|
|
ncap->value = vp;
|
|
ncap->nalloc = NC_ARRAY_GROWBY;
|
|
ncap->hashmap = NC_hashmapnew(0);
|
|
}
|
|
else if(ncap->nelems +1 > ncap->nalloc)
|
|
{
|
|
vp = (NC_dim **) realloc(ncap->value,
|
|
(ncap->nalloc + NC_ARRAY_GROWBY) * sizeof(NC_dim *));
|
|
if(vp == NULL)
|
|
return NC_ENOMEM;
|
|
ncap->value = vp;
|
|
ncap->nalloc += NC_ARRAY_GROWBY;
|
|
}
|
|
|
|
if(newelemp != NULL)
|
|
{
|
|
uintptr_t intdata = ncap->nelems;
|
|
NC_hashmapadd(ncap->hashmap, intdata, newelemp->name->cp, strlen(newelemp->name->cp));
|
|
ncap->value[ncap->nelems] = newelemp;
|
|
ncap->nelems++;
|
|
}
|
|
return NC_NOERR;
|
|
}
|
|
|
|
|
|
NC_dim *
|
|
elem_NC_dimarray(const NC_dimarray *ncap, size_t elem)
|
|
{
|
|
assert(ncap != NULL);
|
|
/* cast needed for braindead systems with signed size_t */
|
|
if(ncap->nelems == 0 || (unsigned long) elem >= ncap->nelems)
|
|
return NULL;
|
|
|
|
assert(ncap->value != NULL);
|
|
|
|
return ncap->value[elem];
|
|
}
|
|
|
|
|
|
/* Public */
|
|
|
|
int
|
|
NC3_def_dim(int ncid, const char *name, size_t size, int *dimidp)
|
|
{
|
|
int status;
|
|
NC *nc;
|
|
NC3_INFO* ncp;
|
|
int dimid;
|
|
NC_dim *dimp;
|
|
|
|
status = NC_check_id(ncid, &nc);
|
|
if(status != NC_NOERR)
|
|
return status;
|
|
ncp = NC3_DATA(nc);
|
|
|
|
if(!NC_indef(ncp))
|
|
return NC_ENOTINDEFINE;
|
|
|
|
status = NC_check_name(name);
|
|
if(status != NC_NOERR)
|
|
return status;
|
|
|
|
if(ncp->flags & NC_64BIT_DATA) {/*CDF-5*/
|
|
if((sizeof(size_t) > 4) && (size > X_UINT64_MAX - 3)) /* "- 3" handles rounded-up size */
|
|
return NC_EDIMSIZE;
|
|
} else if(ncp->flags & NC_64BIT_OFFSET) {/* CDF2 format and LFS */
|
|
if((sizeof(size_t) > 4) && (size > X_UINT_MAX - 3)) /* "- 3" handles rounded-up size */
|
|
return NC_EDIMSIZE;
|
|
} else {/*CDF-1*/
|
|
if(size > X_INT_MAX - 3)
|
|
return NC_EDIMSIZE;
|
|
}
|
|
|
|
if(size == NC_UNLIMITED)
|
|
{
|
|
dimid = find_NC_Udim(&ncp->dims, &dimp);
|
|
if(dimid != -1)
|
|
{
|
|
assert(dimid != -1);
|
|
return NC_EUNLIMIT;
|
|
}
|
|
}
|
|
|
|
dimid = NC_finddim(&ncp->dims, name, &dimp);
|
|
if(dimid != -1)
|
|
return NC_ENAMEINUSE;
|
|
|
|
dimp = new_NC_dim(name, size);
|
|
if(dimp == NULL)
|
|
return NC_ENOMEM;
|
|
status = incr_NC_dimarray(&ncp->dims, dimp);
|
|
if(status != NC_NOERR)
|
|
{
|
|
free_NC_dim(dimp);
|
|
return status;
|
|
}
|
|
|
|
if(dimidp != NULL)
|
|
*dimidp = (int)ncp->dims.nelems -1;
|
|
return NC_NOERR;
|
|
}
|
|
|
|
|
|
int
|
|
NC3_inq_dimid(int ncid, const char *name, int *dimid_ptr)
|
|
{
|
|
int status;
|
|
NC *nc;
|
|
NC3_INFO* ncp;
|
|
int dimid;
|
|
|
|
status = NC_check_id(ncid, &nc);
|
|
if(status != NC_NOERR)
|
|
return status;
|
|
ncp = NC3_DATA(nc);
|
|
|
|
dimid = NC_finddim(&ncp->dims, name, NULL);
|
|
|
|
if(dimid == -1)
|
|
return NC_EBADDIM;
|
|
|
|
if (dimid_ptr)
|
|
*dimid_ptr = dimid;
|
|
return NC_NOERR;
|
|
}
|
|
|
|
int
|
|
NC3_inq_dim(int ncid, int dimid, char *name, size_t *sizep)
|
|
{
|
|
int status;
|
|
NC *nc;
|
|
NC3_INFO* ncp;
|
|
NC_dim *dimp;
|
|
|
|
status = NC_check_id(ncid, &nc);
|
|
if(status != NC_NOERR)
|
|
return status;
|
|
ncp = NC3_DATA(nc);
|
|
|
|
dimp = elem_NC_dimarray(&ncp->dims, (size_t)dimid);
|
|
if(dimp == NULL)
|
|
return NC_EBADDIM;
|
|
|
|
if(name != NULL)
|
|
{
|
|
(void)strncpy(name, dimp->name->cp,
|
|
dimp->name->nchars);
|
|
name[dimp->name->nchars] = 0;
|
|
}
|
|
if(sizep != NULL)
|
|
{
|
|
if(dimp->size == NC_UNLIMITED)
|
|
*sizep = NC_get_numrecs(ncp);
|
|
else
|
|
*sizep = dimp->size;
|
|
}
|
|
return NC_NOERR;
|
|
}
|
|
|
|
int
|
|
NC3_rename_dim( int ncid, int dimid, const char *unewname)
|
|
{
|
|
int status = NC_NOERR;
|
|
NC *nc;
|
|
NC3_INFO* ncp;
|
|
int existid;
|
|
NC_dim *dimp;
|
|
char *newname = NULL; /* normalized */
|
|
NC_string *old = NULL;
|
|
uintptr_t intdata;
|
|
|
|
|
|
status = NC_check_id(ncid, &nc);
|
|
if(status != NC_NOERR)
|
|
goto done;
|
|
ncp = NC3_DATA(nc);
|
|
|
|
if(NC_readonly(ncp))
|
|
{status = NC_EPERM; goto done;}
|
|
|
|
status = NC_check_name(unewname);
|
|
if(status != NC_NOERR)
|
|
goto done;
|
|
|
|
existid = NC_finddim(&ncp->dims, unewname, &dimp);
|
|
if(existid != -1)
|
|
{status = NC_ENAMEINUSE; goto done;}
|
|
|
|
dimp = elem_NC_dimarray(&ncp->dims, (size_t)dimid);
|
|
if(dimp == NULL)
|
|
{status = NC_EBADDIM; goto done;}
|
|
|
|
old = dimp->name;
|
|
status = nc_utf8_normalize((const unsigned char *)unewname,(unsigned char **)&newname);
|
|
if(status != NC_NOERR)
|
|
goto done;
|
|
if(NC_indef(ncp))
|
|
{
|
|
NC_string *newStr = new_NC_string(strlen(newname), newname);
|
|
if(newStr == NULL)
|
|
{status = NC_ENOMEM; goto done;}
|
|
|
|
/* Remove old name from hashmap; add new... */
|
|
NC_hashmapremove(ncp->dims.hashmap, old->cp, strlen(old->cp), NULL);
|
|
dimp->name = newStr;
|
|
|
|
intdata = dimid;
|
|
NC_hashmapadd(ncp->dims.hashmap, intdata, newStr->cp, strlen(newStr->cp));
|
|
free_NC_string(old);
|
|
goto done;
|
|
}
|
|
|
|
/* else, not in define mode */
|
|
|
|
/* If new name is longer than old, then complain,
|
|
but otherwise, no change (test is same as set_NC_string)*/
|
|
if(dimp->name->nchars < strlen(newname)) {
|
|
{status = NC_ENOTINDEFINE; goto done;}
|
|
}
|
|
|
|
/* Remove old name from hashmap; add new... */
|
|
/* WARNING: strlen(NC_string.cp) may be less than NC_string.nchars */
|
|
NC_hashmapremove(ncp->dims.hashmap, old->cp, strlen(old->cp), NULL);
|
|
|
|
/* WARNING: strlen(NC_string.cp) may be less than NC_string.nchars */
|
|
status = set_NC_string(dimp->name, newname);
|
|
if(status != NC_NOERR)
|
|
goto done;
|
|
|
|
intdata = (uintptr_t)dimid;
|
|
NC_hashmapadd(ncp->dims.hashmap, intdata, dimp->name->cp, strlen(dimp->name->cp));
|
|
|
|
set_NC_hdirty(ncp);
|
|
|
|
if(NC_doHsync(ncp))
|
|
{
|
|
status = NC_sync(ncp);
|
|
if(status != NC_NOERR)
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
if(newname) free(newname);
|
|
return status;
|
|
}
|