mirror of
https://github.com/Unidata/netcdf-c.git
synced 2025-02-11 16:40:36 +08:00
* main: (39 commits) Define USE_SZIP variable for nc-config.cmake.in matching cmake variables in autotools configuration moving the version into the project command in cmake Updated doxygen files for older, less-forgiving versions of doxygen (1.9.1, at least) Clean up doxygen warnings that were being treated as failures. Correcting a weird doxygen issue that has appeared. cmake: Fix Szip link using correct cmake var cmake: Improve FindSzip logic to provide a Szip_LIBRARY var Changed link to netCDF-Fortran documentation. Replaced ancient K&R function declarations to be C23 compatible Rebased PR by hand against main. count argument in H5Sselect_hyperslab Add H5FD_http_finalize function and call on hdf5 finalize Catching up on PRs, this is 2431 on the current 'main' Fix cmake syntax typo. Typo fix in support of gh2824.wif Fix typo in nc-config.cmake.in Removed a use of sprintf that required changing a function signature Replaced some sprintf with snprintf with aid of new variable containing size Replaced trivial uses of sprintf with snprintf ...
603 lines
14 KiB
C
603 lines
14 KiB
C
#include <stddef.h>
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_GETOPT_H
|
|
#include <getopt.h>
|
|
#endif
|
|
|
|
#if defined(_WIN32) && !defined(__MINGW32__)
|
|
#include "XGetopt.h"
|
|
#endif
|
|
|
|
#include "netcdf.h"
|
|
#include "ncpathmgr.h"
|
|
#include "nclog.h"
|
|
|
|
#ifdef HAVE_HDF5_H
|
|
#include <hdf5.h>
|
|
#include <H5DSpublic.h>
|
|
#endif
|
|
|
|
#ifdef ENABLE_NCZARR
|
|
#include "zincludes.h"
|
|
#endif
|
|
|
|
#undef DEBUG
|
|
|
|
/* Short Aliases */
|
|
#ifdef USE_HDF5
|
|
#define H5
|
|
#endif
|
|
#ifdef ENABLE_NCZARR
|
|
#define NZ
|
|
#endif
|
|
|
|
typedef struct Format {
|
|
int format;
|
|
char file_name[NC_MAX_NAME];
|
|
char var_name[NC_MAX_NAME];
|
|
int fillvalue;
|
|
int debug;
|
|
int linear;
|
|
int holevalue;
|
|
int rank;
|
|
size_t dimlens[NC_MAX_VAR_DIMS];
|
|
size_t chunklens[NC_MAX_VAR_DIMS];
|
|
size_t chunkcounts[NC_MAX_VAR_DIMS];
|
|
size_t chunkprod;
|
|
size_t dimprod;
|
|
nc_type xtype;
|
|
} Format;
|
|
|
|
typedef struct Odometer {
|
|
int rank; /*rank */
|
|
size_t start[NC_MAX_VAR_DIMS];
|
|
size_t stop[NC_MAX_VAR_DIMS];
|
|
size_t max[NC_MAX_VAR_DIMS]; /* max size of ith index */
|
|
size_t index[NC_MAX_VAR_DIMS]; /* current value of the odometer*/
|
|
} Odometer;
|
|
|
|
#define floordiv(x,y) ((x) / (y))
|
|
#define ceildiv(x,y) (((x) % (y)) == 0 ? ((x) / (y)) : (((x) / (y)) + 1))
|
|
|
|
static char* captured[4096];
|
|
static int ncap = 0;
|
|
|
|
extern int nc__testurl(const char*,char**);
|
|
|
|
Odometer* odom_new(int rank, const size_t* stop, const size_t* max);
|
|
void odom_free(Odometer* odom);
|
|
int odom_more(Odometer* odom);
|
|
int odom_next(Odometer* odom);
|
|
size_t* odom_indices(Odometer* odom);
|
|
size_t odom_offset(Odometer* odom);
|
|
const char* odom_print(Odometer* odom);
|
|
|
|
static void
|
|
usage(int err)
|
|
{
|
|
if(err != 0) {
|
|
fprintf(stderr,"Error: (%d) %s\n",err,nc_strerror(err));
|
|
}
|
|
fprintf(stderr,"usage: ncdumpchunks [-b] -v <var> <file> \n");
|
|
fflush(stderr);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
const char*
|
|
printvector(int rank, size_t* vec)
|
|
{
|
|
char svec[NC_MAX_VAR_DIMS*3+1];
|
|
int i;
|
|
svec[0] = '\0';
|
|
for(i=0;i<rank;i++) {
|
|
char s[3+1];
|
|
if(i > 0) strlcat(svec,",",sizeof(svec));
|
|
snprintf(s,sizeof(s),"%u",(unsigned)vec[i]);
|
|
strlcat(svec,s,sizeof(svec));
|
|
}
|
|
captured[ncap++] = strdup(svec);
|
|
return captured[ncap-1];
|
|
}
|
|
|
|
void
|
|
cleanup(void)
|
|
{
|
|
size_t i;
|
|
for(i=0;i<ncap;i++)
|
|
if(captured[i]) free(captured[i]);
|
|
}
|
|
|
|
Odometer*
|
|
odom_new(int rank, const size_t* stop, const size_t* max)
|
|
{
|
|
int i;
|
|
Odometer* odom = NULL;
|
|
if((odom = calloc(1,sizeof(Odometer))) == NULL)
|
|
return NULL;
|
|
odom->rank = rank;
|
|
for(i=0;i<rank;i++) {
|
|
odom->start[i] = 0;
|
|
odom->stop[i] = stop[i];
|
|
odom->max[i] = max[i];
|
|
odom->index[i] = 0;
|
|
}
|
|
return odom;
|
|
}
|
|
|
|
void
|
|
odom_free(Odometer* odom)
|
|
{
|
|
if(odom) free(odom);
|
|
}
|
|
|
|
int
|
|
odom_more(Odometer* odom)
|
|
{
|
|
return (odom->index[0] < odom->stop[0]);
|
|
}
|
|
|
|
int
|
|
odom_next(Odometer* odom)
|
|
{
|
|
int i;
|
|
for(i=odom->rank-1;i>=0;i--) {
|
|
odom->index[i]++;
|
|
if(odom->index[i] < odom->stop[i]) break;
|
|
if(i == 0) return 0; /* leave the 0th entry if it overflows */
|
|
odom->index[i] = 0; /* reset this position */
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* Get the value of the odometer */
|
|
size_t*
|
|
odom_indices(Odometer* odom)
|
|
{
|
|
return odom->index;
|
|
}
|
|
|
|
size_t
|
|
odom_offset(Odometer* odom)
|
|
{
|
|
size_t i,offset;
|
|
|
|
offset = 0;
|
|
for(i=0;i<odom->rank;i++) {
|
|
offset *= odom->max[i];
|
|
offset += odom->index[i];
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
const char*
|
|
odom_print(Odometer* odom)
|
|
{
|
|
static char s[4096];
|
|
static char tmp[4096];
|
|
const char* sv;
|
|
|
|
s[0] = '\0';
|
|
snprintf(tmp,sizeof(tmp),"{rank=%u",(unsigned)odom->rank);
|
|
strcat(s,tmp);
|
|
strcat(s," start=("); sv = printvector(odom->rank,odom->start); strcat(s,sv); strcat(s,")");
|
|
strcat(s," stop=("); sv = printvector(odom->rank,odom->stop); strcat(s,sv); strcat(s,")");
|
|
strcat(s," max=("); sv = printvector(odom->rank,odom->max); strcat(s,sv); strcat(s,")");
|
|
snprintf(tmp,sizeof(tmp)," offset=%u",(unsigned)odom_offset(odom)); strcat(s,tmp);
|
|
strcat(s," indices=("); sv = printvector(odom->rank,odom->index); strcat(s,sv); strcat(s,")");
|
|
strcat(s,"}");
|
|
return s;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
char*
|
|
chunk_key(int format->rank, size_t* indices)
|
|
{
|
|
char key[NC_MAX_VAR_DIMS*3+1];
|
|
int i;
|
|
key[0] = '\0';
|
|
for(i=0;i<format->rank;i++) {
|
|
char s[3+1];
|
|
if(i > 0) strlcat(key,".",sizeof(key));
|
|
snprintf(s,sizeof(s),"%u",(unsigned)indices[i]);
|
|
strlcat(key,s,sizeof(key));
|
|
}
|
|
return strdup(key);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
setoffset(Odometer* odom, size_t* chunksizes, size_t* offset)
|
|
{
|
|
size_t i;
|
|
for(i=0;i<odom->rank;i++)
|
|
offset[i] = odom->index[i] * chunksizes[i];
|
|
}
|
|
|
|
static void
|
|
printindent(size_t indent)
|
|
{
|
|
while(indent-- > 0) printf(" ");
|
|
}
|
|
|
|
int
|
|
printchunklinear(Format* format, int* chunkdata, size_t indent)
|
|
{
|
|
size_t i;
|
|
for(i=0;i<format->chunkprod;i++) {
|
|
if(chunkdata[i] == format->fillvalue)
|
|
printf(" _");
|
|
else
|
|
printf(" %02d", chunkdata[i]);
|
|
}
|
|
printf("\n");
|
|
return NC_NOERR;
|
|
}
|
|
|
|
static void
|
|
printchunk2d(Format* format, int* chunkdata, size_t indent)
|
|
{
|
|
size_t pos;
|
|
size_t row;
|
|
pos = 0;
|
|
for(row=0;row<format->chunklens[0];row++) {
|
|
size_t col;
|
|
if(row > 0) printindent(indent);
|
|
for(col=0;col<format->chunklens[1];col++,pos++) {
|
|
if(chunkdata[pos] == format->fillvalue) printf(" _"); else printf(" %02d", chunkdata[pos]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
printchunk(Format* format, int* chunkdata, size_t indent)
|
|
{
|
|
size_t k[3];
|
|
int rank = format->rank;
|
|
size_t cols[3], pos;
|
|
size_t* chl = format->chunklens;
|
|
|
|
memset(cols,0,sizeof(cols));
|
|
|
|
if(format->xtype == NC_UBYTE) rank = 0;
|
|
switch (rank) {
|
|
case 1:
|
|
cols[0] = 1;
|
|
cols[1] = 1;
|
|
cols[2] = chl[0];
|
|
break;
|
|
case 2:
|
|
cols[0] = 1;
|
|
cols[1] = chl[0];
|
|
cols[2] = chl[1];
|
|
break;
|
|
case 3:
|
|
cols[0] = chl[0];
|
|
cols[1] = chl[1];
|
|
cols[2] = chl[2];
|
|
break;
|
|
default:
|
|
cols[0] = 1;
|
|
cols[1] = 1;
|
|
cols[2] = format->chunkprod;
|
|
break;
|
|
}
|
|
// offset = (((k0*chl[0])+k1)*chl[1])+k2;
|
|
pos = 0;
|
|
for(k[0]=0;k[0]<cols[0];k[0]++) {
|
|
if(k[0] > 0) printindent(indent);
|
|
k[1] = 0; k[2] = 0; /* reset */
|
|
for(k[1]=0;k[1]<cols[1];k[1]++) {
|
|
k[2] = 0;
|
|
if(k[1] > 0) printf(" |");
|
|
for(k[2]=0;k[2]<cols[2];k[2]++) {
|
|
if(format->xtype == NC_UBYTE) {
|
|
size_t l;
|
|
unsigned char* bchunkdata = (unsigned char*)(&chunkdata[pos]);
|
|
for(l=0;l<sizeof(int);l++) {
|
|
if(bchunkdata[l] == format->fillvalue)
|
|
printf(" _");
|
|
else
|
|
printf(" %02u", bchunkdata[l]);
|
|
}
|
|
} else {
|
|
if(chunkdata[pos] == format->fillvalue)
|
|
printf(" _");
|
|
else
|
|
printf(" %02d", chunkdata[pos]);
|
|
}
|
|
pos++;
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
#if 0
|
|
for(k=0;k<format->chunkprod;k++) {
|
|
if(k > 0 && k % cols == 0) printf(" |");
|
|
printf(" %02d", chunkdata[k]);
|
|
}
|
|
printf("\n");
|
|
#endif
|
|
}
|
|
|
|
|
|
int
|
|
dump(Format* format)
|
|
{
|
|
void* chunkdata = NULL; /*[CHUNKPROD];*/
|
|
Odometer* odom = NULL;
|
|
int r;
|
|
size_t offset[NC_MAX_VAR_DIMS];
|
|
int holechunk = 0;
|
|
char sindices[64];
|
|
#ifdef H5
|
|
int i;
|
|
hid_t fileid, grpid, datasetid;
|
|
hid_t dxpl_id = H5P_DEFAULT; /*data transfer property list */
|
|
unsigned int filter_mask = 0;
|
|
hsize_t hoffset[NC_MAX_VAR_DIMS];
|
|
#endif
|
|
#ifdef NZ
|
|
int stat = NC_NOERR;
|
|
size64_t zindices[NC_MAX_VAR_DIMS];
|
|
int ncid, varid;
|
|
#endif
|
|
|
|
#ifdef H5
|
|
if(format->debug) {
|
|
H5Eset_auto2(H5E_DEFAULT,(H5E_auto2_t)H5Eprint1,stderr);
|
|
}
|
|
#endif
|
|
|
|
memset(offset,0,sizeof(offset));
|
|
#ifdef H5
|
|
memset(hoffset,0,sizeof(hoffset));
|
|
#endif
|
|
|
|
switch (format->format) {
|
|
#ifdef H5
|
|
case NC_FORMATX_NC_HDF5:
|
|
if ((fileid = H5Fopen(format->file_name, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0) usage(NC_EHDFERR);
|
|
if ((grpid = H5Gopen1(fileid, "/")) < 0) usage(NC_EHDFERR);
|
|
if ((datasetid = H5Dopen1(grpid, format->var_name)) < 0) usage(NC_EHDFERR);
|
|
break;
|
|
#endif
|
|
#ifdef NZ
|
|
case NC_FORMATX_NCZARR:
|
|
if((stat=nc_open(format->file_name,0,&ncid))) usage(stat);
|
|
if((stat=nc_inq_varid(ncid,format->var_name,&varid))) usage(stat);
|
|
break;
|
|
#endif
|
|
default: usage(NC_EINVAL);
|
|
}
|
|
|
|
if((odom = odom_new(format->rank,format->chunkcounts,format->dimlens))==NULL) usage(NC_ENOMEM);
|
|
|
|
if((chunkdata = calloc(sizeof(int),format->chunkprod))==NULL) usage(NC_ENOMEM);
|
|
|
|
printf("rank=%d dims=(%s) chunks=(%s)\n",format->rank,printvector(format->rank,format->dimlens),
|
|
printvector(format->rank,format->chunklens));
|
|
|
|
while(odom_more(odom)) {
|
|
setoffset(odom,format->chunklens,offset);
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr,"odom=%s\n",odom_print(odom));
|
|
fprintf(stderr,"offset=(");
|
|
for(i=0;i<format->rank;i++)
|
|
fprintf(stderr,"%s%lu",(i > 0 ? "," : ""),(unsigned long)offset[i]);
|
|
fprintf(stderr,")\n");
|
|
fflush(stderr);
|
|
#endif
|
|
|
|
if(format->debug) {
|
|
fprintf(stderr,"chunk: %s\n",printvector(format->rank,offset));
|
|
}
|
|
|
|
holechunk = 0;
|
|
switch (format->format) {
|
|
#ifdef H5
|
|
case NC_FORMATX_NC_HDF5: {
|
|
for(i=0;i<format->rank;i++) hoffset[i] = (hsize_t)offset[i];
|
|
if(H5Dread_chunk(datasetid, dxpl_id, hoffset, &filter_mask, chunkdata) < 0)
|
|
holechunk = 1;
|
|
} break;
|
|
#endif
|
|
#ifdef NZ
|
|
case NC_FORMATX_NCZARR:
|
|
for(r=0;r<format->rank;r++) zindices[r] = (size64_t)odom->index[r];
|
|
switch (stat=NCZ_read_chunk(ncid, varid, zindices, chunkdata)) {
|
|
case NC_NOERR: break;
|
|
case NC_EEMPTY: holechunk = 1; break;
|
|
default: usage(stat);
|
|
}
|
|
break;
|
|
#endif
|
|
default: usage(NC_EINVAL);
|
|
}
|
|
if(holechunk) {
|
|
/* Hole chunk: use holevalue */
|
|
size_t i = 0;
|
|
int* idata = (int*)chunkdata;
|
|
for(i=0;i<format->chunkprod;i++)
|
|
idata[i] = format->holevalue;
|
|
}
|
|
sindices[0] = '\0';
|
|
for(r=0;r<format->rank;r++) {
|
|
char sstep[64];
|
|
snprintf(sstep,sizeof(sstep),"[%lu/%lu]",(unsigned long)odom->index[r],(unsigned long)offset[r]);
|
|
strcat(sindices,sstep);
|
|
}
|
|
strcat(sindices," =");
|
|
printf("%s",sindices);
|
|
if(format->linear)
|
|
printchunklinear(format,chunkdata,strlen(sindices));
|
|
else if(format->rank == 2)
|
|
printchunk2d(format,chunkdata,strlen(sindices));
|
|
else
|
|
printchunk(format,chunkdata,strlen(sindices));
|
|
fflush(stdout);
|
|
odom_next(odom);
|
|
}
|
|
|
|
/* Close up. */
|
|
switch (format->format) {
|
|
#ifdef H5
|
|
case NC_FORMATX_NC_HDF5:
|
|
if (H5Dclose(datasetid) < 0) abort();
|
|
if (H5Gclose(grpid) < 0) abort();
|
|
if (H5Fclose(fileid) < 0) abort();
|
|
break;
|
|
#endif
|
|
#ifdef NZ
|
|
case NC_FORMATX_NCZARR:
|
|
if((stat=nc_close(ncid))) usage(stat);
|
|
break;
|
|
#endif
|
|
default: usage(NC_EINVAL);
|
|
}
|
|
/* Cleanup */
|
|
free(chunkdata);
|
|
odom_free(odom);
|
|
return 0;
|
|
}
|
|
|
|
static const char* urlexts[] = {"file", "zip", NULL};
|
|
|
|
static const char*
|
|
filenamefor(const char* f0)
|
|
{
|
|
static char result[4096];
|
|
const char** extp;
|
|
char* p;
|
|
|
|
strcpy(result,f0); /* default */
|
|
if(nc__testurl(f0,NULL)) goto done;
|
|
/* Not a URL */
|
|
p = strrchr(f0,'.'); /* look at the extension, if any */
|
|
if(p == NULL) goto done; /* No extension */
|
|
p++;
|
|
for(extp=urlexts;*extp;extp++) {
|
|
if(strcmp(p,*extp)==0) break;
|
|
}
|
|
if(*extp == NULL) goto done; /* not found */
|
|
/* Assemble the url */
|
|
strcpy(result,"file://");
|
|
strcat(result,f0); /* core path */
|
|
strcat(result,"#mode=nczarr,");
|
|
strcat(result,*extp);
|
|
done:
|
|
return result;
|
|
}
|
|
|
|
int
|
|
main(int argc, char** argv)
|
|
{
|
|
int i,stat = NC_NOERR;
|
|
Format format;
|
|
int ncid, varid, dimids[NC_MAX_VAR_DIMS];
|
|
int vtype, storage;
|
|
int mode;
|
|
int c;
|
|
|
|
memset(&format,0,sizeof(format));
|
|
|
|
/* Init some format fields */
|
|
format.xtype = NC_INT;
|
|
format.holevalue = -2;
|
|
|
|
while ((c = getopt(argc, argv, "bhv:DT:L")) != EOF) {
|
|
switch(c) {
|
|
case 'b':
|
|
format.xtype = NC_UBYTE;
|
|
break;
|
|
case 'h':
|
|
usage(0);
|
|
break;
|
|
case 'v':
|
|
strcpy(format.var_name,optarg);
|
|
break;
|
|
case 'D':
|
|
format.debug = 1;
|
|
break;
|
|
case 'L':
|
|
format.linear = 1;
|
|
break;
|
|
case 'T':
|
|
nctracelevel(atoi(optarg));
|
|
break;
|
|
case '?':
|
|
fprintf(stderr,"unknown option: '%c'\n",c);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/* get file argument */
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc == 0) {
|
|
fprintf(stderr, "no input file specified\n");
|
|
exit(1);
|
|
}
|
|
|
|
{
|
|
char* s = NC_shellUnescape(argv[0]);
|
|
strcpy(format.file_name,filenamefor(s));
|
|
nullfree(s);
|
|
}
|
|
|
|
if(strlen(format.file_name) == 0) {
|
|
fprintf(stderr, "no input file specified\n");
|
|
exit(1);
|
|
}
|
|
|
|
if(strlen(format.var_name) == 0) {
|
|
fprintf(stderr, "no input var specified\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* Get info about the file type */
|
|
if((stat=nc_open(format.file_name,0,&ncid))) usage(stat);
|
|
|
|
if((stat=nc_inq_format_extended(ncid,&format.format,&mode))) usage(stat);
|
|
|
|
/* Get the info about the var */
|
|
if((stat=nc_inq_varid(ncid,format.var_name,&varid))) usage(stat);
|
|
if((stat=nc_inq_var(ncid,varid,NULL,&vtype,&format.rank,dimids,NULL))) usage(stat);
|
|
if(format.rank == 0) usage(NC_EDIMSIZE);
|
|
if((stat=nc_inq_var_chunking(ncid,varid,&storage,format.chunklens))) usage(stat);
|
|
if(storage != NC_CHUNKED) usage(NC_EBADCHUNK);
|
|
if((stat=nc_get_att(ncid,varid,"_FillValue",&format.fillvalue))) usage(stat);
|
|
|
|
for(i=0;i<format.rank;i++) {
|
|
if((stat=nc_inq_dimlen(ncid,dimids[i],&format.dimlens[i]))) usage(stat);
|
|
format.chunkcounts[i] = ceildiv(format.dimlens[i],format.chunklens[i]);
|
|
}
|
|
|
|
if((stat=nc_close(ncid))) usage(stat);
|
|
|
|
/* Precompute */
|
|
for(format.chunkprod=1,i=0;i<format.rank;i++) format.chunkprod *= format.chunklens[i];
|
|
for(format.dimprod=1,i=0;i<format.rank;i++) format.dimprod *= format.dimlens[i];
|
|
|
|
dump(&format);
|
|
|
|
cleanup();
|
|
|
|
return 0;
|
|
}
|