/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the LICENSE file, which can be found at the root of the source code       *
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*****************************************************************************
   FILE
   ttypes.cpp - HDF5 C++ testing the general datatype functionality

 ***************************************************************************/
#include <iostream>
using std::cerr;
using std::endl;

#include <string>
#include "H5Cpp.h" // C++ API header file
using namespace H5;

#include "h5test.h"
#include "h5cpputil.h" // C++ utilility header file

/*
 * Define if you want to test alignment code on a machine that doesn't
 * normally require alignment. When set, all native datatypes must be aligned
 * on a byte boundary equal to the data size.
 */
#if 0
#define TEST_ALIGNMENT

/* Alignment test stuff */
#ifdef TEST_ALIGNMENT
#define H5T_FRIEND
#include "H5Tpkg.h"
#endif
#define SET_ALIGNMENT(TYPE, VAL) H5T_NATIVE_##TYPE##_ALIGN_g = MAX(H5T_NATIVE_##TYPE##_ALIGN_g, VAL)
#endif
/* #include "H5Tpkg.h"
 */

static const char *FILENAME[] = {"dtypes1.h5",       "dtypes2.h5",           "dtypes3.h5", "dtypes4.h5",
                                 "encode_decode.h5", "h5_type_operators.h5", NULL};

typedef enum flt_t { FLT_FLOAT, FLT_DOUBLE, FLT_LDOUBLE, FLT_OTHER } flt_t;

typedef enum int_t {
    INT_CHAR,
    INT_UCHAR,
    INT_SHORT,
    INT_USHORT,
    INT_INT,
    INT_UINT,
    INT_LONG,
    INT_ULONG,
    INT_LLONG,
    INT_ULLONG,
    INT_OTHER
} int_t;

typedef struct {
    int    a;
    float  b;
    long   c;
    double d;
} src_typ_t;

/*-------------------------------------------------------------------------
 * Function:    test_classes
 *
 * Purpose      Test type classes
 *
 * Return       None.
 *-------------------------------------------------------------------------
 */
static void
test_classes()
{
    SUBTEST("PredType::getClass()");
    try {
        // maybe later, int curr_nerrors = GetTestNumErrs();

        // PredType::NATIVE_INT should be in H5T_INTEGER class
        H5T_class_t tcls = PredType::NATIVE_INT.getClass();
        if (H5T_INTEGER != tcls) {
            puts("    Invalid type class for H5T_NATIVE_INT");
        }

        // PredType::NATIVE_DOUBLE should be in H5T_FLOAT class
        tcls = PredType::NATIVE_DOUBLE.getClass();
        if (H5T_FLOAT != tcls) {
            verify_val(static_cast<long>(tcls), static_cast<long>(H5T_FLOAT),
                       "test_class: invalid type class for NATIVE_DOUBLE -", __LINE__, __FILE__);
        }
        PASSED();
    } // end of try block
    catch (Exception &E) {
        issue_fail_msg("test_classes", __LINE__, __FILE__, E.getCDetailMsg());
    }
}

/*-------------------------------------------------------------------------
 * Function:    test_copy
 *
 * Purpose      Test datatype copy functionality
 *
 * Return       None
 *-------------------------------------------------------------------------
 */
static void
test_copy()
{

    SUBTEST("DataType::copy() and DataType::operator=");
    try {
        // Test copying from a predefined datatype using DataType::operator=
        DataType assigned_type;
        assigned_type = PredType::NATIVE_SHORT;

        // Test copying a predefined type using DataType::copy
        DataType copied_type;
        copied_type.copy(PredType::STD_B8LE);

        // Test copying a user-defined type using DataType::operator=
        DataType assigned_usertype;
        assigned_usertype = copied_type;

        // Test copying from a user-defined datatype using DataType::copy
        DataType copied_usertype;
        copied_usertype.copy(copied_type);

        // Test copying a user-defined int type using DataType::operator=
        IntType  orig_int(PredType::STD_B8LE);
        DataType generic_type;
        generic_type = orig_int;

        // Test copying an integer predefined type
        IntType new_int_type(PredType::STD_B8LE);

        // Test copying an int predefined type using DataType::operator=
        IntType another_int_type;
        another_int_type = new_int_type;

        PASSED();
    }
    catch (Exception &E) {
        issue_fail_msg("test_copy", __LINE__, __FILE__, E.getCDetailMsg());
    }
}

/*-------------------------------------------------------------------------
 * Function:    test_detect_type_class
 *
 * Purpose      Test DataType::detectClass()
 *
 * Return       None
 *-------------------------------------------------------------------------
 */
typedef struct { /* Struct with atomic fields */
    int    i;
    float  f;
    char   c;
    double d;
    short  s;
} atomic_typ_t;

typedef struct { /* Struct with complex fields */
    hobj_ref_t arr_r[3][3];
    int        i;
    hvl_t      vl_f;
    hvl_t      vl_s;
    char       c;
    short      s;
} complex_typ_t;

