mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-12-27 08:49:16 +08:00
2afbdbd18f
The XArray implementation that uses Zarr for storage provides a mechanism to simulate named dimensions. It does this by adding a per-variable attribute called _ARRAY_DIMENSIONS. This attribute contains a list of names to be matched against the shape values of the variable. In effect a named dimension is created with the name _ARRAY_DIMENSIONS(i) and length shape(i) for all i in range 0..rank(variable). Both read and write support is provided. This XArray support is only invoked if the mode value of "xarray" is defined. So for example, as in this URL. ```` https://s3.us-west-1.amazonaws.com/bucket/dataset#mode=nczarr,xarray,s3 ```` Note that the "xarray" mode flag also implies mode flag "zarr", so the above is equivalent to this URL. ```` https://s3.us-west-1.amazonaws.com/bucket/dataset#mode=nczarr,zarr,xarray,s3 ```` The primary change to implement this was to unify the handling of dimension references in libnczarr/zsync. A test for this and other pure-zarr features was added as nczarr_test/run_purezarr.sh Other changes: * Make sure distcheck leaves no files around. * Change the special attribute flag DIMSCALEFLAG to HIDDENATTRFLAG to support the xarray attribute. * Annotate the zmap implementations with feature flags such as WRITEONCE (for zip files).
390 lines
8.8 KiB
C
390 lines
8.8 KiB
C
/*
|
|
* Copyright 2018, University Corporation for Atmospheric Research
|
|
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
|
|
*/
|
|
|
|
#include "ut_includes.h"
|
|
|
|
#undef DEBUG
|
|
|
|
#if 0
|
|
#define FILE1 "testmapnc4.ncz"
|
|
#define URL1 "file://" FILE1 "#mode=zarr"
|
|
#endif
|
|
|
|
#define META1 "/meta1"
|
|
#define META2 "/meta2"
|
|
#define DATA1 "/data1"
|
|
#define DATA1LEN 25
|
|
|
|
static const char* metadata1 = "{\n\"foo\": 42,\n\"bar\": \"apples\",\n\"baz\": [1, 2, 3, 4]}";
|
|
|
|
static const char* metadata2 = "{\n\"foo\": 42,\n\"bar\": \"apples\",\n\"baz\": [1, 2, 3, 4],\n\"extra\": 137}";
|
|
|
|
static char* url = NULL;
|
|
static NCZM_IMPL impl = NCZM_UNDEF;
|
|
|
|
/* Forward */
|
|
static int simplecreate(void);
|
|
static int simpledelete(void);
|
|
static int writemeta(void);
|
|
static int writemeta2(void);
|
|
static int readmeta(void);
|
|
static int readmeta2(void);
|
|
static int writedata(void);
|
|
static int readdata(void);
|
|
static int search(void);
|
|
|
|
struct Test tests[] = {
|
|
{"create",simplecreate},
|
|
{"delete",simpledelete},
|
|
{"writemeta", writemeta},
|
|
{"writemeta2", writemeta2},
|
|
{"readmeta", readmeta},
|
|
{"readmeta2", readmeta2},
|
|
{"writedata", writedata},
|
|
{"readdata", readdata},
|
|
{"search", search},
|
|
{NULL,NULL}
|
|
};
|
|
|
|
int
|
|
main(int argc, char** argv)
|
|
{
|
|
int stat = NC_NOERR;
|
|
|
|
if((stat = ut_init(argc, argv, &utoptions))) goto done;
|
|
if(utoptions.file == NULL && utoptions.output != NULL) utoptions.file = strdup(utoptions.output);
|
|
if(utoptions.output == NULL && utoptions.file != NULL)utoptions.output = strdup(utoptions.file);
|
|
impl = kind2impl(utoptions.kind);
|
|
url = makeurl(utoptions.file,impl);
|
|
|
|
if((stat = runtests((const char**)utoptions.cmds,tests))) goto done;
|
|
|
|
done:
|
|
if(stat) usage(stat);
|
|
return 0;
|
|
}
|
|
|
|
/* Do a simple create */
|
|
static int
|
|
simplecreate(void)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NCZMAP* map = NULL;
|
|
char* path = NULL;
|
|
|
|
if((stat = nczmap_create(impl,url,0,0,NULL,&map)))
|
|
goto done;
|
|
|
|
if((stat=nczm_concat(NULL,NCZMETAROOT,&path)))
|
|
goto done;
|
|
|
|
/* Write empty metadata content */
|
|
if((stat = nczmap_write(map, path, 0, 0, (const void*)"")))
|
|
goto done;
|
|
|
|
done:
|
|
/* Do not delete so we can look at it with ncdump */
|
|
stat = nczmap_close(map,0);
|
|
nullfree(path);
|
|
return THROW(stat);
|
|
}
|
|
|
|
/* Do a simple delete of previously created file */
|
|
static int
|
|
simpledelete(void)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NCZMAP* map = NULL;
|
|
|
|
if((stat = nczmap_open(impl,url,0,0,NULL,&map)))
|
|
goto done;
|
|
if((stat = nczmap_close(map,1)))
|
|
goto done;
|
|
|
|
done:
|
|
return THROW(stat);
|
|
}
|
|
|
|
static int
|
|
writemeta(void)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NCZMAP* map = NULL;
|
|
char* path = NULL;
|
|
|
|
if((stat = nczmap_open(impl,url,NC_WRITE,0,NULL,&map)))
|
|
goto done;
|
|
|
|
if((stat=nczm_concat(META1,ZARRAY,&path)))
|
|
goto done;
|
|
if((stat = nczmap_write(map, path, 0, strlen(metadata1), metadata1)))
|
|
goto done;
|
|
free(path); path = NULL;
|
|
|
|
done:
|
|
/* Do not delete so we can look at it with ncdump */
|
|
(void)nczmap_close(map,0);
|
|
nullfree(path);
|
|
return THROW(stat);
|
|
}
|
|
|
|
static int
|
|
writemeta2(void)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NCZMAP* map = NULL;
|
|
char* path = NULL;
|
|
|
|
if((stat = nczmap_open(impl,url,NC_WRITE,0,NULL,&map)))
|
|
goto done;
|
|
|
|
if((stat=nczm_concat(META2,NCZARRAY,&path)))
|
|
goto done;
|
|
if((stat = nczmap_write(map, path, 0, strlen(metadata2), metadata2)))
|
|
goto done;
|
|
|
|
done:
|
|
/* Do not delete so we can look at it with ncdump */
|
|
(void)nczmap_close(map,0);
|
|
nullfree(path);
|
|
return THROW(stat);
|
|
}
|
|
|
|
static int
|
|
readkey(NCZMAP* map, const char* prefix, const char* object)
|
|
{
|
|
int stat = NC_NOERR;
|
|
char* path = NULL;
|
|
size64_t olen;
|
|
char* content = NULL;
|
|
|
|
if((stat=nczm_concat(prefix,object,&path)))
|
|
goto done;
|
|
|
|
/* Get length */
|
|
if((stat = nczmap_len(map, path, &olen)))
|
|
goto done;
|
|
|
|
/* Allocate the space for reading the metadata (might be overkill) */
|
|
if((content = malloc(olen+1)) == NULL)
|
|
{stat = NC_ENOMEM; goto done;}
|
|
|
|
if((stat = nczmap_read(map, path, 0, olen, content)))
|
|
goto done;
|
|
|
|
/* nul terminate */
|
|
content[olen] = '\0';
|
|
|
|
printf("%s: |%s|\n",path,content);
|
|
|
|
done:
|
|
nullfree(content);
|
|
nullfree(path);
|
|
return THROW(stat);
|
|
}
|
|
|
|
static int
|
|
readmeta(void)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NCZMAP* map = NULL;
|
|
|
|
if((stat = nczmap_open(impl,url,0,0,NULL,&map)))
|
|
goto done;
|
|
|
|
if((stat = readkey(map,META1,ZARRAY))) goto done;
|
|
|
|
done:
|
|
(void)nczmap_close(map,0);
|
|
return THROW(stat);
|
|
}
|
|
|
|
static int
|
|
readmeta2(void)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NCZMAP* map = NULL;
|
|
|
|
if((stat = nczmap_open(impl,url,0,0,NULL,&map)))
|
|
goto done;
|
|
|
|
if((stat = readkey(map,META2,NCZARRAY)))
|
|
goto done;
|
|
|
|
done:
|
|
(void)nczmap_close(map,0);
|
|
return THROW(stat);
|
|
}
|
|
|
|
static int
|
|
writedata(void)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NCZMAP* map = NULL;
|
|
char* path = NULL;
|
|
int data1[DATA1LEN];
|
|
int i;
|
|
size64_t totallen;
|
|
char* data1p = (char*)&data1[0]; /* byte level version of data1 */
|
|
NCZM_FEATURES features;
|
|
|
|
/* Create the data */
|
|
for(i=0;i<DATA1LEN;i++) data1[i] = i;
|
|
totallen = sizeof(int)*DATA1LEN;
|
|
|
|
if((stat = nczmap_open(impl,url,NC_WRITE,0,NULL,&map)))
|
|
goto done;
|
|
|
|
/* ensure object */
|
|
if((stat=nczm_concat(DATA1,"0",&path)))
|
|
goto done;
|
|
|
|
features = nczmap_features(impl);
|
|
if((NCZM_ZEROSTART & features) || (NCZM_WRITEONCE & features)) {
|
|
if((stat = nczmap_write(map, path, 0, totallen, data1p)))
|
|
goto done;
|
|
} else {
|
|
/* Write in 3 slices */
|
|
for(i=0;i<3;i++) {
|
|
size64_t start, count, third, last;
|
|
third = (totallen+2) / 3; /* round up */
|
|
start = i * third;
|
|
last = start + third;
|
|
if(last > totallen)
|
|
last = totallen;
|
|
count = last - start;
|
|
if((stat = nczmap_write(map, path, start, count, &data1p[start])))
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
/* Do not delete so we can look at it with ncdump */
|
|
(void)nczmap_close(map,0);
|
|
nullfree(path);
|
|
return THROW(stat);
|
|
}
|
|
|
|
static int
|
|
readdata(void)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NCZMAP* map = NULL;
|
|
char* path = NULL;
|
|
int data1[DATA1LEN];
|
|
int i;
|
|
size64_t chunklen, totallen;
|
|
char* data1p = NULL; /* byte level pointer into data1 */
|
|
|
|
if((stat = nczmap_open(impl,url,0,0,NULL,&map)))
|
|
goto done;
|
|
|
|
/* ensure object */
|
|
if((stat=nczm_concat(DATA1,"0",&path)))
|
|
goto done;
|
|
|
|
if((stat = nczmap_exists(map,path)))
|
|
goto done;
|
|
|
|
/* Read chunks in size sizeof(int)*n, where is rndup(DATA1LEN/3) */
|
|
chunklen = sizeof(int) * ((DATA1LEN+2)/3);
|
|
data1p = (char*)&data1[0];
|
|
totallen = sizeof(int)*DATA1LEN;
|
|
|
|
/* Read in 3 chunks */
|
|
memset(data1,0,sizeof(data1));
|
|
for(i=0;i<3;i++) {
|
|
size64_t start, count, last;
|
|
start = i * chunklen;
|
|
last = start + chunklen;
|
|
if(last > totallen)
|
|
last = totallen;
|
|
count = last - start;
|
|
if((stat = nczmap_read(map, path, start, count, &data1p[start])))
|
|
goto done;
|
|
}
|
|
|
|
/* Validate */
|
|
for(i=0;i<DATA1LEN;i++) {
|
|
if(data1[i] != i) {
|
|
fprintf(stderr,"data mismatch: is: %d should be: %d\n",data1[i],i);
|
|
stat = NC_EINVAL;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
/* Do not delete so we can look at it with ncdump */
|
|
(void)nczmap_close(map,0);
|
|
nullfree(path);
|
|
return THROW(stat);
|
|
}
|
|
|
|
static int
|
|
searchR(NCZMAP* map, int depth, const char* prefix0, NClist* objects)
|
|
{
|
|
int i,stat = NC_NOERR;
|
|
NClist* matches = nclistnew();
|
|
char prefix[4096]; /* only ok because we know testdata */
|
|
size_t prefixlen;
|
|
|
|
nclistpush(objects,strdup(prefix0));
|
|
|
|
prefix[0] = '\0';
|
|
strlcat(prefix,prefix0,sizeof(prefix));
|
|
prefixlen = strlen(prefix);
|
|
|
|
/* get next level object keys **below** the prefix: should have form: <name> */
|
|
switch (stat = nczmap_search(map, prefix, matches)) {
|
|
case NC_NOERR: break;
|
|
case NC_ENOTFOUND: stat = NC_NOERR; break;/* prefix is not a dir */
|
|
default: goto done;
|
|
}
|
|
/* recurse */
|
|
for(i=0;i<nclistlength(matches);i++) {
|
|
const char* key = nclistget(matches,i);
|
|
/* ensure trailing '/' */
|
|
if(prefix[prefixlen-1] != '/')
|
|
strlcat(prefix,"/",sizeof(prefix));
|
|
strlcat(prefix,key,sizeof(prefix));
|
|
if((stat = searchR(map,depth+1,prefix,objects))) goto done;
|
|
/* restore prefix */
|
|
prefix[prefixlen] = '\0';
|
|
if(stat != NC_NOERR)
|
|
goto done;
|
|
}
|
|
done:
|
|
nclistfreeall(matches);
|
|
return THROW(stat);
|
|
}
|
|
|
|
static int
|
|
search(void)
|
|
{
|
|
int i,stat = NC_NOERR;
|
|
NCZMAP* map = NULL;
|
|
NClist* objects = nclistnew();
|
|
|
|
if((stat = nczmap_open(impl,url,0,0,NULL,&map)))
|
|
goto done;
|
|
|
|
/* Do a recursive search on root to get all object keys */
|
|
if((stat=searchR(map,0,"/",objects)))
|
|
goto done;
|
|
/* Sort */
|
|
ut_sortlist(objects);
|
|
/* Print out the list */
|
|
for(i=0;i<nclistlength(objects);i++) {
|
|
const char* key = nclistget(objects,i);
|
|
printf("[%d] %s\n",i,key);
|
|
}
|
|
|
|
done:
|
|
/* Do not delete so later tests can use it */
|
|
(void)nczmap_close(map,0);
|
|
nclistfreeall(objects);
|
|
return THROW(stat);
|
|
}
|