netcdf-c/ncdump/tst_enum_undef.c
Dennis Heimbigner 65fd9fe1a5 Provide a default enum const when fill value does not match any enum const.
re: https://github.com/Unidata/netcdf-c/issues/982

It is possible to define an enum type that has no enum constant
with value zero. However, HDF5 has a default fill value of zero
that it used to fill all chunks. In the event that this situation
occurs, ncdump, say, will fail because there is no enum const
to print for the value zero.

The solution is to create a special enum constant called "_UNDEFINED"
that has the value zero. It is only used in the case that there is
no constant in the enum that already covers zero.

A test case is added in netcdf-c/ncdump to validate this solution.

Note: the changes occur primarily in libsrc4, so they also work for NCZarr.
2022-07-17 14:32:31 -06:00

149 lines
4.2 KiB
C

/* This is part of the netCDF package. Copyright 2018 University
Corporation for Atmospheric Research/Unidata See COPYRIGHT file for
conditions of use. See www.unidata.ucar.edu for more info.
Create a test file with an enum type and enum data for ncdump to read.
$Id: tst_enum_data.c,v 1.8 2008/10/20 01:48:08 ed Exp $
*/
#include <nc_tests.h>
#include "err_macros.h"
#include <netcdf.h>
#undef WITHFILL
#define FILE2_NAME "tst_enum_undef.nc"
#define TYPE2_NAME "cloud_class_t"
#define DIM2_NAME "station"
#define DIM2_LEN 5
#define VAR2_NAME "primary_cloud"
#define VAR2_RANK 1
#define ATT2_NAME "_FillValue"
#define ATT2_LEN 1
int
main(int argc, char **argv)
{
int ncid;
int dimid, varid;
nc_type typeid;
int num_members;
char name_in[NC_MAX_NAME+1];
nc_type base_nc_type_in;
size_t nfields_in, base_size_in, num_members_in;
int class_in;
unsigned char value_in;
#ifdef WITHFILL
char zero = 0;
#endif
int i;
enum clouds { /* a C enumeration */
/* No 0 value */
CUMULONIMBUS=1,
STRATUS=2,
STRATOCUMULUS=3,
CUMULUS=4,
ALTOSTRATUS=5,
NIMBOSTRATUS=6,
ALTOCUMULUS=7,
CIRROSTRATUS=8,
CIRROCUMULUS=9,
CIRRUS=10,
MISSING=255};
struct {
char *name;
unsigned char value;
} cloud_types[] = {
{"Cumulonimbus", CUMULONIMBUS},
{"Stratus", STRATUS},
{"Stratocumulus", STRATOCUMULUS},
{"Cumulus", CUMULUS},
{"Altostratus", ALTOSTRATUS},
{"Nimbostratus", NIMBOSTRATUS},
{"Altocumulus", ALTOCUMULUS},
{"Cirrostratus", CIRROSTRATUS},
{"Cirrocumulus", CIRROCUMULUS},
{"Cirrus", CIRRUS},
{"Missing", MISSING}
};
int var_dims[VAR2_RANK];
unsigned char cloud_data[DIM2_LEN] = {
0, STRATUS, 0, CUMULONIMBUS, MISSING};
unsigned char cloud_data_in[DIM2_LEN];
printf("\n*** Testing enum undefined identifier.\n");
printf("*** creating enum test file %s...", FILE2_NAME);
/*nc_set_log_level(3);*/
if (nc_create(FILE2_NAME, NC_CLOBBER | NC_NETCDF4, &ncid)) ERR;
/* Create an enum type. */
if (nc_def_enum(ncid, NC_UBYTE, TYPE2_NAME, &typeid)) ERR;
num_members = (sizeof cloud_types) / (sizeof cloud_types[0]);
for (i = 0; i < num_members; i++) {
if (nc_insert_enum(ncid, typeid, cloud_types[i].name,
&cloud_types[i].value))
ERR;
}
/* Declare a station dimension */
if (nc_def_dim(ncid, DIM2_NAME, DIM2_LEN, &dimid)) ERR;
/* Declare a variable of the enum type */
var_dims[0] = dimid;
if (nc_def_var(ncid, VAR2_NAME, typeid, VAR2_RANK, var_dims, &varid)) ERR;
#ifdef WITHFILL
if (nc_def_var_fill(ncid, varid, NC_FILL, &zero)) ERR;
#endif
if (nc_enddef(ncid)) ERR;
/* Store some data of the enum type */
if(nc_put_var(ncid, varid, cloud_data)) ERR;
/* Write the file. */
if (nc_close(ncid)) ERR;
/* Check it out. */
/* Reopen the file. */
if (nc_open(FILE2_NAME, NC_NOWRITE, &ncid)) ERR;
if (nc_inq_user_type(ncid, typeid, name_in, &base_size_in, &base_nc_type_in,
&nfields_in, &class_in)) ERR;
if (strcmp(name_in, TYPE2_NAME) ||
base_size_in != sizeof(unsigned char) ||
base_nc_type_in != NC_UBYTE ||
nfields_in != num_members ||
class_in != NC_ENUM) ERR;
if (nc_inq_enum(ncid, typeid, name_in,
&base_nc_type_in, &base_size_in, &num_members_in)) ERR;
if (strcmp(name_in, TYPE2_NAME) ||
base_nc_type_in != NC_UBYTE ||
num_members_in != num_members) ERR;
for (i = 0; i < num_members; i++)
{
if (nc_inq_enum_member(ncid, typeid, i, name_in, &value_in)) ERR;
if (strcmp(name_in, cloud_types[i].name) ||
value_in != cloud_types[i].value) ERR;
if (nc_inq_enum_ident(ncid, typeid, cloud_types[i].value,
name_in)) ERR;
if (strcmp(name_in, cloud_types[i].name)) ERR;
}
if (nc_inq_varid(ncid, VAR2_NAME, &varid)) ERR;
#ifdef WITHFILL
if (nc_get_att(ncid, varid, ATT2_NAME, &value_in)) ERR;
if (value_in != 0) ERR;
#endif
if(nc_get_var(ncid, varid, cloud_data_in)) ERR;
for (i = 0; i < DIM2_LEN; i++) {
if (cloud_data_in[i] != cloud_data[i]) ERR;
}
if (nc_close(ncid)) ERR;
SUMMARIZE_ERR;
FINAL_RESULTS;
}