static void
test_detect_type_class()
{

    SUBTEST("DataType::detectClass()");
    try {
        bool in_class = false; // indicates whether a datatype is in a class

        /*
         * Test class of some atomic types.
         */

        // Native integers should be in the integer class
        in_class = DataType::detectClass(PredType::NATIVE_INT, H5T_INTEGER);
        verify_val(in_class, true, "DataType::detectClass() with H5T_INTEGER", __LINE__, __FILE__);

        // Native integers should _not_ be in other classes
        in_class = DataType::detectClass(PredType::NATIVE_INT, H5T_FLOAT);
        verify_val(in_class, false, "DataType::detectClass() with H5T_FLOAT", __LINE__, __FILE__);
        in_class = DataType::detectClass(PredType::NATIVE_INT, H5T_ARRAY);
        verify_val(in_class, false, "DataType::detectClass() with H5T_ARRAY", __LINE__, __FILE__);
        in_class = DataType::detectClass(PredType::NATIVE_INT, H5T_ENUM);
        verify_val(in_class, false, "DataType::detectClass() with H5T_ENUM", __LINE__, __FILE__);

        /*
         *  Test class of a compound type with some atomic types as fields.
         */

        // Create a compound datatype and insert some atomic types
        CompType atom_cmpd(sizeof(atomic_typ_t));
        atom_cmpd.insertMember("i", HOFFSET(atomic_typ_t, i), PredType::NATIVE_INT);
        atom_cmpd.insertMember("f", HOFFSET(atomic_typ_t, f), PredType::NATIVE_FLOAT);
        atom_cmpd.insertMember("c", HOFFSET(atomic_typ_t, c), PredType::NATIVE_CHAR);
        atom_cmpd.insertMember("d", HOFFSET(atomic_typ_t, d), PredType::NATIVE_DOUBLE);
        atom_cmpd.insertMember("s", HOFFSET(atomic_typ_t, s), PredType::NATIVE_SHORT);

        // Make certain that atom_cmpd is a compound type,
        in_class = atom_cmpd.detectClass(H5T_COMPOUND);
        verify_val(in_class, true, "CompType::detectClass() with H5T_COMPOUND", __LINE__, __FILE__);
        // and that it contains a field of type integer
        in_class = atom_cmpd.detectClass(H5T_INTEGER);
        verify_val(in_class, true, "CompType::detectClass() with H5T_INTEGER", __LINE__, __FILE__);
        // and a field of type float,
        in_class = atom_cmpd.detectClass(H5T_FLOAT);
        verify_val(in_class, true, "CompType::detectClass() with H5T_FLOAT", __LINE__, __FILE__);
        // and that it doesn't contain any field of variable-length
        in_class = atom_cmpd.detectClass(H5T_VLEN);
        verify_val(in_class, false, "CompType::detectClass() with H5T_VLEN", __LINE__, __FILE__);

        /*
         *  Test class of array datatype
         */

        // Create an array datatype with an atomic base type
        int       rank    = 2;      // Rank for array datatype
        hsize_t   dims[2] = {3, 3}; // Dimensions for array datatype
        ArrayType atom_arr(PredType::STD_REF_OBJ, rank, dims);

        // Make certain that the correct classes can be detected
        in_class = atom_arr.detectClass(H5T_ARRAY);
        verify_val(in_class, true, "CompType::detectClass() with H5T_ARRAY", __LINE__, __FILE__);
        in_class = atom_arr.detectClass(H5T_REFERENCE);
        verify_val(in_class, true, "CompType::detectClass() with H5T_REFERENCE", __LINE__, __FILE__);

        // Make certain that an incorrect class is not detected
        in_class = atom_arr.detectClass(H5T_VLEN);
        verify_val(in_class, false, "CompType::detectClass() with H5T_VLEN", __LINE__, __FILE__);
        in_class = atom_arr.detectClass(H5T_FLOAT);
        verify_val(in_class, false, "CompType::detectClass() with H5T_FLOAT", __LINE__, __FILE__);
        in_class = atom_arr.detectClass(H5T_INTEGER);
        verify_val(in_class, false, "CompType::detectClass() with H5T_INTEGER", __LINE__, __FILE__);

        /*
         *  Test class of VL datatype
         */

        // Create a VL datatype with an atomic base type of float
        VarLenType atom_vlf(PredType::NATIVE_FLOAT);

        // Make certain that the correct classes can be detected
        in_class = atom_vlf.detectClass(H5T_VLEN);
        verify_val(in_class, true, "CompType::detectClass() with H5T_VLEN", __LINE__, __FILE__);
        in_class = atom_vlf.detectClass(H5T_FLOAT);
        verify_val(in_class, true, "CompType::detectClass() with H5T_FLOAT", __LINE__, __FILE__);

        // Make certain that an incorrect class is not detected
        in_class = atom_vlf.detectClass(H5T_COMPOUND);
        verify_val(in_class, false, "CompType::detectClass() with H5T_COMPOUND", __LINE__, __FILE__);
        in_class = atom_vlf.detectClass(H5T_INTEGER);
        verify_val(in_class, false, "CompType::detectClass() with H5T_INTEGER", __LINE__, __FILE__);

        /*
         *  Test class of VL datatype
         */

        // Create a VL datatype with an atomic base type of char.  It should be a VL
        // but not a string class.
        VarLenType atom_vlc(PredType::NATIVE_CHAR);

        // Make certain that the correct classes can be detected
        in_class = atom_vlc.detectClass(H5T_VLEN);
        verify_val(in_class, true, "CompType::detectClass() with H5T_VLEN", __LINE__, __FILE__);
        in_class = atom_vlc.detectClass(H5T_INTEGER);
        verify_val(in_class, true, "CompType::detectClass() with H5T_INTEGER", __LINE__, __FILE__);

        // Make certain that an incorrect class is not detected
        in_class = atom_vlc.detectClass(H5T_STRING);
        verify_val(in_class, false, "CompType::detectClass() with H5T_STRING", __LINE__, __FILE__);

        /*
         *  Test class of VL string datatype
         */

        // Create a VL string.  It should be a string, not a VL class.
        StrType atom_vls(0, H5T_VARIABLE);

        // Make certain that the correct classes can be detected
        in_class = atom_vls.detectClass(H5T_STRING);
        verify_val(in_class, true, "CompType::detectClass() with H5T_STRING", __LINE__, __FILE__);

        // Make certain that an incorrect class is not detected
        in_class = atom_vls.detectClass(H5T_VLEN);
        verify_val(in_class, false, "CompType::detectClass() with H5T_VLEN", __LINE__, __FILE__);

        /*
         *  Test class of a compound type with some complex types as fields.
         */

        // Create a compound datatype with complex type fields
        CompType cplx_cmpd(sizeof(complex_typ_t));
        cplx_cmpd.insertMember("arr_r", HOFFSET(complex_typ_t, arr_r), atom_arr);
        cplx_cmpd.insertMember("i", HOFFSET(complex_typ_t, i), PredType::NATIVE_INT);
        cplx_cmpd.insertMember("vl_f", HOFFSET(complex_typ_t, vl_f), atom_vlf);
        cplx_cmpd.insertMember("vl_s", HOFFSET(complex_typ_t, vl_s), atom_vls);
        cplx_cmpd.insertMember("c", HOFFSET(complex_typ_t, c), PredType::NATIVE_CHAR);
        cplx_cmpd.insertMember("s", HOFFSET(complex_typ_t, s), PredType::NATIVE_SHORT);

        // Make certain that the correct classes can be detected
        in_class = cplx_cmpd.detectClass(H5T_COMPOUND);
        verify_val(in_class, true, "CompType::detectClass() with H5T_COMPOUND", __LINE__, __FILE__);
        in_class = cplx_cmpd.detectClass(H5T_ARRAY);
        verify_val(in_class, true, "CompType::detectClass() with H5T_ARRAY", __LINE__, __FILE__);
        in_class = cplx_cmpd.detectClass(H5T_REFERENCE);
        verify_val(in_class, true, "CompType::detectClass() with H5T_REFERENCE", __LINE__, __FILE__);
        in_class = cplx_cmpd.detectClass(H5T_INTEGER);
        verify_val(in_class, true, "CompType::detectClass() with H5T_INTEGER", __LINE__, __FILE__);
        in_class = cplx_cmpd.detectClass(H5T_FLOAT);
        verify_val(in_class, true, "CompType::detectClass() with H5T_FLOAT", __LINE__, __FILE__);
        in_class = cplx_cmpd.detectClass(H5T_STRING);
        verify_val(in_class, true, "CompType::detectClass() with H5T_STRING", __LINE__, __FILE__);
        in_class = cplx_cmpd.detectClass(H5T_VLEN);
        verify_val(in_class, true, "CompType::detectClass() with H5T_VLEN", __LINE__, __FILE__);

        // Make certain that an incorrect class is not detected
        in_class = cplx_cmpd.detectClass(H5T_TIME);
        verify_val(in_class, false, "CompType::detectClass() with H5T_TIME", __LINE__, __FILE__);
        in_class = cplx_cmpd.detectClass(H5T_ENUM);
        verify_val(in_class, false, "CompType::detectClass() with H5T_ENUM", __LINE__, __FILE__);

        PASSED();
    }
    catch (Exception &E) {
        issue_fail_msg("test_detect_type_class", __LINE__, __FILE__, E.getCDetailMsg());
    }
}

