more testing for late fill setting

This commit is contained in:
Ed Hartnett 2018-10-17 09:44:18 -06:00
parent d959512a2e
commit 91ec0109f3
2 changed files with 87 additions and 39 deletions

View File

@ -546,42 +546,57 @@ NC_getshape(int ncid, int varid, int ndims, size_t* shape)
return status; return status;
} }
/*! Set the fill value for a variable. /**
* @ingroup variables
\ingroup variables *
* Set the fill value for a variable.
\param ncid NetCDF ID, from a previous call to nc_open or *
nc_create. * @note For netCDF classic, 64-bit offset, and CDF5 formats, it is
* allowed (but not good practice) to set the fill value after data
\param varid Variable ID. * have been written to the variable. In this case, unless the
* variable has been completely specified (without gaps in the data),
\param no_fill Set to NC_NOFILL to turn off fill mode for this * any existing filled values will not be recognized as fill values by
variable. Set to NC_FILL (the default) to turn on fill mode for the * applications reading the data. Best practice is to set the fill
variable. * value after the variable has been defined, but before any data have
* been written to that varibale. In NetCDF-4 files, this is enforced
\param fill_value the fill value to be used for this variable. Must be * by the HDF5 library. For netCDF-4 files, an error is returned if
the same type as the variable. This must point to enough free memory * the user attempts to set the fill value after writing data to the
to hold one element of the data type of the variable. (For example, an * variable.
NC_INT will require 4 bytes for it's fill value, which is also an
NC_INT.)
* @param ncid NetCDF ID, from a previous call to nc_open or
* nc_create.
* @param varid Variable ID.
* @param no_fill Set to NC_NOFILL to turn off fill mode for this
* variable. Set to NC_FILL (the default) to turn on fill mode for the
* variable.
* @param fill_value the fill value to be used for this variable. Must
* be the same type as the variable. This must point to enough free
* memory to hold one element of the data type of the variable. (For
* example, an NC_INT will require 4 bytes for it's fill value, which
* is also an NC_INT.)
*
* @returns ::NC_NOERR No error. * @returns ::NC_NOERR No error.
* @returns ::NC_EBADID Bad ID. * @returns ::NC_EBADID Bad ID.
* @returns ::NC_ENOTINDEFINE Not in define mode. This is returned for * @returns ::NC_ENOTINDEFINE Not in define mode. This is returned
netCDF classic, 64-bit offset, or 64-bit data files, or for netCDF-4 files, * for netCDF classic, 64-bit offset, or 64-bit data files, or for
when they were created with NC_STRICT_NC3 flag. See \ref nc_create. * netCDF-4 files, when they were created with NC_STRICT_NC3 flag. See
* @ref nc_create.
* @returns ::NC_EPERM Attempt to create object in read-only file. * @returns ::NC_EPERM Attempt to create object in read-only file.
* @returns ::NC_ELATEDEF (NetCDF-4 only). Returned when user attempts
\section nc_def_var_fill_example Example * to set fill value after data are written.
* @returns ::NC_EGLOBAL Attempt to set fill value on NC_GLOBAL.
In this example from libsrc4/tst_vars.c, a variable is defined, and *
the fill mode turned off. Then nc_inq_fill() is used to check that the * @section nc_def_var_fill_example Example
setting is correct. Then some data are written to the variable. Since *
the data that are written do not cover the full extent of the * In this example from libsrc4/tst_vars.c, a variable is defined, and
variable, the missing values will just be random. If fill value mode * the fill mode turned off. Then nc_inq_fill() is used to check that
was turned on, the missing values would get the fill value. * the setting is correct. Then some data are written to the
* variable. Since the data that are written do not cover the full
\code * extent of the variable, the missing values will just be random. If
* fill value mode was turned on, the missing values would get the
* fill value.
*
@code
#define DIM7_LEN 2 #define DIM7_LEN 2
#define DIM7_NAME "dim_7_from_Indiana" #define DIM7_NAME "dim_7_from_Indiana"
#define VAR7_NAME "var_7_from_Idaho" #define VAR7_NAME "var_7_from_Idaho"
@ -608,7 +623,8 @@ was turned on, the missing values would get the fill value.
if (nc_get_var1_ushort(ncid, varid, index, &ushort_data_in)) ERR; if (nc_get_var1_ushort(ncid, varid, index, &ushort_data_in)) ERR;
if (nc_close(ncid)) ERR; if (nc_close(ncid)) ERR;
\endcode @endcode
* @author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/ */
int int
nc_def_var_fill(int ncid, int varid, int no_fill, const void *fill_value) nc_def_var_fill(int ncid, int varid, int no_fill, const void *fill_value)
@ -617,7 +633,7 @@ nc_def_var_fill(int ncid, int varid, int no_fill, const void *fill_value)
int stat = NC_check_id(ncid,&ncp); int stat = NC_check_id(ncid,&ncp);
if(stat != NC_NOERR) return stat; if(stat != NC_NOERR) return stat;
/* Dennis Heimbigner: (Using NC_GLOBAL is ilegal, as this API) has no /* Dennis Heimbigner: Using NC_GLOBAL is illegal, as this API has no
* provision for specifying the type of the fillvalue, it must of necessity * provision for specifying the type of the fillvalue, it must of necessity
* be using the type of the variable to interpret the bytes of the * be using the type of the variable to interpret the bytes of the
* fill_value argument. * fill_value argument.

View File

@ -17,6 +17,7 @@
#define DIM_LEN 10 #define DIM_LEN 10
#define VAR_NAME "Copernicus_var" #define VAR_NAME "Copernicus_var"
#define DIM_NAME "Copernicus_dim" #define DIM_NAME "Copernicus_dim"
#define NUM_FILL_WRITE_TESTS 2
/* Determine how many formats are available, and what they are. */ /* Determine how many formats are available, and what they are. */
void void
@ -103,7 +104,7 @@ main(int argc, char **argv)
printf("\n*** Testing netcdf format functions.\n"); printf("\n*** Testing netcdf format functions.\n");
{ {
int ncid; int ncid;
int f; int f, d;
int format[MAX_NUM_FORMATS]; int format[MAX_NUM_FORMATS];
int num_formats; int num_formats;
@ -160,20 +161,51 @@ main(int argc, char **argv)
if (nc_close(ncid)) ERR; if (nc_close(ncid)) ERR;
} }
SUMMARIZE_ERR; SUMMARIZE_ERR;
printf("*** testing NC_ELATEFILL errors with format %d...", format[f]); for (d = 0; d < NUM_FILL_WRITE_TESTS; d++)
{ {
printf("*** testing late fill handling with format %d writing %d...", format[f], d);
char file_name[NC_MAX_NAME + 1]; char file_name[NC_MAX_NAME + 1];
int dimid, varid; int dimid, varid;
size_t index = {DIM_LEN - 1};
int data = TEST_VAL_42;
int data_in;
int fill_value = TEST_VAL_42 * 2;
sprintf(file_name, "%s_%d_elatefill.nc", FILE_NAME_BASE, format[f]); /* Try to set fill mode after data have been written. */
sprintf(file_name, "%s_%d_%d_elatefill.nc", FILE_NAME_BASE, format[f], d);
if (nc_set_default_format(format[f], NULL)) ERR; if (nc_set_default_format(format[f], NULL)) ERR;
if (nc_create(file_name, 0, &ncid)) ERR; if (nc_create(file_name, 0, &ncid)) ERR;
if (nc_def_dim(ncid, DIM_NAME, DIM_LEN, &dimid)) ERR; if (nc_def_dim(ncid, DIM_NAME, DIM_LEN, &dimid)) ERR;
if (nc_def_var(ncid, VAR_NAME, NC_INT, NDIM1, &dimid, &varid)) ERR; if (nc_def_var(ncid, VAR_NAME, NC_INT, NDIM1, &dimid, &varid)) ERR;
if (nc_enddef(ncid)) ERR; if (nc_enddef(ncid)) ERR;
/* For netCDF-4, we don't actually have to write data to
* prevent future setting of the fill value. Once the user
* leaves calls enddef after defining a var, fill values
* can no longer be set. */
if (d)
if (nc_put_var1_int(ncid, varid, &index, &data)) ERR;
if (nc_redef(ncid)) ERR;
/* Setting the fill value after data are written is
* allowed in classic formats, but not netcdf-4. */
if (format[f] == NC_FORMAT_CLASSIC || format[f] == NC_FORMAT_64BIT_OFFSET ||
format[f] == NC_FORMAT_CDF5)
{
if (nc_def_var_fill(ncid, varid, NC_FILL, &fill_value)) ERR;
}
else
{
if (nc_def_var_fill(ncid, varid, NC_FILL, &fill_value) != NC_ELATEDEF) ERR;
}
if (nc_enddef(ncid)) ERR;
if (nc_close(ncid)) ERR; if (nc_close(ncid)) ERR;
}
SUMMARIZE_ERR; /* Open the file and check data. */
if (nc_open(file_name, NC_NOWRITE, &ncid)) ERR;
if (nc_get_var1_int(ncid, varid, &index, &data_in)) ERR;
if (data_in != (d ? data : NC_FILL_INT)) ERR;
if (nc_close(ncid)) ERR;
SUMMARIZE_ERR;
} /* next fill val write test */
} /* next format */ } /* next format */
} }
FINAL_RESULTS; FINAL_RESULTS;