mirror of
https://github.com/Unidata/netcdf-c.git
synced 2025-01-18 15:55:12 +08:00
Merge group iterator source into existing variable data iterator files nciter.h and nciter.c. Adapt nccopy.c accordingly.
This commit is contained in:
parent
4b3ea48bbd
commit
7a6f7c6a94
@ -58,7 +58,7 @@ ncdump.h vardata.h dumplib.h indent.h isnan.h nctime.h cdl.h
|
||||
# Another utility program that copies any netCDF file using only the
|
||||
# netCDF API
|
||||
bin_PROGRAMS += nccopy
|
||||
nccopy_SOURCES = nccopy.c nciter.c nciter.h ncgiter.c ncgiter.h
|
||||
nccopy_SOURCES = nccopy.c nciter.c nciter.h
|
||||
|
||||
# This is the man page.
|
||||
man_MANS = ncdump.1 nccopy.1
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <string.h>
|
||||
#include <netcdf.h>
|
||||
#include "nciter.h"
|
||||
#include "ncgiter.h"
|
||||
|
||||
/* default bytes of memory we are willing to allocate for variable
|
||||
* values during copy */
|
||||
|
266
ncdump/ncgiter.c
266
ncdump/ncgiter.c
@ -1,266 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Copyright 2010, University Corporation for Atmospheric Research
|
||||
* See netcdf/README file for copying and redistribution conditions.
|
||||
* "$Id"
|
||||
*********************************************************************/
|
||||
|
||||
#include "config.h" /* for USE_NETCDF4 macro */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <netcdf.h>
|
||||
#include "ncgiter.h"
|
||||
|
||||
#define CHECK(stat,f) if(stat != NC_NOERR) {check(stat,#f,__FILE__,__LINE__);} else {}
|
||||
|
||||
static void
|
||||
check(int err, const char* fcn, const char* file, const int line)
|
||||
{
|
||||
fprintf(stderr,"%s\n",nc_strerror(err));
|
||||
fprintf(stderr,"Location: function %s; file %s; line %d\n",
|
||||
fcn,file,line);
|
||||
fflush(stderr); fflush(stdout);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Check error return from malloc, and allow malloc(0) with subsequent free */
|
||||
static void *
|
||||
emalloc (size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = (void *) malloc (size==0 ? 1 : size); /* don't malloc(0) */
|
||||
if (p == 0) {
|
||||
fprintf(stderr,"Out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* initialize and return a new empty stack */
|
||||
static ncgiter_t *
|
||||
gs_init() {
|
||||
ncgiter_t *s = emalloc(sizeof(ncgiter_t));
|
||||
s->ngrps = 0;
|
||||
s->top = NULL;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* free a stack and all its nodes */
|
||||
static void
|
||||
gs_free(ncgiter_t *s) {
|
||||
grpnode_t *n0, *n1;
|
||||
n0 = s->top;
|
||||
while (n0) {
|
||||
n1 = n0->next;
|
||||
free(n0);
|
||||
n0 = n1;
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
|
||||
/* test if a stack is empty */
|
||||
static int
|
||||
gs_empty(ncgiter_t *s)
|
||||
{
|
||||
return s->ngrps == 0;
|
||||
}
|
||||
|
||||
/* push a grpid on stack */
|
||||
static void
|
||||
gs_push(ncgiter_t *s, int grpid)
|
||||
{
|
||||
grpnode_t *node = emalloc(sizeof(grpnode_t));
|
||||
|
||||
node->grpid = grpid;
|
||||
node->next = gs_empty(s) ? NULL : s->top;
|
||||
s->top = node;
|
||||
s->ngrps++;
|
||||
}
|
||||
|
||||
/* pop value off stack and return */
|
||||
static int
|
||||
gs_pop(ncgiter_t *s)
|
||||
{
|
||||
if (gs_empty(s)) {
|
||||
return -1; /* underflow, stack is empty */
|
||||
} else { /* pop a node */
|
||||
grpnode_t *top = s->top;
|
||||
int value = top->grpid;
|
||||
s->top = top->next;
|
||||
/* TODO: first call to free gets seg fault with libumem */
|
||||
free(top);
|
||||
s->ngrps--;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/* return top value on stack without popping stack */
|
||||
static int
|
||||
gs_top(ncgiter_t *s)
|
||||
{
|
||||
if (gs_empty(s)) {
|
||||
return -1; /* underflow, stack is empty */
|
||||
} else { /* get top value */
|
||||
grpnode_t *top = s->top;
|
||||
int value = top->grpid;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Begin public interface */
|
||||
|
||||
/* Initialize group iterator for start group and all its descendant
|
||||
* groups. */
|
||||
int
|
||||
nc_get_giter(int grpid, /* start group id */
|
||||
ncgiter_t **iterp /* returned opaque iteration state */
|
||||
)
|
||||
{
|
||||
int stat = NC_NOERR;
|
||||
|
||||
stat = nc_inq_grpname(grpid, NULL); /* check if grpid is valid */
|
||||
if(stat != NC_EBADGRPID && stat != NC_EBADID) {
|
||||
*iterp = gs_init();
|
||||
gs_push(*iterp, grpid);
|
||||
}
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get group id of next group. On first call gets start group id,
|
||||
* subsequently returns other subgroup ids in preorder. Returns zero
|
||||
* when no more groups left.
|
||||
*/
|
||||
int
|
||||
nc_next_giter(ncgiter_t *iterp, int *grpidp) {
|
||||
int stat = NC_NOERR;
|
||||
int numgrps;
|
||||
int *grpids;
|
||||
int i;
|
||||
|
||||
if(gs_empty(iterp)) {
|
||||
*grpidp = 0; /* not a group, signals iterator is done */
|
||||
} else {
|
||||
*grpidp = gs_pop(iterp);
|
||||
stat = nc_inq_grps(*grpidp, &numgrps, NULL);
|
||||
CHECK(stat, nc_inq_grps);
|
||||
if(numgrps > 0) {
|
||||
grpids = (int *)emalloc(sizeof(int) * numgrps);
|
||||
stat = nc_inq_grps(*grpidp, &numgrps, grpids);
|
||||
CHECK(stat, nc_inq_grps);
|
||||
for(i = numgrps - 1; i >= 0; i--) { /* push ids on stack in reverse order */
|
||||
gs_push(iterp, grpids[i]);
|
||||
}
|
||||
free(grpids);
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release group iter.
|
||||
*/
|
||||
void
|
||||
nc_rel_giter(ncgiter_t *iterp)
|
||||
{
|
||||
gs_free(iterp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get total number of groups (including the top-level group and all
|
||||
* descendant groups, recursively) and all descendant subgroup ids
|
||||
* (including the input rootid of the start group) for a group and
|
||||
* all its descendants, in preorder.
|
||||
*
|
||||
* If grpids or numgrps is NULL, it will be ignored. So typical use
|
||||
* is to call with grpids NULL to get numgrps, allocate enough space
|
||||
* for the group ids, then call again to get them.
|
||||
*/
|
||||
int
|
||||
nc_inq_grps_full(int rootid, int *numgrps, int *grpids)
|
||||
{
|
||||
int stat = NC_NOERR;
|
||||
ncgiter_t *giter; /* pointer to group iterator */
|
||||
int grpid;
|
||||
size_t count;
|
||||
|
||||
stat = nc_get_giter(rootid, &giter);
|
||||
CHECK(stat, nc_get_giter);
|
||||
|
||||
count = 0;
|
||||
stat = nc_next_giter(giter, &grpid);
|
||||
CHECK(stat, nc_next_giter);
|
||||
while(grpid != 0) {
|
||||
if(grpids)
|
||||
grpids[count] = grpid;
|
||||
count++;
|
||||
stat = nc_next_giter(giter, &grpid);
|
||||
CHECK(stat, nc_next_iter);
|
||||
}
|
||||
if(numgrps)
|
||||
*numgrps = count;
|
||||
nc_rel_giter(giter);
|
||||
return stat;
|
||||
}
|
||||
|
||||
#ifdef TEST_GITER_MAIN
|
||||
/* Test on input file by printing all group names in iteration order */
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int stat = NC_NOERR;
|
||||
char* infile = NULL;
|
||||
int ncid;
|
||||
ncgiter_t *giter; /* pointer to group iterator */
|
||||
int grpid;
|
||||
size_t namelen;
|
||||
char grpname[NC_MAX_NAME];
|
||||
|
||||
infile = argv[1];
|
||||
|
||||
stat = nc_open(infile, NC_NOWRITE, &ncid);
|
||||
CHECK(stat, nc_open);
|
||||
|
||||
stat = nc_get_giter(ncid, &giter);
|
||||
CHECK(stat, nc_get_giter);
|
||||
|
||||
stat = nc_next_giter(giter, &grpid);
|
||||
CHECK(stat, nc_next_giter);
|
||||
while(grpid != 0) {
|
||||
/* get group name from group id */
|
||||
stat = nc_inq_grpname(grpid, grpname);
|
||||
CHECK(stat, nc_get_iter);
|
||||
printf("%d %s\n", grpid, grpname);
|
||||
stat = nc_next_giter(giter, &grpid);
|
||||
CHECK(stat, nc_next_iter);
|
||||
}
|
||||
|
||||
nc_rel_giter(giter);
|
||||
|
||||
/* Now try the simpler API */
|
||||
{
|
||||
size_t ngrps0, ngrps;
|
||||
int *grpids;
|
||||
int i;
|
||||
|
||||
printf("Same thing with simpler API\n");
|
||||
stat = nc_inq_grps_full(ncid, &ngrps0, NULL);
|
||||
CHECK(stat, nc_inq_grps_full);
|
||||
grpids = emalloc(ngrps0 * sizeof(int));
|
||||
stat = nc_inq_grps_full(ncid, &ngrps, grpids);
|
||||
CHECK(stat, nc_inq_grps_full);
|
||||
assert(ngrps0 == ngrps);
|
||||
for(i = 0; i < ngrps; i++) {
|
||||
/* get group name from group id */
|
||||
stat = nc_inq_grpname(grpids[i], grpname);
|
||||
CHECK(stat, nc_inq_grpname);
|
||||
printf("%d %s\n", grpids[i], grpname);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_GITER_MAIN */
|
@ -1,68 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Copyright 2010, University Corporation for Atmospheric Research
|
||||
* See netcdf/README file for copying and redistribution conditions.
|
||||
* "$Id: ncgiter.h,v 1.4 2010/02/01 21:44:04 russ Exp $"
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef _NCGITER_
|
||||
#define _NCGITER_
|
||||
|
||||
#include <netcdf.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* node in stack of group ids */
|
||||
typedef struct grpnode {
|
||||
int grpid;
|
||||
struct grpnode *next;
|
||||
} grpnode_t;
|
||||
|
||||
/*
|
||||
* The opaque structure to hold state of iteration over groups.
|
||||
* (Just implemented as a stack of group ids.)
|
||||
*/
|
||||
typedef struct {
|
||||
int ngrps; /* number of groups left to visit */
|
||||
grpnode_t *top; /* group ids left to visit */
|
||||
} ncgiter_t;
|
||||
|
||||
/*
|
||||
* The Interface
|
||||
*/
|
||||
|
||||
/*
|
||||
* Simplest interface: get total number of groups (including all
|
||||
* descendant groups, recursively) and all descendant subgroup ids for
|
||||
* start group and all its descendants, in preorder.
|
||||
*/
|
||||
extern int
|
||||
nc_inq_grps_full(int ncid, int *numgrps, int *ncids);
|
||||
|
||||
/*
|
||||
* More complex iterator interface: get group iterator for start group
|
||||
* ncid and all its descendant groups.
|
||||
*/
|
||||
extern int
|
||||
nc_get_giter(int ncid, ncgiter_t **iterp);
|
||||
|
||||
/*
|
||||
* Get group id of next group. On first call returns start group,
|
||||
* subsequently returns other subgroup ids in preorder. Returns grpid
|
||||
* of 0 (never an actual group number) when no more groups.
|
||||
*/
|
||||
extern int
|
||||
nc_next_giter(ncgiter_t *iterp, int *grpid);
|
||||
|
||||
/*
|
||||
* Release memory allocated for group iterator.
|
||||
*/
|
||||
void
|
||||
nc_rel_giter(ncgiter_t *iterp);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _NCGITER_ */
|
476
ncdump/nciter.c
476
ncdump/nciter.c
@ -12,15 +12,6 @@
|
||||
|
||||
#define CHECK(stat,f) if(stat != NC_NOERR) {check(stat,#f,__FILE__,__LINE__);} else {}
|
||||
|
||||
/* forward declarations */
|
||||
static int nc_blkio_init(size_t bufsize, size_t value_size, int rank,
|
||||
int chunked, nciter_t *iter);
|
||||
static int up_start(int ndims, const size_t *dims, int incdim, size_t inc,
|
||||
size_t* odom);
|
||||
static int up_start_by_chunks(int ndims, const size_t *dims,
|
||||
const size_t *chunks, size_t* odom);
|
||||
static int inq_value_size(int igrp, nc_type vartype, size_t *value_sizep);
|
||||
|
||||
static void
|
||||
check(int err, const char* fcn, const char* file, const int line)
|
||||
{
|
||||
@ -31,7 +22,7 @@ check(int err, const char* fcn, const char* file, const int line)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Check error return from malloc, and allow malloc(0) with subsequent free */
|
||||
/* Check error return from malloc */
|
||||
static void *
|
||||
emalloc (size_t size)
|
||||
{
|
||||
@ -45,155 +36,6 @@ emalloc (size_t size)
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Initialize iteration for a variable. Just a wrapper for
|
||||
* nc_blkio_init() that makes the netCDF calls needed to initialize
|
||||
* lower-level iterator. */
|
||||
int
|
||||
nc_get_iter(int ncid,
|
||||
int varid,
|
||||
size_t bufsize, /* size in bytes of memory buffer */
|
||||
nciter_t **iterpp /* returned opaque iteration state */)
|
||||
{
|
||||
int stat = NC_NOERR;
|
||||
nciter_t *iterp;
|
||||
nc_type vartype;
|
||||
size_t value_size; /* size in bytes of each variable element */
|
||||
int ndims; /* number of dimensions for variable */
|
||||
int *dimids;
|
||||
long long nvalues = 1;
|
||||
int dim;
|
||||
int chunked = 0;
|
||||
|
||||
/* Caller should free this by calling nc_free_iter(iterp) */
|
||||
iterp = (nciter_t *) emalloc(sizeof(nciter_t));
|
||||
memset((void*)iterp,0,sizeof(nciter_t)); /* make sure it is initialized */
|
||||
|
||||
stat = nc_inq_varndims(ncid, varid, &ndims);
|
||||
CHECK(stat, nc_inq_varndims);
|
||||
dimids = (int *) emalloc((ndims + 1) * sizeof(size_t));
|
||||
iterp->dimsizes = (size_t *) emalloc((ndims + 1) * sizeof(size_t));
|
||||
iterp->chunksizes = (size_t *) emalloc((ndims + 1) * sizeof(size_t));
|
||||
|
||||
stat = nc_inq_vardimid (ncid, varid, dimids);
|
||||
CHECK(stat, nc_inq_vardimid);
|
||||
for(dim = 0; dim < ndims; dim++) {
|
||||
size_t len;
|
||||
stat = nc_inq_dimlen(ncid, dimids[dim], &len);
|
||||
CHECK(stat, nc_inq_dimlen);
|
||||
nvalues *= len;
|
||||
iterp->dimsizes[dim] = len;
|
||||
}
|
||||
stat = nc_inq_vartype(ncid, varid, &vartype);
|
||||
CHECK(stat, nc_inq_vartype);
|
||||
stat = inq_value_size(ncid, vartype, &value_size);
|
||||
CHECK(stat, inq_value_size);
|
||||
#ifdef USE_NETCDF4
|
||||
{
|
||||
int contig = 1;
|
||||
if(ndims > 0) {
|
||||
stat = nc_inq_var_chunking(ncid, varid, &contig, NULL);
|
||||
CHECK(stat, nc_inq_var_chunking);
|
||||
}
|
||||
if(contig == 0) { /* chunked */
|
||||
stat = nc_inq_var_chunking(ncid, varid, &contig, iterp->chunksizes);
|
||||
CHECK(stat, nc_inq_var_chunking);
|
||||
chunked = 1;
|
||||
}
|
||||
}
|
||||
#endif /* USE_NETCDF4 */
|
||||
stat = nc_blkio_init(bufsize, value_size, ndims, chunked, iterp);
|
||||
CHECK(stat, nc_blkio_init);
|
||||
iterp->to_get = 0;
|
||||
free(dimids);
|
||||
*iterpp = iterp;
|
||||
return stat;
|
||||
}
|
||||
|
||||
/* Iterate on blocks for variables, by updating start and count vector
|
||||
* for next vara call. Assumes nc_get_iter called first. Returns
|
||||
* number of variable values to get, 0 if done, negative number if
|
||||
* error, so use like this:
|
||||
size_t to_get;
|
||||
while((to_get = nc_next_iter(&iter, start, count)) > 0) {
|
||||
... iteration ...
|
||||
}
|
||||
if(to_get < 0) { ... handle error ... }
|
||||
*/
|
||||
size_t
|
||||
nc_next_iter(nciter_t *iter, /* returned opaque iteration state */
|
||||
size_t *start, /* returned start vector for next vara call */
|
||||
size_t *count /* returned count vector for next vara call */
|
||||
) {
|
||||
int i;
|
||||
/* Note: special case for chunked variables is just an
|
||||
* optimization, the contiguous code below is OK even
|
||||
* for chunked variables, but in general will do more I/O ... */
|
||||
if(iter->first) {
|
||||
if(!iter->chunked) { /* contiguous storage */
|
||||
for(i = 0; i < iter->right_dim; i++) {
|
||||
start[i] = 0;
|
||||
count[i] = 1;
|
||||
}
|
||||
start[iter->right_dim] = 0;
|
||||
count[iter->right_dim] = iter->rows;
|
||||
for(i = iter->right_dim + 1; i < iter->rank; i++) {
|
||||
start[i] = 0;
|
||||
count[i] = iter->dimsizes[i];
|
||||
}
|
||||
} else { /* chunked storage */
|
||||
for(i = 0; i < iter->rank; i++) {
|
||||
start[i] = 0;
|
||||
count[i] = iter->chunksizes[i];
|
||||
}
|
||||
}
|
||||
iter->first = 0;
|
||||
} else {
|
||||
if(!iter->chunked) { /* contiguous storage */
|
||||
iter->more = up_start(iter->rank, iter->dimsizes, iter->right_dim,
|
||||
iter->inc, start);
|
||||
/* iterate on pieces of variable */
|
||||
if(iter->cur < iter->numrows) {
|
||||
iter->inc = iter->rows;
|
||||
count[iter->right_dim] = iter->rows;
|
||||
iter->cur++;
|
||||
} else {
|
||||
if(iter->leftover > 0) {
|
||||
count[iter->right_dim] = iter->leftover;
|
||||
iter->inc = iter->leftover;
|
||||
iter->cur = 0;
|
||||
}
|
||||
}
|
||||
} else { /* chunked storage */
|
||||
iter->more = up_start_by_chunks(iter->rank, iter->dimsizes,
|
||||
iter->chunksizes, start);
|
||||
/* adjust count to stay in range of dimsizes */
|
||||
for(i = 0; i < iter->rank; i++) {
|
||||
int leftover = iter->dimsizes[i] - start[i];
|
||||
count[i] = iter->chunksizes[i];
|
||||
if(leftover < count[i])
|
||||
count[i] = leftover;
|
||||
}
|
||||
}
|
||||
}
|
||||
iter->to_get = 1;
|
||||
for(i = 0; i < iter->rank; i++) {
|
||||
iter->to_get *= count[i];
|
||||
}
|
||||
return iter->more == 0 ? 0 : iter->to_get ;
|
||||
}
|
||||
|
||||
/* Free iterator and its internally allocated memory */
|
||||
int
|
||||
nc_free_iter(nciter_t *iterp) {
|
||||
if(iterp->dimsizes)
|
||||
free(iterp->dimsizes);
|
||||
if(iterp->chunksizes)
|
||||
free(iterp->chunksizes);
|
||||
if(iterp)
|
||||
free(iterp);
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/* Initialize block iteration for variables, including those that
|
||||
* won't fit in the copy buffer all at once. Returns error if
|
||||
* variable is chunked but size of chunks is too big to fit in bufsize
|
||||
@ -350,3 +192,319 @@ up_start_by_chunks(
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* initialize and return a new empty stack of grpids */
|
||||
static ncgiter_t *
|
||||
gs_init() {
|
||||
ncgiter_t *s = emalloc(sizeof(ncgiter_t));
|
||||
s->ngrps = 0;
|
||||
s->top = NULL;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* free a stack and all its nodes */
|
||||
static void
|
||||
gs_free(ncgiter_t *s) {
|
||||
grpnode_t *n0, *n1;
|
||||
n0 = s->top;
|
||||
while (n0) {
|
||||
n1 = n0->next;
|
||||
free(n0);
|
||||
n0 = n1;
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
|
||||
/* test if a stack is empty */
|
||||
static int
|
||||
gs_empty(ncgiter_t *s)
|
||||
{
|
||||
return s->ngrps == 0;
|
||||
}
|
||||
|
||||
/* push a grpid on stack */
|
||||
static void
|
||||
gs_push(ncgiter_t *s, int grpid)
|
||||
{
|
||||
grpnode_t *node = emalloc(sizeof(grpnode_t));
|
||||
|
||||
node->grpid = grpid;
|
||||
node->next = gs_empty(s) ? NULL : s->top;
|
||||
s->top = node;
|
||||
s->ngrps++;
|
||||
}
|
||||
|
||||
/* pop value off stack and return */
|
||||
static int
|
||||
gs_pop(ncgiter_t *s)
|
||||
{
|
||||
if (gs_empty(s)) {
|
||||
return -1; /* underflow, stack is empty */
|
||||
} else { /* pop a node */
|
||||
grpnode_t *top = s->top;
|
||||
int value = top->grpid;
|
||||
s->top = top->next;
|
||||
/* TODO: first call to free gets seg fault with libumem */
|
||||
free(top);
|
||||
s->ngrps--;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/* return top value on stack without popping stack */
|
||||
static int
|
||||
gs_top(ncgiter_t *s)
|
||||
{
|
||||
if (gs_empty(s)) {
|
||||
return -1; /* underflow, stack is empty */
|
||||
} else { /* get top value */
|
||||
grpnode_t *top = s->top;
|
||||
int value = top->grpid;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Begin public interfaces */
|
||||
|
||||
/* Initialize iteration for a variable. Just a wrapper for
|
||||
* nc_blkio_init() that makes the netCDF calls needed to initialize
|
||||
* lower-level iterator. */
|
||||
int
|
||||
nc_get_iter(int ncid,
|
||||
int varid,
|
||||
size_t bufsize, /* size in bytes of memory buffer */
|
||||
nciter_t **iterpp /* returned opaque iteration state */)
|
||||
{
|
||||
int stat = NC_NOERR;
|
||||
nciter_t *iterp;
|
||||
nc_type vartype;
|
||||
size_t value_size; /* size in bytes of each variable element */
|
||||
int ndims; /* number of dimensions for variable */
|
||||
int *dimids;
|
||||
long long nvalues = 1;
|
||||
int dim;
|
||||
int chunked = 0;
|
||||
|
||||
/* Caller should free this by calling nc_free_iter(iterp) */
|
||||
iterp = (nciter_t *) emalloc(sizeof(nciter_t));
|
||||
memset((void*)iterp,0,sizeof(nciter_t)); /* make sure it is initialized */
|
||||
|
||||
stat = nc_inq_varndims(ncid, varid, &ndims);
|
||||
CHECK(stat, nc_inq_varndims);
|
||||
dimids = (int *) emalloc((ndims + 1) * sizeof(size_t));
|
||||
iterp->dimsizes = (size_t *) emalloc((ndims + 1) * sizeof(size_t));
|
||||
iterp->chunksizes = (size_t *) emalloc((ndims + 1) * sizeof(size_t));
|
||||
|
||||
stat = nc_inq_vardimid (ncid, varid, dimids);
|
||||
CHECK(stat, nc_inq_vardimid);
|
||||
for(dim = 0; dim < ndims; dim++) {
|
||||
size_t len;
|
||||
stat = nc_inq_dimlen(ncid, dimids[dim], &len);
|
||||
CHECK(stat, nc_inq_dimlen);
|
||||
nvalues *= len;
|
||||
iterp->dimsizes[dim] = len;
|
||||
}
|
||||
stat = nc_inq_vartype(ncid, varid, &vartype);
|
||||
CHECK(stat, nc_inq_vartype);
|
||||
stat = inq_value_size(ncid, vartype, &value_size);
|
||||
CHECK(stat, inq_value_size);
|
||||
#ifdef USE_NETCDF4
|
||||
{
|
||||
int contig = 1;
|
||||
if(ndims > 0) {
|
||||
stat = nc_inq_var_chunking(ncid, varid, &contig, NULL);
|
||||
CHECK(stat, nc_inq_var_chunking);
|
||||
}
|
||||
if(contig == 0) { /* chunked */
|
||||
stat = nc_inq_var_chunking(ncid, varid, &contig, iterp->chunksizes);
|
||||
CHECK(stat, nc_inq_var_chunking);
|
||||
chunked = 1;
|
||||
}
|
||||
}
|
||||
#endif /* USE_NETCDF4 */
|
||||
stat = nc_blkio_init(bufsize, value_size, ndims, chunked, iterp);
|
||||
CHECK(stat, nc_blkio_init);
|
||||
iterp->to_get = 0;
|
||||
free(dimids);
|
||||
*iterpp = iterp;
|
||||
return stat;
|
||||
}
|
||||
|
||||
/* Iterate on blocks for variables, by updating start and count vector
|
||||
* for next vara call. Assumes nc_get_iter called first. Returns
|
||||
* number of variable values to get, 0 if done, negative number if
|
||||
* error, so use like this:
|
||||
size_t to_get;
|
||||
while((to_get = nc_next_iter(&iter, start, count)) > 0) {
|
||||
... iteration ...
|
||||
}
|
||||
if(to_get < 0) { ... handle error ... }
|
||||
*/
|
||||
size_t
|
||||
nc_next_iter(nciter_t *iter, /* returned opaque iteration state */
|
||||
size_t *start, /* returned start vector for next vara call */
|
||||
size_t *count /* returned count vector for next vara call */
|
||||
) {
|
||||
int i;
|
||||
/* Note: special case for chunked variables is just an
|
||||
* optimization, the contiguous code below is OK even
|
||||
* for chunked variables, but in general will do more I/O ... */
|
||||
if(iter->first) {
|
||||
if(!iter->chunked) { /* contiguous storage */
|
||||
for(i = 0; i < iter->right_dim; i++) {
|
||||
start[i] = 0;
|
||||
count[i] = 1;
|
||||
}
|
||||
start[iter->right_dim] = 0;
|
||||
count[iter->right_dim] = iter->rows;
|
||||
for(i = iter->right_dim + 1; i < iter->rank; i++) {
|
||||
start[i] = 0;
|
||||
count[i] = iter->dimsizes[i];
|
||||
}
|
||||
} else { /* chunked storage */
|
||||
for(i = 0; i < iter->rank; i++) {
|
||||
start[i] = 0;
|
||||
count[i] = iter->chunksizes[i];
|
||||
}
|
||||
}
|
||||
iter->first = 0;
|
||||
} else {
|
||||
if(!iter->chunked) { /* contiguous storage */
|
||||
iter->more = up_start(iter->rank, iter->dimsizes, iter->right_dim,
|
||||
iter->inc, start);
|
||||
/* iterate on pieces of variable */
|
||||
if(iter->cur < iter->numrows) {
|
||||
iter->inc = iter->rows;
|
||||
count[iter->right_dim] = iter->rows;
|
||||
iter->cur++;
|
||||
} else {
|
||||
if(iter->leftover > 0) {
|
||||
count[iter->right_dim] = iter->leftover;
|
||||
iter->inc = iter->leftover;
|
||||
iter->cur = 0;
|
||||
}
|
||||
}
|
||||
} else { /* chunked storage */
|
||||
iter->more = up_start_by_chunks(iter->rank, iter->dimsizes,
|
||||
iter->chunksizes, start);
|
||||
/* adjust count to stay in range of dimsizes */
|
||||
for(i = 0; i < iter->rank; i++) {
|
||||
int leftover = iter->dimsizes[i] - start[i];
|
||||
count[i] = iter->chunksizes[i];
|
||||
if(leftover < count[i])
|
||||
count[i] = leftover;
|
||||
}
|
||||
}
|
||||
}
|
||||
iter->to_get = 1;
|
||||
for(i = 0; i < iter->rank; i++) {
|
||||
iter->to_get *= count[i];
|
||||
}
|
||||
return iter->more == 0 ? 0 : iter->to_get ;
|
||||
}
|
||||
|
||||
/* Free iterator and its internally allocated memory */
|
||||
int
|
||||
nc_free_iter(nciter_t *iterp) {
|
||||
if(iterp->dimsizes)
|
||||
free(iterp->dimsizes);
|
||||
if(iterp->chunksizes)
|
||||
free(iterp->chunksizes);
|
||||
if(iterp)
|
||||
free(iterp);
|
||||
return NC_NOERR;
|
||||
}
|
||||
|
||||
/* Initialize group iterator for start group and all its descendant
|
||||
* groups. */
|
||||
int
|
||||
nc_get_giter(int grpid, /* start group id */
|
||||
ncgiter_t **iterp /* returned opaque iteration state */
|
||||
)
|
||||
{
|
||||
int stat = NC_NOERR;
|
||||
|
||||
stat = nc_inq_grpname(grpid, NULL); /* check if grpid is valid */
|
||||
if(stat != NC_EBADGRPID && stat != NC_EBADID) {
|
||||
*iterp = gs_init();
|
||||
gs_push(*iterp, grpid);
|
||||
}
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get group id of next group. On first call gets start group id,
|
||||
* subsequently returns other subgroup ids in preorder. Returns zero
|
||||
* when no more groups left.
|
||||
*/
|
||||
int
|
||||
nc_next_giter(ncgiter_t *iterp, int *grpidp) {
|
||||
int stat = NC_NOERR;
|
||||
int numgrps;
|
||||
int *grpids;
|
||||
int i;
|
||||
|
||||
if(gs_empty(iterp)) {
|
||||
*grpidp = 0; /* not a group, signals iterator is done */
|
||||
} else {
|
||||
*grpidp = gs_pop(iterp);
|
||||
stat = nc_inq_grps(*grpidp, &numgrps, NULL);
|
||||
CHECK(stat, nc_inq_grps);
|
||||
if(numgrps > 0) {
|
||||
grpids = (int *)emalloc(sizeof(int) * numgrps);
|
||||
stat = nc_inq_grps(*grpidp, &numgrps, grpids);
|
||||
CHECK(stat, nc_inq_grps);
|
||||
for(i = numgrps - 1; i >= 0; i--) { /* push ids on stack in reverse order */
|
||||
gs_push(iterp, grpids[i]);
|
||||
}
|
||||
free(grpids);
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release group iter.
|
||||
*/
|
||||
void
|
||||
nc_free_giter(ncgiter_t *iterp)
|
||||
{
|
||||
gs_free(iterp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get total number of groups (including the top-level group and all
|
||||
* descendant groups, recursively) and all descendant subgroup ids
|
||||
* (including the input rootid of the start group) for a group and
|
||||
* all its descendants, in preorder.
|
||||
*
|
||||
* If grpids or numgrps is NULL, it will be ignored. So typical use
|
||||
* is to call with grpids NULL to get numgrps, allocate enough space
|
||||
* for the group ids, then call again to get them.
|
||||
*/
|
||||
int
|
||||
nc_inq_grps_full(int rootid, int *numgrps, int *grpids)
|
||||
{
|
||||
int stat = NC_NOERR;
|
||||
ncgiter_t *giter; /* pointer to group iterator */
|
||||
int grpid;
|
||||
size_t count;
|
||||
|
||||
stat = nc_get_giter(rootid, &giter);
|
||||
CHECK(stat, nc_get_giter);
|
||||
|
||||
count = 0;
|
||||
stat = nc_next_giter(giter, &grpid);
|
||||
CHECK(stat, nc_next_giter);
|
||||
while(grpid != 0) {
|
||||
if(grpids)
|
||||
grpids[count] = grpid;
|
||||
count++;
|
||||
stat = nc_next_giter(giter, &grpid);
|
||||
CHECK(stat, nc_next_iter);
|
||||
}
|
||||
if(numgrps)
|
||||
*numgrps = count;
|
||||
nc_free_giter(giter);
|
||||
return stat;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The opaque structure to hold per-variable state of iteration
|
||||
* The opaque structure to hold per-variable state of data iteration
|
||||
*/
|
||||
typedef struct {
|
||||
int first; /* false after first invocation of nc_next_iter() */
|
||||
@ -33,12 +33,27 @@ typedef struct {
|
||||
size_t *chunksizes; /* ignored if not chunked */
|
||||
} nciter_t;
|
||||
|
||||
/* node in stack of group ids */
|
||||
typedef struct grpnode {
|
||||
int grpid;
|
||||
struct grpnode *next;
|
||||
} grpnode_t;
|
||||
|
||||
/*
|
||||
* The opaque structure to hold state of iteration over groups.
|
||||
* (Just implemented as a stack of group ids.)
|
||||
*/
|
||||
typedef struct {
|
||||
int ngrps; /* number of groups left to visit */
|
||||
grpnode_t *top; /* group ids left to visit */
|
||||
} ncgiter_t;
|
||||
|
||||
/*
|
||||
* The Interface
|
||||
* The Interfaces
|
||||
*/
|
||||
|
||||
/* Get iterator for a variable. Returns pointer to malloc'd nciter_t,
|
||||
* which caller must later release using nc_free_iter(), not
|
||||
/* Get iterator for variable data. Returns pointer to malloc'd
|
||||
* nciter_t, which caller must later release using nc_free_iter(), not
|
||||
* free(). */
|
||||
extern int
|
||||
nc_get_iter(int ncid, int varid, size_t bufsize, nciter_t **iterpp);
|
||||
@ -53,6 +68,35 @@ nc_next_iter(nciter_t *iterp, size_t *start, size_t *count);
|
||||
extern int
|
||||
nc_free_iter(nciter_t *iterp);
|
||||
|
||||
/*
|
||||
* Simplest interface for group iteration: get total number of groups
|
||||
* (including all descendant groups, recursively) and all group ids
|
||||
* for start group and its descendants, in preorder.
|
||||
*/
|
||||
extern int
|
||||
nc_inq_grps_full(int ncid, int *numgrps, int *ncids);
|
||||
|
||||
/*
|
||||
* More complex iterator interface: get group iterator for start group
|
||||
* ncid and all its descendant groups.
|
||||
*/
|
||||
extern int
|
||||
nc_get_giter(int ncid, ncgiter_t **iterp);
|
||||
|
||||
/*
|
||||
* Get group id of next group. On first call returns start group,
|
||||
* subsequently returns other subgroup ids in preorder. Returns grpid
|
||||
* of 0 (never an actual group number) when no more groups.
|
||||
*/
|
||||
extern int
|
||||
nc_next_giter(ncgiter_t *iterp, int *grpid);
|
||||
|
||||
/*
|
||||
* Release memory allocated for group iterator.
|
||||
*/
|
||||
void
|
||||
nc_free_giter(ncgiter_t *iterp);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user