/*-------------------------------------------------------------------------
 * Function:    test_vltype
 *
 * Purpose      Tests VarLenType class
 *
 * Return       None
 *-------------------------------------------------------------------------
 */
static void
test_vltype()
{
    // Output message about test being performed
    SUBTEST("VarLenType functions");
    try {
        VarLenType vltype(PredType::NATIVE_INT);

        bool in_class = vltype.detectClass(H5T_VLEN);
        verify_val(in_class, true, "VarLenType::detectClass() with H5T_VLEN", __LINE__, __FILE__);
        in_class = vltype.detectClass(H5T_INTEGER);
        verify_val(in_class, true, "VarLenType::detectClass() with H5T_INTEGER", __LINE__, __FILE__);

        // Test copy constructor
        VarLenType vltype2(vltype);

        // Verify that the copied type has a valid id
        bool is_valid = IdComponent::isValid(vltype2.getId());
        verify_val(is_valid, true, "isValid on vltype2", __LINE__, __FILE__);

        in_class = vltype2.detectClass(H5T_VLEN);
        verify_val(in_class, true, "VarLenType::detectClass() with H5T_VLEN for vltype2", __LINE__, __FILE__);
        in_class = vltype2.detectClass(H5T_INTEGER);
        verify_val(in_class, true, "VarLenType::detectClass() with H5T_INTEGER for vltype2", __LINE__,
                   __FILE__);
        in_class = vltype2.detectClass(H5T_FLOAT);
        verify_val(in_class, false, "VarLenType::detectClass() with H5T_FLOAT for vltype2", __LINE__,
                   __FILE__);

        // Create a new file to use in this test
        H5File file(FILENAME[3], H5F_ACC_TRUNC);

        // Create a group in the file, to hold some varlentype
        Group top_group(file.createGroup("top group"));

        // Create a variable-length type
        VarLenType first_vlt(PredType::NATIVE_FLOAT);

        // Commit the type to the group
        first_vlt.commit(top_group, "first variable-length type");

        // Close it
        first_vlt.close();

        // Reopen it
        VarLenType first_vlt_again(top_group, "first variable-length type");

        // Trying to detect H5T_VLEN and H5T_FLOAT classes on this type,
        // should both be true
        in_class = vltype2.detectClass(H5T_VLEN);
        verify_val(in_class, true, "VarLenType::detectClass() with H5T_VLEN for vltype2", __LINE__, __FILE__);
        in_class = first_vlt_again.detectClass(H5T_FLOAT);
        verify_val(in_class, true, "VarLenType::detectClass() with H5T_FLOAT for first_vlt_again", __LINE__,
                   __FILE__);

        PASSED();
    } // end of try block
    catch (Exception &E) {
        issue_fail_msg("test_vltype", __LINE__, __FILE__, E.getCDetailMsg());
    }
} // test_vltype

