Repository: Unidata/netcdf-c (c)

Issue:
Error in netcdf-4 endianness handling?
The attached file appears to produce incorrect result.
I believe this is because endianness is attached to the type
object (NC_TYPE_INFO_T) rather than the var object (NC_VAR_INFO_T).
In the attached, we define an enum type and use it as the var type
for two different variables. We then set different endianness on the
two variables. What happens is that the endianness of the first variable
is changed when the endianness of the second variable is set.
[t_endian.zip](https://github.com/Unidata/netcdf-c/files/3542691/t_endian.zip)



Pull request description:
now testing that endianness can only be set on atomic ints and floats
Fixes #1479

As is clearly stated in the documentation for nc_def_var_endian():

`Warning: this function is only defined if the type of the variable is an atomic integer or float type.`

But this was not actually being checked in the code. Setting the endianness for other types is not safe. If there is an interest in controlling endianess for user-defined types, we can pursue that as a separate and new feature. (But no one wants it anyway.)

In this PR I add checking to ensure that endianness is only set on atomic integer or float types. Tests are added to confirm.



Patch:
diff --git a/libhdf5/hdf5var.c b/libhdf5/hdf5var.c
index bf0b7e8a1..38d683eba 100644
--- a/libhdf5/hdf5var.c
+++ b/libhdf5/hdf5var.c
@@ -796,7 +796,27 @@ reportchunking(dfalt?"extra: default: ":"extra: user: ",var);
 
     /* Is the user setting the endianness? */
     if (endianness)
+    {
+        /* Setting endianness is only premitted on atomic integer and
+         * atomic float types. */
+        switch (var->type_info->hdr.id)
+        {
+        case NC_BYTE:
+        case NC_SHORT:
+        case NC_INT:
+        case NC_FLOAT:
+        case NC_DOUBLE:
+        case NC_UBYTE:
+        case NC_USHORT:
+        case NC_UINT:
+        case NC_INT64:
+        case NC_UINT64:
+            break;
+        default:
+            return NC_EINVAL;
+        }
         var->type_info->endianness = *endianness;
+    }
 
     return NC_NOERR;
 }


Test patch:
diff --git a/nc_test4/tst_vars3.c b/nc_test4/tst_vars3.c
index 3f7cf3511..285446d27 100644
--- a/nc_test4/tst_vars3.c
+++ b/nc_test4/tst_vars3.c
@@ -179,7 +179,7 @@ main(int argc, char **argv)
       if (nc_insert_compound(ncid, typeid, BILLY, NC_COMPOUND_OFFSET(struct billy_bob, billy), NC_INT)) ERR;
       if (nc_insert_compound(ncid, typeid, BOB, NC_COMPOUND_OFFSET(struct billy_bob, bob), NC_INT)) ERR;
       if (nc_def_var(ncid, VAR_NAME1, typeid, 0, NULL, &varid)) ERR;
-      if (nc_def_var_endian(ncid, varid, NC_ENDIAN_BIG)) ERR;
+      if (nc_def_var_endian(ncid, varid, NC_ENDIAN_BIG) != NC_EINVAL) ERR;
       if (nc_close(ncid)) ERR;
 
       /* Open the file and check. */
diff --git a/nc_test4/tst_vars4.c b/nc_test4/tst_vars4.c
index acc2d0b3c..5d8712b42 100644
--- a/nc_test4/tst_vars4.c
+++ b/nc_test4/tst_vars4.c
@@ -174,7 +174,6 @@ main(int argc, char **argv)
       int data_in;
 
       /* Create the test file with two scalar vars. */
-      nc_set_log_level(4);
       if (nc_create(FILE_NAME, NC_NETCDF4 | NC_CLOBBER, &ncid)) ERR;
       if (nc_def_var(ncid, CLAIR, NC_INT, 0, NULL, &varid1)) ERR;
       if (nc_def_var_endian(ncid, varid1, NC_ENDIAN_BIG)) ERR;
@@ -196,5 +195,35 @@ main(int argc, char **argv)
       if (nc_close(ncid)) ERR;
    }
    SUMMARIZE_ERR;
+   printf("**** testing scalar big endian vars...");
+   {
+       int ncid, enumid;
+       int bigid, littleid;
+       int endian_in;
+       /* Note: if no zero valued enum, then causes ncdump error */
+       int econst0 = 0;
+       int econst1 = 1;
+
+       if (nc_create(FILE_NAME, NC_NETCDF4|NC_CLOBBER, &ncid)) ERR;
+
+       if (nc_def_enum(ncid, NC_INT, "enum_t", &enumid)) ERR;
+       if (nc_insert_enum(ncid, enumid, "econst0", &econst0)) ERR;
+       if (nc_insert_enum(ncid, enumid, "econst1", &econst1)) ERR;
+
+       if (nc_def_var(ncid, "little", enumid, 0, NULL, &littleid)) ERR;
+       if (nc_def_var(ncid, "big", enumid, 0, NULL, &bigid)) ERR;
+
+       if (nc_def_var_endian(ncid, littleid, NC_ENDIAN_LITTLE) != NC_EINVAL) ERR;
+       if (nc_def_var_endian(ncid, bigid, NC_ENDIAN_BIG) != NC_EINVAL) ERR;
+
+       /* Note that it is important to set endian ness before testing it */
+       if (nc_inq_var_endian(ncid, littleid, &endian_in)) ERR;
+       if (endian_in) ERR;
+       if (nc_inq_var_endian(ncid, bigid, &endian_in)) ERR;
+       if (endian_in) ERR;
+
+       if (nc_close(ncid)) ERR;
+   }
+   SUMMARIZE_ERR;
    FINAL_RESULTS;
 }