/*-------------------------------------------------------------------------
 * Function:    test_query
 *
 * Purpose      Tests query functions of compound and enumeration types.
 *
 * Return       None
 *-------------------------------------------------------------------------
 */
const H5std_string CompT_NAME("Compound_type");
const H5std_string EnumT_NAME("Enum_type");

static void
test_query()
{
    short enum_val;

    // Output message about test being performed
    SUBTEST("Query functions of compound and enumeration types");
    try {
        // Create File
        H5File file(FILENAME[2], H5F_ACC_TRUNC);

        // Create a compound datatype
        CompType tid1(sizeof(src_typ_t));

        tid1.insertMember("a", HOFFSET(src_typ_t, a), PredType::NATIVE_INT);
        tid1.insertMember("b", HOFFSET(src_typ_t, b), PredType::NATIVE_FLOAT);
        tid1.insertMember("c", HOFFSET(src_typ_t, c), PredType::NATIVE_LONG);
        tid1.insertMember("d", HOFFSET(src_typ_t, d), PredType::NATIVE_DOUBLE);

        // Create a enumerate datatype
        EnumType tid2(sizeof(short));

        enum_val = 0;
        tid2.insert("RED", &enum_val);
        enum_val = 1;
        tid2.insert("GREEN", &enum_val);
        enum_val = 2;
        tid2.insert("BLUE", &enum_val);
        enum_val = 3;
        tid2.insert("ORANGE", &enum_val);
        enum_val = 4;
        tid2.insert("YELLOW", &enum_val);

        // Query member number and member index by name, for compound type
        int nmembs = tid1.getNmembers();
        verify_val(nmembs, 4, "CompType::getNmembers()", __LINE__, __FILE__);

        int index = tid1.getMemberIndex("c");
        verify_val(index, 2, "CompType::getMemberIndex()", __LINE__, __FILE__);

        // Query member number and member index by name, for enumeration type.
        nmembs = tid2.getNmembers();
        verify_val(nmembs, 5, "EnumType::getNmembers()", __LINE__, __FILE__);

        index = tid2.getMemberIndex("ORANGE");
        verify_val(index, 3, "EnumType::getMemberIndex()", __LINE__, __FILE__);

        // Commit compound datatype, and test getting the datatype creation
        // prop list, then close it
        tid1.commit(file, CompT_NAME);
        PropList tcpl = tid1.getCreatePlist();
        if (!IdComponent::isValid(tcpl.getId())) {
            // Throw an invalid action exception
            throw InvalidActionException("H5Object::createAttribute",
                                         "Datatype creation property list is not valid");
        }
        tcpl.close();
        tid1.close();

        // Commit enumeration datatype, and test getting the datatype creation
        // prop list, then close it
        tid2.commit(file, EnumT_NAME);
        tcpl = tid2.getCreatePlist();
        if (!IdComponent::isValid(tcpl.getId())) {
            // Throw an invalid action exception
            throw InvalidActionException("H5Object::createAttribute",
                                         "Datatype creation property list is not valid");
        }
        tcpl.close();
        tid2.close();

        // Open the datatypes for query.  Testing both ways

        tid1 = file.openCompType(CompT_NAME);
        tid1.close();
        tid2 = file.openEnumType(EnumT_NAME);
        tid2.close();

        CompType comptype(file, CompT_NAME);
        EnumType enumtype(file, EnumT_NAME);

        // Query member number and member index by name, for compound type
        nmembs = comptype.getNmembers();
        verify_val(nmembs, 4, "CompType::getNmembers()", __LINE__, __FILE__);
        index = comptype.getMemberIndex("c");
        verify_val(index, 2, "CompType::getMemberIndex()", __LINE__, __FILE__);

        // Query member number and member index by name, for enumeration type
        nmembs = enumtype.getNmembers();
        verify_val(nmembs, 5, "EnumType::getNmembers()", __LINE__, __FILE__);
        index = enumtype.getMemberIndex("ORANGE");
        verify_val(index, 3, "EnumType::getMemberIndex()", __LINE__, __FILE__);

        // Close datatypes and file
        comptype.close();
        enumtype.close();
        file.close();

        // Try truncating the file to make sure reference counting is good.
        // If any references to ids of the accessed types are left unterminated,
        // the truncating will fail, because the file will not be closed in
        // the file.close() above.
        H5File file1(FILENAME[2], H5F_ACC_TRUNC);

        PASSED();
    } // end of try block
    catch (Exception &E) {
        issue_fail_msg("test_query", __LINE__, __FILE__, E.getCDetailMsg());
    }
} // test_query

/*-------------------------------------------------------------------------
 * Function:    test_transient
 *
 * Purpose      Tests transient datatypes.
 *
 * Return       None
 *-------------------------------------------------------------------------
 */

static void
test_transient()
{
    static hsize_t ds_size[2] = {10, 20};

    SUBTEST("Transient datatypes");
    try {

        // Create the file and the dataspace.
        H5File    file(FILENAME[0], H5F_ACC_TRUNC);
        DataSpace space(2, ds_size, ds_size);

        // Copying a predefined type results in a modifiable copy
        IntType type(PredType::NATIVE_INT);
        type.setPrecision(256);

        // It should not be possible to create an attribute for a transient type
        try {
            Attribute attr(type.createAttribute("attr1", PredType::NATIVE_INT, space));
            // Should FAIL but didn't, so throw an invalid action exception
            throw InvalidActionException("H5Object::createAttribute",
                                         "Attempted to commit a predefined datatype.");
        }
        catch (AttributeIException &err) {
        } // do nothing, failure expected

        // Create a dataset from a transient datatype
        // type.close(); - put trace in H5Tclose to make sure it's closed
        type.copy(PredType::NATIVE_INT);
        DataSet dset(file.createDataSet("dset1", type, space));

        // The type returned from a dataset should not be modifiable
        IntType itype(dset);
        try {
            itype.setPrecision(256);

            // Should FAIL but didn't, so throw an invalid action exception
            throw InvalidActionException("PredType::setPrecision",
                                         "Dataset datatypes should not be modifiable!");
        }
        catch (DataTypeIException &err) {
        }
        itype.close();

        // Get a copy of the dataset's datatype by applying DataType::copy()
        // to the dataset. The resulted datatype should be modifiable.
        itype.copy(dset);
        itype.setPrecision(256);
        itype.close();

        // Close the dataset and reopen it, testing that its type is still
        // read-only.  (Note that a copy of it is modifiable.)
        dset.close();
        dset = file.openDataSet("dset1");

        // Close objects and file.
        dset.close();
        file.close();
        type.close();
        space.close();
        PASSED();
    } // end of try block
    catch (Exception &E) {
        issue_fail_msg("test_transient", __LINE__, __FILE__, E.getCDetailMsg());
    }
} // test_transient

/*-------------------------------------------------------------------------
 * Function:    test_named
 *
 * Purpose      Tests named datatypes.
 *
 * Return       None
 *-------------------------------------------------------------------------
 */

static void
test_named()
{
    static hsize_t ds_size[2] = {10, 20};
    unsigned       attr_data[10][20];
    DataType      *ds_type = NULL;

    SUBTEST("Named datatypes");
    try {
        // Create the file.
        H5File file(FILENAME[1], H5F_ACC_TRUNC);

        // Create a simple dataspace.
        DataSpace space(2, ds_size, ds_size);

        // Predefined types cannot be committed.
        try {
            PredType nativeint(PredType::NATIVE_INT);
            nativeint.commit(file, "test_named_1 (should not exist)");

            // Should FAIL but didn't, so throw an invalid action exception
            throw InvalidActionException("PredType::commit", "Attempted to commit a predefined datatype.");
        }
        catch (DataTypeIException &err) {
        }

        // Copy a predefined datatype and commit the copy.
        IntType itype(PredType::NATIVE_INT);
        itype.commit(file, "native-int");

        // Test commit passing in const H5File& for prototype with const
        try {
            // Create random char type
            IntType atype(PredType::NATIVE_UCHAR);

            // Creating group, declared as const
            const Group const_grp = file.createGroup("GR as loc");

            // Commit type passing in const group; compilation would fail if
            // no matching prototype
            atype.commit(const_grp, "random uchar");
        } // end of try block
        catch (Exception &E) {
            issue_fail_msg("test_named", __LINE__, __FILE__, "Commit at const group");
        }

        // Check that it is committed.
        if (!itype.committed())
            cerr << "IntType::committed() returned false" << endl;

        // We should not be able to modify a type after it has been committed.
        try {
            itype.setPrecision(256); // attempt an invalid action...

            // Should FAIL but didn't, so throw an invalid action exception
            throw InvalidActionException("IntType::setPrecision",
                                         "Attempted to modify a committed datatype.");
        }
        catch (DataTypeIException &err) {
        }

        // We should not be able to re-commit a committed type
        try {
            itype.commit(file, "test_named_2 (should not exist)");

            // Should FAIL but didn't, so throw an invalid action exception
            throw InvalidActionException("IntType::commit", "Attempted to re-commit a committed datatype.");
        }
        catch (DataTypeIException &err) {
        } // do nothing, failure expected

        // It should be possible to define an attribute for the named type
        Attribute attr1 = itype.createAttribute("attr1", PredType::NATIVE_UCHAR, space);
        for (hsize_t i = 0; i < ds_size[0]; i++) {
            for (hsize_t j = 0; j < ds_size[1]; j++) {
                attr_data[i][j] = static_cast<unsigned>(i * ds_size[1] + j);
            }
        }
        attr1.write(PredType::NATIVE_UINT, attr_data);
        attr1.close();

        // Copying a committed type should result in a transient type which is
        // not locked.
        IntType trans_type;
        trans_type.copy(itype);
        bool iscommitted = trans_type.committed();
        verify_val(iscommitted, 0,
                   "DataType::committed() - Copying a named type should result in a transient type!",
                   __LINE__, __FILE__);
        trans_type.setPrecision(256);
        trans_type.close();

        // Close the committed type and reopen it.  It should be a named type.
        itype.close();
        itype       = file.openIntType("native-int");
        iscommitted = itype.committed();
        if (!iscommitted)
            throw InvalidActionException("IntType::committed()", "Opened named types should be named types!");

        // Create a dataset that uses the named type, then get the dataset's
        // datatype and make sure it's a named type.
        DataSet dset = file.createDataSet("dset1", itype, space);
        ds_type      = new DataType(dset.getDataType());
        iscommitted  = ds_type->committed();
        if (!iscommitted)
            throw InvalidActionException("IntType::committed()", "Dataset type should be named type!");
        dset.close();
        ds_type->close();
        delete ds_type;

        // Reopen the dataset and its type, then make sure the type is
        // a named type.
        dset        = file.openDataSet("dset1");
        ds_type     = new DataType(dset.getDataType());
        iscommitted = ds_type->committed();
        if (!iscommitted)
            throw InvalidActionException("IntType::committed()", "Dataset type should be named type!");

        // Close the dataset and create another with the type returned from
        // the first dataset.
        dset.close();
        dset = file.createDataSet("dset2", *ds_type, space);
        ds_type->close();
        dset.close();
        delete ds_type;

        // Reopen the second dataset and make sure the type is shared
        dset        = file.openDataSet("dset2");
        ds_type     = new DataType(dset.getDataType());
        iscommitted = ds_type->committed();
        if (!iscommitted)
            throw InvalidActionException("DataType::iscommitted()", "Dataset type should be named type!");
        ds_type->close();

        // Get the dataset datatype by applying DataType::copy() to the
        // dataset. The resulted datatype should be modifiable.
        IntType copied_type;
        copied_type.copy(dset);
        copied_type.setPrecision(256);
        copied_type.close();

        // Clean up
        dset.close();
        itype.close();
        space.close();
        file.close();
        PASSED();
    } // end of try block
    catch (Exception &E) {
        issue_fail_msg("test_named", __LINE__, __FILE__, E.getCDetailMsg());
    }

    delete ds_type;
} // test_named

/*-------------------------------------------------------------------------
 * Function:    test_encode_decode
 *
 * Purpose      Test datatype encode/decode functionality.
 *
 * Return       None
 *-------------------------------------------------------------------------
 */
const int ARRAY1_RANK = 1;
const int ARRAY1_DIM  = 10;

static void
test_encode_decode()
{
    short enum_val;

    SUBTEST("DataType::encode() and DataType::decode()");
    try {
        // Create the file.
        H5File file(FILENAME[4], H5F_ACC_TRUNC);

        //
        // Test with CompType
        //

        // Create a compound datatype
        CompType cmptyp(sizeof(src_typ_t));

        cmptyp.insertMember("a", HOFFSET(src_typ_t, a), PredType::NATIVE_INT);
        cmptyp.insertMember("b", HOFFSET(src_typ_t, b), PredType::NATIVE_FLOAT);
        cmptyp.insertMember("c", HOFFSET(src_typ_t, c), PredType::NATIVE_LONG);
        cmptyp.insertMember("d", HOFFSET(src_typ_t, d), PredType::NATIVE_DOUBLE);

        // Encode compound type in its buffer
        cmptyp.encode();

        // Verify that encoding had been done
        verify_val(cmptyp.hasBinaryDesc(), true, "DataType::encode", __LINE__, __FILE__);

        // Decode compound type's buffer to a new CompType
        CompType *decoded_cmp_ptr(static_cast<CompType *>(cmptyp.decode()));

        // Verify that the datatype was copied exactly via encoding/decoding
        verify_val(cmptyp == *decoded_cmp_ptr, true, "DataType::decode", __LINE__, __FILE__);

        // Verify again via querying member number and member index by name.
        verify_val(decoded_cmp_ptr->getNmembers(), 4, "DataType::decode", __LINE__, __FILE__);
        verify_val(decoded_cmp_ptr->getMemberIndex("c"), 2, "DataType::decode", __LINE__, __FILE__);

        // Create a CompType instance from the pointer and verify it
        CompType cmptyp_clone(*decoded_cmp_ptr);
        verify_val(cmptyp == cmptyp_clone, true, "DataType::decode", __LINE__, __FILE__);
        verify_val(cmptyp_clone.getNmembers(), 4, "DataType::decode", __LINE__, __FILE__);
        verify_val(cmptyp_clone.getMemberIndex("c"), 2, "DataType::decode", __LINE__, __FILE__);

        delete decoded_cmp_ptr;

        //
        // Test with EnumType
        //

        // Create a enumerate datatype
        EnumType enumtyp(sizeof(short));

        enum_val = 0;
        enumtyp.insert("RED", &enum_val);
        enum_val = 1;
        enumtyp.insert("GREEN", &enum_val);
        enum_val = 2;
        enumtyp.insert("BLUE", &enum_val);
        enum_val = 3;
        enumtyp.insert("ORANGE", &enum_val);
        enum_val = 4;
        enumtyp.insert("YELLOW", &enum_val);

        // Encode compound type in a buffer
        enumtyp.encode();

        // Verify that encoding had been done
        verify_val(enumtyp.hasBinaryDesc(), true, "DataType::encode", __LINE__, __FILE__);

        // Decode enumeration type's buffer to a new EnumType
        EnumType *decoded_enum_ptr(static_cast<EnumType *>(enumtyp.decode()));

        // Verify that the datatype was copied exactly via encoding/decoding
        verify_val(enumtyp == *decoded_enum_ptr, true, "DataType::decode", __LINE__, __FILE__);

        // Verify again via querying member number and member index by name.
        verify_val(decoded_enum_ptr->getNmembers(), 5, "DataType::decode", __LINE__, __FILE__);
        verify_val(decoded_enum_ptr->getMemberIndex("GREEN"), 1, "DataType::decode", __LINE__, __FILE__);

        // Create a EnumType instance from the pointer and verify it
        EnumType enumtyp_clone(*decoded_enum_ptr);
        verify_val(enumtyp == enumtyp_clone, true, "DataType::decode", __LINE__, __FILE__);
        verify_val(enumtyp_clone.getNmembers(), 5, "DataType::decode", __LINE__, __FILE__);
        verify_val(enumtyp_clone.getMemberIndex("GREEN"), 1, "DataType::decode", __LINE__, __FILE__);

        delete decoded_enum_ptr;

        //
        // Test with variable-length string
        //

        // Create a variable-length string type
        StrType vlsttyp(PredType::C_S1);
        vlsttyp.setSize(H5T_VARIABLE);

        // Encode the variable-length type in its buffer
        vlsttyp.encode();

        // Verify that encoding had been done
        verify_val(vlsttyp.hasBinaryDesc(), true, "DataType::encode", __LINE__, __FILE__);

        // Decode the variable-length type's buffer to a new StrType
        StrType *decoded_str_ptr(static_cast<StrType *>(vlsttyp.decode()));

        verify_val(vlsttyp == *decoded_str_ptr, true, "DataType::decode", __LINE__, __FILE__);
        verify_val(decoded_str_ptr->isVariableStr(), true, "DataType::decode", __LINE__, __FILE__);

        delete decoded_str_ptr;

        // Test decoding the type by way of DataType*

        // Decode variable-length string type to a new DataType
        DataType *decoded_vlstr_ptr(vlsttyp.decode());

        // Create a StrType instance from the DataType object and verify it
        StrType decoded_vlsttyp(decoded_vlstr_ptr->getId());
        verify_val(vlsttyp == decoded_vlsttyp, true, "DataType::decode", __LINE__, __FILE__);
        verify_val(decoded_vlsttyp.isVariableStr(), true, "DataType::decode", __LINE__, __FILE__);

        delete decoded_vlstr_ptr;

        //
        // Test with ArrayType
        //

        hsize_t tdims1[] = {ARRAY1_DIM};

        // Create an array datatype of the compound datatype
        ArrayType arrtyp(cmptyp, ARRAY1_RANK, tdims1);

        // Encode the array type in its buffer
        arrtyp.encode();

        // Verify that encoding had been done
        verify_val(arrtyp.hasBinaryDesc(), true, "DataType::encode", __LINE__, __FILE__);

        // Create an ArrayType instance from the decoded pointer and verify it
        ArrayType *decoded_arr_ptr(static_cast<ArrayType *>(arrtyp.decode()));

        verify_val(arrtyp == *decoded_arr_ptr, true, "DataType::decode", __LINE__, __FILE__);

        delete decoded_arr_ptr;

        // Test decoding the type by way of DataType*

        // Decode the array type's buffer
        DataType *decoded_dt_ptr = arrtyp.decode();

        // Create a ArrayType instance from the decoded pointer and verify it
        ArrayType decoded_arrtyp(decoded_dt_ptr->getId());
        verify_val(arrtyp == decoded_arrtyp, true, "DataType::decode", __LINE__, __FILE__);
        verify_val(decoded_arrtyp.getArrayNDims(), ARRAY1_RANK, "DataType::decode", __LINE__, __FILE__);

        delete decoded_dt_ptr;

        //
        // Test with IntType
        //

        // Create an int datatype
        IntType inttyp(PredType::NATIVE_UINT);

        // Encode the array type in its buffer
        inttyp.encode();

        // Verify that encoding had been done
        verify_val(inttyp.hasBinaryDesc(), true, "DataType::encode", __LINE__, __FILE__);

        // Create an IntType instance from the decoded pointer and verify it
        IntType   *decoded_int_ptr(static_cast<IntType *>(inttyp.decode()));
        H5T_sign_t int_sign = decoded_int_ptr->getSign();
        verify_val(static_cast<long>(int_sign), static_cast<long>(H5T_SGN_NONE), "DataType::decode", __LINE__,
                   __FILE__);
        verify_val(inttyp == *decoded_int_ptr, true, "DataType::decode", __LINE__, __FILE__);

        delete decoded_int_ptr;

        //
        // Test decoding FloatType by way of DataType*
        //

        // Create a float datatype
        FloatType flttyp(PredType::NATIVE_FLOAT);

        // Encode the float type in its buffer
        flttyp.encode();

        // Verify that encoding had been done
        verify_val(flttyp.hasBinaryDesc(), true, "DataType::encode", __LINE__, __FILE__);

        // Decode the array type's buffer
        DataType *decoded_flt_ptr(flttyp.decode());

        // Create a IntType instance from the decoded pointer and verify it
        FloatType decoded_flttyp(decoded_flt_ptr->getId());
        verify_val(flttyp == decoded_flttyp, true, "DataType::decode", __LINE__, __FILE__);

        // H5std_string norm_string;
        // H5T_norm_t mant_norm = decoded_flttyp.getNorm(norm_string);
        // verify_val(decoded_flttyp.isVariableStr(), true, "DataType::decode", __LINE__, __FILE__);

        delete decoded_flt_ptr;

        PASSED();
    }
    catch (Exception &E) {
        issue_fail_msg("test_encode_decode", __LINE__, __FILE__, E.getCDetailMsg());
    }
}

/*-------------------------------------------------------------------------
 * Function:    test_operators
 *
 * Purpose      Test datatype encode/decode functionality.
 *
 * Return       None
 *-------------------------------------------------------------------------
 */

static void
test_operators()
{
    short enum_val;

    SUBTEST("DataType::operator== and DataType::operator!=");
    try {
        // Create the file.
        H5File file(FILENAME[5], H5F_ACC_TRUNC);

        //
        // Test with CompType
        //

        // Create a compound datatype
        CompType cmptyp(sizeof(src_typ_t));

        cmptyp.insertMember("a", HOFFSET(src_typ_t, a), PredType::NATIVE_INT);
        cmptyp.insertMember("b", HOFFSET(src_typ_t, b), PredType::NATIVE_FLOAT);
        cmptyp.insertMember("c", HOFFSET(src_typ_t, c), PredType::NATIVE_LONG);
        cmptyp.insertMember("d", HOFFSET(src_typ_t, d), PredType::NATIVE_DOUBLE);

        // Copy this compound datatype
        CompType clone_cmptyp(cmptyp);

        // Verify that operator== and operator!= work properly
        verify_val(cmptyp == clone_cmptyp, true, "DataType::operator==", __LINE__, __FILE__);
        verify_val(cmptyp != clone_cmptyp, false, "DataType::operator!=", __LINE__, __FILE__);

        //
        // Test with EnumType
        //

        // Create an enumerate datatype
        EnumType enumtyp(sizeof(short));

        enum_val = 0;
        enumtyp.insert("RED", &enum_val);
        enum_val = 1;
        enumtyp.insert("GREEN", &enum_val);
        enum_val = 2;
        enumtyp.insert("BLUE", &enum_val);

        // Verify that operator== and operator!= work properly
        verify_val(cmptyp == enumtyp, false, "DataType::operator==", __LINE__, __FILE__);
        verify_val(cmptyp != enumtyp, true, "DataType::operator!=", __LINE__, __FILE__);

        //
        // Test with compound datatype's member
        //

        // Create random atomic datatypes
        IntType   inttyp(PredType::NATIVE_INT);
        FloatType flttyp(PredType::NATIVE_FLOAT);

        // Get the NATIVE_INT member from the compound datatype above
        IntType member_inttyp = cmptyp.getMemberIntType(0);

        // Test various combinations
        verify_val(inttyp == member_inttyp, true, "DataType::operator==", __LINE__, __FILE__);
        verify_val(flttyp == member_inttyp, false, "DataType::operator==", __LINE__, __FILE__);
        verify_val(flttyp != member_inttyp, true, "DataType::operator==", __LINE__, __FILE__);

        // Get the NATIVE_FLOAT member from the compound datatype above
        IntType member_flttyp = cmptyp.getMemberIntType(1);

        // Test various combinations
        verify_val(inttyp == member_flttyp, false, "DataType::operator==", __LINE__, __FILE__);
        verify_val(flttyp != member_flttyp, false, "DataType::operator==", __LINE__, __FILE__);

        PASSED();
    }
    catch (Exception &E) {
        issue_fail_msg("test_operators", __LINE__, __FILE__, E.getCDetailMsg());
    }
} // test_operators

/*-------------------------------------------------------------------------
 * Function:    test_types
 *
 * Purpose      Main datatypes testing routine
 *
 * Return       None
 *-------------------------------------------------------------------------
 */
extern "C" void
test_types(void *params)
{
    (void)params;

    // Output message about test being performed
    MESSAGE(5, ("Testing Generic Data Types\n"));

    // Test basic datatypes
    test_classes();
    test_copy();
    test_detect_type_class();
    test_vltype();
    test_query();
    test_transient();
    test_named();
    test_encode_decode();
    test_operators();

} // test_types()

/*-------------------------------------------------------------------------
 * Function:    cleanup_types
 *
 * Purpose      Cleanup temporary test files
 *
 * Return       None
 *-------------------------------------------------------------------------
 */
extern "C" void
cleanup_types(void *params)
{
    (void)params;

    if (GetTestCleanup()) {
        for (int i = 0; i < 6; i++)
            HDremove(FILENAME[i]);
    }
} // cleanup_types
