/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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 COPYING 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.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*
 * Module Info: This module contains most of the "core" functionality of
 *      the H5T interface, including the API initialization code, etc.
 *      Many routines that are infrequently used, or are specialized for
 *      one particular datatype class are in another module.
 */

/****************/
/* Module Setup */
/****************/

#include "H5Tmodule.h" /* This source code file is part of the H5T module */

/***********/
/* Headers */
/***********/
#include "H5private.h"   /* Generic Functions                        */
#include "H5ACprivate.h" /* Metadata cache                           */
#include "H5CXprivate.h" /* API Contexts                             */
#include "H5Dprivate.h"  /* Datasets                                 */
#include "H5Eprivate.h"  /* Error handling                           */
#include "H5ESprivate.h" /* Event Sets                               */
#include "H5Fprivate.h"  /* Files                                    */
#include "H5FLprivate.h" /* Free Lists                               */
#include "H5FOprivate.h" /* File objects                             */
#include "H5Gprivate.h"  /* Groups                                   */
#include "H5Iprivate.h"  /* IDs                                      */
#include "H5MMprivate.h" /* Memory management                        */
#include "H5Pprivate.h"  /* Property lists                           */
#include "H5Tpkg.h"      /* Datatypes                                */
#include "H5VLprivate.h" /* Virtual Object Layer                     */
#include "H5VMprivate.h" /* Vectors and arrays                       */

/* Datatype conversion functions */
#include "H5Tconv_integer.h"
#include "H5Tconv_float.h"
#include "H5Tconv_string.h"
#include "H5Tconv_bitfield.h"
#include "H5Tconv_compound.h"
#include "H5Tconv_reference.h"
#include "H5Tconv_enum.h"
#include "H5Tconv_vlen.h"
#include "H5Tconv_array.h"

/****************/
/* Local Macros */
/****************/

#define H5T_ENCODE_VERSION 0

/*
 * The default number of slots allocated in the
 * datatype conversion path table.
 */
#define H5T_DEF_CONV_TABLE_SLOTS 128

/*
 * Type initialization macros
 *
 * These use the "template macro" technique to reduce the amount of gratuitous
 * duplicated code when initializing the datatypes for the library.  The main
 * template macro is the H5T_INIT_TYPE() macro below.
 *
 */

/* Define the code template for bitfields for the "GUTS" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_BITFIELD_CORE                                                                          \
    {                                                                                                        \
        dt->shared->type = H5T_BITFIELD;                                                                     \
    }

#define H5T_INIT_TYPE_BITFIELD_COMMON(ENDIANNESS)                                                            \
    {                                                                                                        \
        H5T_INIT_TYPE_NUM_COMMON(ENDIANNESS)                                                                 \
        H5T_INIT_TYPE_BITFIELD_CORE;                                                                         \
    }

#define H5T_INIT_TYPE_BITFIELDLE_CORE                                                                        \
    {                                                                                                        \
        H5T_INIT_TYPE_BITFIELD_COMMON(H5T_ORDER_LE)                                                          \
    }

#define H5T_INIT_TYPE_BITFIELDBE_CORE                                                                        \
    {                                                                                                        \
        H5T_INIT_TYPE_BITFIELD_COMMON(H5T_ORDER_BE)                                                          \
    }

/* Define the code template for times for the "GUTS" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_TIME_CORE                                                                              \
    {                                                                                                        \
        dt->shared->type = H5T_TIME;                                                                         \
    }

/* Define the code template for types which reset the offset for the "GUTS" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_OFFSET_CORE                                                                            \
    {                                                                                                        \
        dt->shared->u.atomic.offset = 0;                                                                     \
    }

/* Define common code for all numeric types (floating-point & int, signed & unsigned) */
#define H5T_INIT_TYPE_NUM_COMMON(ENDIANNESS)                                                                 \
    {                                                                                                        \
        dt->shared->u.atomic.order   = ENDIANNESS;                                                           \
        dt->shared->u.atomic.offset  = 0;                                                                    \
        dt->shared->u.atomic.lsb_pad = H5T_PAD_ZERO;                                                         \
        dt->shared->u.atomic.msb_pad = H5T_PAD_ZERO;                                                         \
    }

/* Define the code templates for standard 16-bit floats for the "GUTS" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_FLOAT16_COMMON(ENDIANNESS)                                                             \
    {                                                                                                        \
        H5T_INIT_TYPE_NUM_COMMON(ENDIANNESS)                                                                 \
        dt->shared->u.atomic.u.f.sign  = 15;                                                                 \
        dt->shared->u.atomic.u.f.epos  = 10;                                                                 \
        dt->shared->u.atomic.u.f.esize = 5;                                                                  \
        dt->shared->u.atomic.u.f.ebias = 0xf;                                                                \
        dt->shared->u.atomic.u.f.mpos  = 0;                                                                  \
        dt->shared->u.atomic.u.f.msize = 10;                                                                 \
        dt->shared->u.atomic.u.f.norm  = H5T_NORM_IMPLIED;                                                   \
        dt->shared->u.atomic.u.f.pad   = H5T_PAD_ZERO;                                                       \
    }

#define H5T_INIT_TYPE_FLOAT16LE_CORE                                                                         \
    {                                                                                                        \
        H5T_INIT_TYPE_FLOAT16_COMMON(H5T_ORDER_LE)                                                           \
    }

#define H5T_INIT_TYPE_FLOAT16BE_CORE                                                                         \
    {                                                                                                        \
        H5T_INIT_TYPE_FLOAT16_COMMON(H5T_ORDER_BE)                                                           \
    }

/* Define the code templates for standard floats for the "GUTS" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_FLOAT_COMMON(ENDIANNESS)                                                               \
    {                                                                                                        \
        H5T_INIT_TYPE_NUM_COMMON(ENDIANNESS)                                                                 \
        dt->shared->u.atomic.u.f.sign  = 31;                                                                 \
        dt->shared->u.atomic.u.f.epos  = 23;                                                                 \
        dt->shared->u.atomic.u.f.esize = 8;                                                                  \
        dt->shared->u.atomic.u.f.ebias = 0x7f;                                                               \
        dt->shared->u.atomic.u.f.mpos  = 0;                                                                  \
        dt->shared->u.atomic.u.f.msize = 23;                                                                 \
        dt->shared->u.atomic.u.f.norm  = H5T_NORM_IMPLIED;                                                   \
        dt->shared->u.atomic.u.f.pad   = H5T_PAD_ZERO;                                                       \
    }

#define H5T_INIT_TYPE_FLOATLE_CORE                                                                           \
    {                                                                                                        \
        H5T_INIT_TYPE_FLOAT_COMMON(H5T_ORDER_LE)                                                             \
    }

#define H5T_INIT_TYPE_FLOATBE_CORE                                                                           \
    {                                                                                                        \
        H5T_INIT_TYPE_FLOAT_COMMON(H5T_ORDER_BE)                                                             \
    }

/* Define the code templates for standard doubles for the "GUTS" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_DOUBLE_COMMON(ENDIANNESS)                                                              \
    {                                                                                                        \
        H5T_INIT_TYPE_NUM_COMMON(ENDIANNESS)                                                                 \
        dt->shared->u.atomic.u.f.sign  = 63;                                                                 \
        dt->shared->u.atomic.u.f.epos  = 52;                                                                 \
        dt->shared->u.atomic.u.f.esize = 11;                                                                 \
        dt->shared->u.atomic.u.f.ebias = 0x03ff;                                                             \
        dt->shared->u.atomic.u.f.mpos  = 0;                                                                  \
        dt->shared->u.atomic.u.f.msize = 52;                                                                 \
        dt->shared->u.atomic.u.f.norm  = H5T_NORM_IMPLIED;                                                   \
        dt->shared->u.atomic.u.f.pad   = H5T_PAD_ZERO;                                                       \
    }

#define H5T_INIT_TYPE_DOUBLELE_CORE                                                                          \
    {                                                                                                        \
        H5T_INIT_TYPE_DOUBLE_COMMON(H5T_ORDER_LE)                                                            \
    }

#define H5T_INIT_TYPE_DOUBLEBE_CORE                                                                          \
    {                                                                                                        \
        H5T_INIT_TYPE_DOUBLE_COMMON(H5T_ORDER_BE)                                                            \
    }

/* Define the code templates for VAX float for the "GUTS" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_FLOATVAX_CORE                                                                          \
    {                                                                                                        \
        H5T_INIT_TYPE_NUM_COMMON(H5T_ORDER_VAX)                                                              \
        dt->shared->u.atomic.u.f.sign  = 31;                                                                 \
        dt->shared->u.atomic.u.f.epos  = 23;                                                                 \
        dt->shared->u.atomic.u.f.esize = 8;                                                                  \
        dt->shared->u.atomic.u.f.ebias = 0x81;                                                               \
        dt->shared->u.atomic.u.f.mpos  = 0;                                                                  \
        dt->shared->u.atomic.u.f.msize = 23;                                                                 \
        dt->shared->u.atomic.u.f.norm  = H5T_NORM_IMPLIED;                                                   \
        dt->shared->u.atomic.u.f.pad   = H5T_PAD_ZERO;                                                       \
        dt->shared->version            = H5O_DTYPE_VERSION_3;                                                \
    }

/* Define the code templates for VAX double for the "GUTS" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_DOUBLEVAX_CORE                                                                         \
    {                                                                                                        \
        H5T_INIT_TYPE_NUM_COMMON(H5T_ORDER_VAX)                                                              \
        dt->shared->u.atomic.u.f.sign  = 63;                                                                 \
        dt->shared->u.atomic.u.f.epos  = 52;                                                                 \
        dt->shared->u.atomic.u.f.esize = 11;                                                                 \
        dt->shared->u.atomic.u.f.ebias = 0x0401;                                                             \
        dt->shared->u.atomic.u.f.mpos  = 0;                                                                  \
        dt->shared->u.atomic.u.f.msize = 52;                                                                 \
        dt->shared->u.atomic.u.f.norm  = H5T_NORM_IMPLIED;                                                   \
        dt->shared->u.atomic.u.f.pad   = H5T_PAD_ZERO;                                                       \
        dt->shared->version            = H5O_DTYPE_VERSION_3;                                                \
    }

/* Define the code templates for standard signed integers for the "GUTS" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_SINT_COMMON(ENDIANNESS)                                                                \
    {                                                                                                        \
        H5T_INIT_TYPE_NUM_COMMON(ENDIANNESS)                                                                 \
        dt->shared->u.atomic.u.i.sign = H5T_SGN_2;                                                           \
    }

#define H5T_INIT_TYPE_SINTLE_CORE                                                                            \
    {                                                                                                        \
        H5T_INIT_TYPE_SINT_COMMON(H5T_ORDER_LE)                                                              \
    }

#define H5T_INIT_TYPE_SINTBE_CORE                                                                            \
    {                                                                                                        \
        H5T_INIT_TYPE_SINT_COMMON(H5T_ORDER_BE)                                                              \
    }

/* Define the code templates for standard unsigned integers for the "GUTS" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_UINT_COMMON(ENDIANNESS)                                                                \
    {                                                                                                        \
        H5T_INIT_TYPE_NUM_COMMON(ENDIANNESS)                                                                 \
        dt->shared->u.atomic.u.i.sign = H5T_SGN_NONE;                                                        \
    }

#define H5T_INIT_TYPE_UINTLE_CORE                                                                            \
    {                                                                                                        \
        H5T_INIT_TYPE_UINT_COMMON(H5T_ORDER_LE)                                                              \
    }

#define H5T_INIT_TYPE_UINTBE_CORE                                                                            \
    {                                                                                                        \
        H5T_INIT_TYPE_UINT_COMMON(H5T_ORDER_BE)                                                              \
    }

/* Define a macro for common code for all newly allocate datatypes */
#define H5T_INIT_TYPE_ALLOC_COMMON(TYPE)                                                                     \
    {                                                                                                        \
        dt->sh_loc.type  = H5O_SHARE_TYPE_UNSHARED;                                                          \
        dt->shared->type = TYPE;                                                                             \
    }

/* Define the code templates for opaque for the "GUTS" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_OPAQ_CORE                                                                              \
    {                                                                                                        \
        H5T_INIT_TYPE_ALLOC_COMMON(H5T_OPAQUE)                                                               \
        dt->shared->u.opaque.tag = H5MM_xstrdup("");                                                         \
    }

/* Define the code templates for strings for the "GUTS" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_STRING_COMMON                                                                          \
    {                                                                                                        \
        H5T_INIT_TYPE_ALLOC_COMMON(H5T_STRING)                                                               \
        H5T_INIT_TYPE_NUM_COMMON(H5T_ORDER_NONE)                                                             \
        dt->shared->u.atomic.u.s.cset = H5F_DEFAULT_CSET;                                                    \
    }

#define H5T_INIT_TYPE_CSTRING_CORE                                                                           \
    {                                                                                                        \
        H5T_INIT_TYPE_STRING_COMMON                                                                          \
        dt->shared->u.atomic.u.s.pad = H5T_STR_NULLTERM;                                                     \
    }

#define H5T_INIT_TYPE_FORSTRING_CORE                                                                         \
    {                                                                                                        \
        H5T_INIT_TYPE_STRING_COMMON                                                                          \
        dt->shared->u.atomic.u.s.pad = H5T_STR_SPACEPAD;                                                     \
    }

/* Define the code templates for references for the "GUTS" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_REF_COMMON                                                                             \
    {                                                                                                        \
        H5T_INIT_TYPE_ALLOC_COMMON(H5T_REFERENCE)                                                            \
        H5T_INIT_TYPE_NUM_COMMON(H5T_ORDER_NONE)                                                             \
        dt->shared->force_conv        = true;                                                                \
        dt->shared->u.atomic.u.r.file = NULL;                                                                \
        dt->shared->u.atomic.u.r.loc  = H5T_LOC_BADLOC;                                                      \
        dt->shared->u.atomic.u.r.cls  = NULL;                                                                \
    }

#define H5T_INIT_TYPE_OBJREF_CORE                                                                            \
    {                                                                                                        \
        H5T_INIT_TYPE_REF_COMMON                                                                             \
        dt->shared->u.atomic.u.r.rtype   = H5R_OBJECT1;                                                      \
        dt->shared->u.atomic.u.r.opaque  = false;                                                            \
        dt->shared->u.atomic.u.r.version = 0;                                                                \
    }

#define H5T_INIT_TYPE_REGREF_CORE                                                                            \
    {                                                                                                        \
        H5T_INIT_TYPE_REF_COMMON                                                                             \
        dt->shared->u.atomic.u.r.rtype   = H5R_DATASET_REGION1;                                              \
        dt->shared->u.atomic.u.r.opaque  = false;                                                            \
        dt->shared->u.atomic.u.r.version = 0;                                                                \
    }

/* rtype value is only used as a placeholder to differentiate the type from
 * other types, any opaque (i.e. "new") reference type could be used.
 */
#define H5T_INIT_TYPE_REF_CORE                                                                               \
    {                                                                                                        \
        H5T_INIT_TYPE_REF_COMMON                                                                             \
        dt->shared->u.atomic.u.r.rtype   = H5R_OBJECT2;                                                      \
        dt->shared->u.atomic.u.r.opaque  = true;                                                             \
        dt->shared->u.atomic.u.r.version = H5R_ENCODE_VERSION;                                               \
        dt->shared->version              = H5O_DTYPE_VERSION_4;                                              \
    }

/* Define the code templates for the "SIZE_TMPL" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_SET_SIZE(SIZE)                                                                         \
    {                                                                                                        \
        dt->shared->size          = SIZE;                                                                    \
        dt->shared->u.atomic.prec = 8 * (SIZE);                                                              \
    }

#define H5T_INIT_TYPE_NOSET_SIZE(SIZE)                                                                       \
    {                                                                                                        \
    }

/* Define the code templates for the "CRT_TMPL" in the H5T_INIT_TYPE macro */
#define H5T_INIT_TYPE_COPY_CREATE(BASE)                                                                      \
    {                                                                                                        \
        /* Base off of existing datatype */                                                                  \
        if (NULL == (dt = H5T_copy(BASE, H5T_COPY_TRANSIENT)))                                               \
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "duplicating base type failed");                   \
    }

#define H5T_INIT_TYPE_ALLOC_CREATE(BASE)                                                                     \
    {                                                                                                        \
        /* Allocate new datatype info */                                                                     \
        if (NULL == (dt = H5T__alloc()))                                                                     \
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "memory allocation failed");                      \
    }

#define H5T_INIT_TYPE(GUTS, GLOBAL, CRT_TMPL, BASE, SIZE_TMPL, SIZE)                                         \
    {                                                                                                        \
        /* Get new datatype struct */                                                                        \
        H5_GLUE3(H5T_INIT_TYPE_, CRT_TMPL, _CREATE)                                                          \
        (BASE)                                                                                               \
                                                                                                             \
            /* Adjust information for all types */                                                           \
            dt->shared->state = H5T_STATE_IMMUTABLE;                                                         \
        H5_GLUE3(H5T_INIT_TYPE_, SIZE_TMPL, _SIZE)                                                           \
        (SIZE)                                                                                               \
                                                                                                             \
            /* Adjust information for this type */                                                           \
            H5_GLUE3(H5T_INIT_TYPE_, GUTS, _CORE)                                                            \
                                                                                                             \
            /* Register result */                                                                            \
            if (((GLOBAL) = H5I_register(H5I_DATATYPE, dt, false)) < 0)                                      \
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTREGISTER, FAIL, "unable to register datatype atom");       \
    }

/******************/
/* Local Typedefs */
/******************/

/* Typedef for recursive const-correct datatype copying routines */
typedef H5T_t *(*H5T_copy_func_t)(H5T_t *old_dt);

/********************/
/* Local Prototypes */
/********************/
static herr_t H5T__register_int(H5T_pers_t pers, const char *name, H5T_t *src, H5T_t *dst,
                                H5T_lib_conv_t func);
static herr_t H5T__register(H5T_pers_t pers, const char *name, H5T_t *src, H5T_t *dst, H5T_conv_func_t *conv);
static htri_t H5T__compiler_conv(H5T_t *src, H5T_t *dst);
static herr_t H5T__set_size(H5T_t *dt, size_t size);
static herr_t H5T__close_cb(H5T_t *dt, void **request);
static herr_t H5T__init_path_table(void);
static bool   H5T__path_table_search(const H5T_t *src, const H5T_t *dst, int *idx, int *last_cmp);
static H5T_path_t *H5T__path_find_real(const H5T_t *src, const H5T_t *dst, const char *name,
                                       H5T_conv_func_t *conv);
static herr_t      H5T__path_find_init_new_path(H5T_path_t *path, const H5T_t *src, const H5T_t *dst,
                                                H5T_conv_func_t *conv, H5T_conv_ctx_t *conv_ctx);
static herr_t      H5T__path_free(H5T_path_t *path, H5T_conv_ctx_t *conv_ctx);
static bool        H5T_path_match(H5T_path_t *path, H5T_pers_t pers, const char *name, H5T_t *src, H5T_t *dst,
                                  H5VL_object_t *owned_vol_obj, H5T_conv_t func);
static bool   H5T_path_match_find_type_with_volobj(const H5T_t *datatype, const H5VL_object_t *owned_vol_obj);
static bool   H5T__detect_vlen_ref(const H5T_t *dt);
static H5T_t *H5T__initiate_copy(const H5T_t *old_dt);
static H5T_t *H5T__copy_transient(H5T_t *old_dt);
static H5T_t *H5T__copy_all(H5T_t *old_dt);
static herr_t H5T__complete_copy(H5T_t *new_dt, const H5T_t *old_dt, H5T_shared_t *reopened_fo,
                                 bool set_memory_type, H5T_copy_func_t copyfn);

/*****************************/
/* Library Private Variables */
/*****************************/

/* The native endianness of the platform */
H5T_order_t H5T_native_order_g = H5T_ORDER_ERROR;

/*********************/
/* Package Variables */
/*********************/

/*
 * Predefined data types. These are initialized at runtime by H5T_init().
 *
 * If more of these are added, the new ones must be added to the list of
 * types to reset in H5T_term_package().
 */
hid_t H5T_IEEE_F16BE_g = H5I_INVALID_HID;
hid_t H5T_IEEE_F16LE_g = H5I_INVALID_HID;
hid_t H5T_IEEE_F32BE_g = H5I_INVALID_HID;
hid_t H5T_IEEE_F32LE_g = H5I_INVALID_HID;
hid_t H5T_IEEE_F64BE_g = H5I_INVALID_HID;
hid_t H5T_IEEE_F64LE_g = H5I_INVALID_HID;

hid_t H5T_VAX_F32_g = H5I_INVALID_HID;
hid_t H5T_VAX_F64_g = H5I_INVALID_HID;

hid_t H5T_STD_I8BE_g        = H5I_INVALID_HID;
hid_t H5T_STD_I8LE_g        = H5I_INVALID_HID;
hid_t H5T_STD_I16BE_g       = H5I_INVALID_HID;
hid_t H5T_STD_I16LE_g       = H5I_INVALID_HID;
hid_t H5T_STD_I32BE_g       = H5I_INVALID_HID;
hid_t H5T_STD_I32LE_g       = H5I_INVALID_HID;
hid_t H5T_STD_I64BE_g       = H5I_INVALID_HID;
hid_t H5T_STD_I64LE_g       = H5I_INVALID_HID;
hid_t H5T_STD_U8BE_g        = H5I_INVALID_HID;
hid_t H5T_STD_U8LE_g        = H5I_INVALID_HID;
hid_t H5T_STD_U16BE_g       = H5I_INVALID_HID;
hid_t H5T_STD_U16LE_g       = H5I_INVALID_HID;
hid_t H5T_STD_U32BE_g       = H5I_INVALID_HID;
hid_t H5T_STD_U32LE_g       = H5I_INVALID_HID;
hid_t H5T_STD_U64BE_g       = H5I_INVALID_HID;
hid_t H5T_STD_U64LE_g       = H5I_INVALID_HID;
hid_t H5T_STD_B8BE_g        = H5I_INVALID_HID;
hid_t H5T_STD_B8LE_g        = H5I_INVALID_HID;
hid_t H5T_STD_B16BE_g       = H5I_INVALID_HID;
hid_t H5T_STD_B16LE_g       = H5I_INVALID_HID;
hid_t H5T_STD_B32BE_g       = H5I_INVALID_HID;
hid_t H5T_STD_B32LE_g       = H5I_INVALID_HID;
hid_t H5T_STD_B64BE_g       = H5I_INVALID_HID;
hid_t H5T_STD_B64LE_g       = H5I_INVALID_HID;
hid_t H5T_STD_REF_OBJ_g     = H5I_INVALID_HID;
hid_t H5T_STD_REF_DSETREG_g = H5I_INVALID_HID;
hid_t H5T_STD_REF_g         = H5I_INVALID_HID;

hid_t H5T_UNIX_D32BE_g = H5I_INVALID_HID;
hid_t H5T_UNIX_D32LE_g = H5I_INVALID_HID;
hid_t H5T_UNIX_D64BE_g = H5I_INVALID_HID;
hid_t H5T_UNIX_D64LE_g = H5I_INVALID_HID;

hid_t H5T_C_S1_g = H5I_INVALID_HID;

hid_t H5T_FORTRAN_S1_g = H5I_INVALID_HID;

hid_t H5T_NATIVE_SCHAR_g   = H5I_INVALID_HID;
hid_t H5T_NATIVE_UCHAR_g   = H5I_INVALID_HID;
hid_t H5T_NATIVE_SHORT_g   = H5I_INVALID_HID;
hid_t H5T_NATIVE_USHORT_g  = H5I_INVALID_HID;
hid_t H5T_NATIVE_INT_g     = H5I_INVALID_HID;
hid_t H5T_NATIVE_UINT_g    = H5I_INVALID_HID;
hid_t H5T_NATIVE_LONG_g    = H5I_INVALID_HID;
hid_t H5T_NATIVE_ULONG_g   = H5I_INVALID_HID;
hid_t H5T_NATIVE_LLONG_g   = H5I_INVALID_HID;
hid_t H5T_NATIVE_ULLONG_g  = H5I_INVALID_HID;
hid_t H5T_NATIVE_FLOAT16_g = H5I_INVALID_HID;
hid_t H5T_NATIVE_FLOAT_g   = H5I_INVALID_HID;
hid_t H5T_NATIVE_DOUBLE_g  = H5I_INVALID_HID;
hid_t H5T_NATIVE_LDOUBLE_g = H5I_INVALID_HID;
hid_t H5T_NATIVE_B8_g      = H5I_INVALID_HID;
hid_t H5T_NATIVE_B16_g     = H5I_INVALID_HID;
hid_t H5T_NATIVE_B32_g     = H5I_INVALID_HID;
hid_t H5T_NATIVE_B64_g     = H5I_INVALID_HID;
hid_t H5T_NATIVE_OPAQUE_g  = H5I_INVALID_HID;
hid_t H5T_NATIVE_HADDR_g   = H5I_INVALID_HID;
hid_t H5T_NATIVE_HSIZE_g   = H5I_INVALID_HID;
hid_t H5T_NATIVE_HSSIZE_g  = H5I_INVALID_HID;
hid_t H5T_NATIVE_HERR_g    = H5I_INVALID_HID;
hid_t H5T_NATIVE_HBOOL_g   = H5I_INVALID_HID;

hid_t H5T_NATIVE_INT8_g        = H5I_INVALID_HID;
hid_t H5T_NATIVE_UINT8_g       = H5I_INVALID_HID;
hid_t H5T_NATIVE_INT_LEAST8_g  = H5I_INVALID_HID;
hid_t H5T_NATIVE_UINT_LEAST8_g = H5I_INVALID_HID;
hid_t H5T_NATIVE_INT_FAST8_g   = H5I_INVALID_HID;
hid_t H5T_NATIVE_UINT_FAST8_g  = H5I_INVALID_HID;

hid_t H5T_NATIVE_INT16_g        = H5I_INVALID_HID;
hid_t H5T_NATIVE_UINT16_g       = H5I_INVALID_HID;
hid_t H5T_NATIVE_INT_LEAST16_g  = H5I_INVALID_HID;
hid_t H5T_NATIVE_UINT_LEAST16_g = H5I_INVALID_HID;
hid_t H5T_NATIVE_INT_FAST16_g   = H5I_INVALID_HID;
hid_t H5T_NATIVE_UINT_FAST16_g  = H5I_INVALID_HID;

hid_t H5T_NATIVE_INT32_g        = H5I_INVALID_HID;
hid_t H5T_NATIVE_UINT32_g       = H5I_INVALID_HID;
hid_t H5T_NATIVE_INT_LEAST32_g  = H5I_INVALID_HID;
hid_t H5T_NATIVE_UINT_LEAST32_g = H5I_INVALID_HID;
hid_t H5T_NATIVE_INT_FAST32_g   = H5I_INVALID_HID;
hid_t H5T_NATIVE_UINT_FAST32_g  = H5I_INVALID_HID;

hid_t H5T_NATIVE_INT64_g        = H5I_INVALID_HID;
hid_t H5T_NATIVE_UINT64_g       = H5I_INVALID_HID;
hid_t H5T_NATIVE_INT_LEAST64_g  = H5I_INVALID_HID;
hid_t H5T_NATIVE_UINT_LEAST64_g = H5I_INVALID_HID;
hid_t H5T_NATIVE_INT_FAST64_g   = H5I_INVALID_HID;
hid_t H5T_NATIVE_UINT_FAST64_g  = H5I_INVALID_HID;

/*
 * Alignment constraints for HDF5 types.  Accessing objects of these
 * types with improper alignment invokes C undefined behavior, so the
 * library lays out objects with correct alignment, always.
 *
 * A value of N indicates that the data must be aligned on an address
 * ADDR such that 0 == ADDR mod N. When N=1 no alignment is required;
 * N=0 implies that alignment constraints were not calculated.  These
 * values are used for structure alignment.
 */
size_t H5T_POINTER_ALIGN_g     = 0;
size_t H5T_HVL_ALIGN_g         = 0;
size_t H5T_HOBJREF_ALIGN_g     = 0;
size_t H5T_HDSETREGREF_ALIGN_g = 0;
size_t H5T_REF_ALIGN_g         = 0;

size_t H5T_NATIVE_SCHAR_ALIGN_g   = 0;
size_t H5T_NATIVE_UCHAR_ALIGN_g   = 0;
size_t H5T_NATIVE_SHORT_ALIGN_g   = 0;
size_t H5T_NATIVE_USHORT_ALIGN_g  = 0;
size_t H5T_NATIVE_INT_ALIGN_g     = 0;
size_t H5T_NATIVE_UINT_ALIGN_g    = 0;
size_t H5T_NATIVE_LONG_ALIGN_g    = 0;
size_t H5T_NATIVE_ULONG_ALIGN_g   = 0;
size_t H5T_NATIVE_LLONG_ALIGN_g   = 0;
size_t H5T_NATIVE_ULLONG_ALIGN_g  = 0;
size_t H5T_NATIVE_FLOAT16_ALIGN_g = 0;
size_t H5T_NATIVE_FLOAT_ALIGN_g   = 0;
size_t H5T_NATIVE_DOUBLE_ALIGN_g  = 0;
size_t H5T_NATIVE_LDOUBLE_ALIGN_g = 0;

/* Alignment constraints for C99 types */
size_t H5T_NATIVE_INT8_ALIGN_g        = 0;
size_t H5T_NATIVE_UINT8_ALIGN_g       = 0;
size_t H5T_NATIVE_INT_LEAST8_ALIGN_g  = 0;
size_t H5T_NATIVE_UINT_LEAST8_ALIGN_g = 0;
size_t H5T_NATIVE_INT_FAST8_ALIGN_g   = 0;
size_t H5T_NATIVE_UINT_FAST8_ALIGN_g  = 0;

size_t H5T_NATIVE_INT16_ALIGN_g        = 0;
size_t H5T_NATIVE_UINT16_ALIGN_g       = 0;
size_t H5T_NATIVE_INT_LEAST16_ALIGN_g  = 0;
size_t H5T_NATIVE_UINT_LEAST16_ALIGN_g = 0;
size_t H5T_NATIVE_INT_FAST16_ALIGN_g   = 0;
size_t H5T_NATIVE_UINT_FAST16_ALIGN_g  = 0;

size_t H5T_NATIVE_INT32_ALIGN_g        = 0;
size_t H5T_NATIVE_UINT32_ALIGN_g       = 0;
size_t H5T_NATIVE_INT_LEAST32_ALIGN_g  = 0;
size_t H5T_NATIVE_UINT_LEAST32_ALIGN_g = 0;
size_t H5T_NATIVE_INT_FAST32_ALIGN_g   = 0;
size_t H5T_NATIVE_UINT_FAST32_ALIGN_g  = 0;

size_t H5T_NATIVE_INT64_ALIGN_g        = 0;
size_t H5T_NATIVE_UINT64_ALIGN_g       = 0;
size_t H5T_NATIVE_INT_LEAST64_ALIGN_g  = 0;
size_t H5T_NATIVE_UINT_LEAST64_ALIGN_g = 0;
size_t H5T_NATIVE_INT_FAST64_ALIGN_g   = 0;
size_t H5T_NATIVE_UINT_FAST64_ALIGN_g  = 0;

/* Useful floating-point values for conversion routines */
/* (+/- Inf for all floating-point types) */
#ifdef H5_HAVE__FLOAT16
/* Initialize these with a float literal since the f16 suffix
 * is non-standard C and gives warnings when compiling the
 * library with the -pedantic flag. These values will be
 * overwritten anyway.
 */
H5__Float16 H5T_NATIVE_FLOAT16_POS_INF_g = 0.0f;
H5__Float16 H5T_NATIVE_FLOAT16_NEG_INF_g = 0.0f;
#endif
float  H5T_NATIVE_FLOAT_POS_INF_g  = 0.0F;
float  H5T_NATIVE_FLOAT_NEG_INF_g  = 0.0F;
double H5T_NATIVE_DOUBLE_POS_INF_g = 0.0;
double H5T_NATIVE_DOUBLE_NEG_INF_g = 0.0;

/* Declare the free list for H5T_t's and H5T_shared_t's */
H5FL_DEFINE(H5T_t);
H5FL_DEFINE(H5T_shared_t);

/* Format version bounds for datatype */
const unsigned H5O_dtype_ver_bounds[] = {
    H5O_DTYPE_VERSION_1,     /* H5F_LIBVER_EARLIEST */
    H5O_DTYPE_VERSION_3,     /* H5F_LIBVER_V18 */
    H5O_DTYPE_VERSION_3,     /* H5F_LIBVER_V110 */
    H5O_DTYPE_VERSION_4,     /* H5F_LIBVER_V112 */
    H5O_DTYPE_VERSION_LATEST /* H5F_LIBVER_LATEST */
};

/*******************/
/* Local Variables */
/*******************/

/*
 * The path database. Each path has a source and destination data type pair
 * which is used as the key by which the `entries' array is sorted.
 */
static struct {
    int          npaths; /*number of paths defined               */
    size_t       apaths; /*number of paths allocated             */
    H5T_path_t **path;   /*sorted array of path pointers         */
    int          nsoft;  /*number of soft conversions defined    */
    size_t       asoft;  /*number of soft conversions allocated  */
    H5T_soft_t  *soft;   /*unsorted array of soft conversions    */
} H5T_g;

/* Declare the free list for H5T_path_t's */
H5FL_DEFINE_STATIC(H5T_path_t);

/* Datatype ID class */
static const H5I_class_t H5I_DATATYPE_CLS[1] = {{
    H5I_DATATYPE,             /* ID class value */
    0,                        /* Class flags    */
    8,                        /* # of reserved IDs for class */
    (H5I_free_t)H5T__close_cb /* Callback routine for closing objects of this class */
}};

/*-------------------------------------------------------------------------
 * Function:    H5T__init_inf
 *
 * Purpose:    Initialize the +/- Infinity floating-poing values for type
 *              conversion.
 *
 * Return:    Success:    non-negative
 *            Failure:    negative
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5T__init_inf(void)
{
    H5T_t        *dst_p;               /* Datatype type operate on */
    H5T_atomic_t *dst;                 /* Datatype's atomic info   */
    uint8_t      *d;                   /* Pointer to value to set  */
    size_t        half_size;           /* Half the type size       */
    size_t        u;                   /* Local index value        */
    herr_t        ret_value = SUCCEED; /* Return value             */

    FUNC_ENTER_PACKAGE

    /* Get the float datatype */
    if (NULL == (dst_p = (H5T_t *)H5I_object(H5T_NATIVE_FLOAT_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");
    dst = &dst_p->shared->u.atomic;

    /* Check that we can re-order the bytes correctly */
    if (H5T_ORDER_LE != H5T_native_order_g && H5T_ORDER_BE != H5T_native_order_g)
        HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, "unsupported byte order");

    /* +Inf */
    d = (uint8_t *)&H5T_NATIVE_FLOAT_POS_INF_g;
    H5T__bit_set(d, dst->u.f.sign, (size_t)1, false);
    H5T__bit_set(d, dst->u.f.epos, dst->u.f.esize, true);
    H5T__bit_set(d, dst->u.f.mpos, dst->u.f.msize, false);

    /* Swap the bytes if the machine architecture is big-endian */
    if (H5T_ORDER_BE == H5T_native_order_g) {
        half_size = dst_p->shared->size / 2;
        for (u = 0; u < half_size; u++) {
            uint8_t tmp                      = d[dst_p->shared->size - (u + 1)];
            d[dst_p->shared->size - (u + 1)] = d[u];
            d[u]                             = tmp;
        } /* end for */
    }     /* end if */

    /* -Inf */
    d = (uint8_t *)&H5T_NATIVE_FLOAT_NEG_INF_g;
    H5T__bit_set(d, dst->u.f.sign, (size_t)1, true);
    H5T__bit_set(d, dst->u.f.epos, dst->u.f.esize, true);
    H5T__bit_set(d, dst->u.f.mpos, dst->u.f.msize, false);

    /* Swap the bytes if the machine architecture is big-endian */
    if (H5T_ORDER_BE == H5T_native_order_g) {
        half_size = dst_p->shared->size / 2;
        for (u = 0; u < half_size; u++) {
            uint8_t tmp                      = d[dst_p->shared->size - (u + 1)];
            d[dst_p->shared->size - (u + 1)] = d[u];
            d[u]                             = tmp;
        } /* end for */
    }     /* end if */

    /* Get the double datatype */
    if (NULL == (dst_p = (H5T_t *)H5I_object(H5T_NATIVE_DOUBLE_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");
    dst = &dst_p->shared->u.atomic;

    /* Check that we can re-order the bytes correctly */
    if (H5T_ORDER_LE != H5T_native_order_g && H5T_ORDER_BE != H5T_native_order_g)
        HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, "unsupported byte order");

    /* +Inf */
    d = (uint8_t *)&H5T_NATIVE_DOUBLE_POS_INF_g;
    H5T__bit_set(d, dst->u.f.sign, (size_t)1, false);
    H5T__bit_set(d, dst->u.f.epos, dst->u.f.esize, true);
    H5T__bit_set(d, dst->u.f.mpos, dst->u.f.msize, false);

    /* Swap the bytes if the machine architecture is big-endian */
    if (H5T_ORDER_BE == H5T_native_order_g) {
        half_size = dst_p->shared->size / 2;
        for (u = 0; u < half_size; u++) {
            uint8_t tmp                      = d[dst_p->shared->size - (u + 1)];
            d[dst_p->shared->size - (u + 1)] = d[u];
            d[u]                             = tmp;
        } /* end for */
    }     /* end if */

    /* -Inf */
    d = (uint8_t *)&H5T_NATIVE_DOUBLE_NEG_INF_g;
    H5T__bit_set(d, dst->u.f.sign, (size_t)1, true);
    H5T__bit_set(d, dst->u.f.epos, dst->u.f.esize, true);
    H5T__bit_set(d, dst->u.f.mpos, dst->u.f.msize, false);

    /* Swap the bytes if the machine architecture is big-endian */
    if (H5T_ORDER_BE == H5T_native_order_g) {
        half_size = dst_p->shared->size / 2;
        for (u = 0; u < half_size; u++) {
            uint8_t tmp                      = d[dst_p->shared->size - (u + 1)];
            d[dst_p->shared->size - (u + 1)] = d[u];
            d[u]                             = tmp;
        } /* end for */
    }     /* end if */

#ifdef H5_HAVE__FLOAT16
    /* Get the _Float16 datatype */
    if (NULL == (dst_p = (H5T_t *)H5I_object(H5T_NATIVE_FLOAT16_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");
    dst = &dst_p->shared->u.atomic;

    /* Check that we can re-order the bytes correctly */
    if (H5T_ORDER_LE != H5T_native_order_g && H5T_ORDER_BE != H5T_native_order_g)
        HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, "unsupported byte order");

    /* +Inf */
    d = (uint8_t *)&H5T_NATIVE_FLOAT16_POS_INF_g;
    H5T__bit_set(d, dst->u.f.sign, (size_t)1, false);
    H5T__bit_set(d, dst->u.f.epos, dst->u.f.esize, true);
    H5T__bit_set(d, dst->u.f.mpos, dst->u.f.msize, false);

    /* Swap the bytes if the machine architecture is big-endian */
    if (H5T_ORDER_BE == H5T_native_order_g) {
        half_size = dst_p->shared->size / 2;
        for (u = 0; u < half_size; u++) {
            uint8_t tmp                      = d[dst_p->shared->size - (u + 1)];
            d[dst_p->shared->size - (u + 1)] = d[u];
            d[u]                             = tmp;
        } /* end for */
    }     /* end if */

    /* -Inf */
    d = (uint8_t *)&H5T_NATIVE_FLOAT16_NEG_INF_g;
    H5T__bit_set(d, dst->u.f.sign, (size_t)1, true);
    H5T__bit_set(d, dst->u.f.epos, dst->u.f.esize, true);
    H5T__bit_set(d, dst->u.f.mpos, dst->u.f.msize, false);

    /* Swap the bytes if the machine architecture is big-endian */
    if (H5T_ORDER_BE == H5T_native_order_g) {
        half_size = dst_p->shared->size / 2;
        for (u = 0; u < half_size; u++) {
            uint8_t tmp                      = d[dst_p->shared->size - (u + 1)];
            d[dst_p->shared->size - (u + 1)] = d[u];
            d[u]                             = tmp;
        } /* end for */
    }     /* end if */
#endif

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__init_inf() */

/*-------------------------------------------------------------------------
 * Function:    H5T_init
 *
 * Purpose:     Initialize the interface from some other layer.
 *
 * Return:      Success:        non-negative
 *              Failure:        negative
 *-------------------------------------------------------------------------
 */
herr_t
H5T_init(void)
{
    H5T_t  *native_schar   = NULL; /* Datatype structure for native signed char */
    H5T_t  *native_uchar   = NULL; /* Datatype structure for native unsigned char */
    H5T_t  *native_short   = NULL; /* Datatype structure for native short */
    H5T_t  *native_ushort  = NULL; /* Datatype structure for native unsigned short */
    H5T_t  *native_int     = NULL; /* Datatype structure for native int */
    H5T_t  *native_uint    = NULL; /* Datatype structure for native unsigned int */
    H5T_t  *native_long    = NULL; /* Datatype structure for native long */
    H5T_t  *native_ulong   = NULL; /* Datatype structure for native unsigned long */
    H5T_t  *native_llong   = NULL; /* Datatype structure for native long long */
    H5T_t  *native_ullong  = NULL; /* Datatype structure for native unsigned long long */
    H5T_t  *native_float   = NULL; /* Datatype structure for native float */
    H5T_t  *native_double  = NULL; /* Datatype structure for native double */
    H5T_t  *native_ldouble = NULL; /* Datatype structure for native long double */
    H5T_t  *std_u8le       = NULL; /* Datatype structure for unsigned 8-bit little-endian integer */
    H5T_t  *std_u8be       = NULL; /* Datatype structure for unsigned 8-bit big-endian integer */
    H5T_t  *std_u16le      = NULL; /* Datatype structure for unsigned 16-bit little-endian integer */
    H5T_t  *std_u16be      = NULL; /* Datatype structure for unsigned 16-bit big-endian integer */
    H5T_t  *std_u32le      = NULL; /* Datatype structure for unsigned 32-bit little-endian integer */
    H5T_t  *std_u32be      = NULL; /* Datatype structure for unsigned 32-bit big-endian integer */
    H5T_t  *std_u64le      = NULL; /* Datatype structure for unsigned 64-bit little-endian integer */
    H5T_t  *std_u64be      = NULL; /* Datatype structure for unsigned 64-bit big-endian integer */
    H5T_t  *dt             = NULL;
    H5T_t  *fixedpt        = NULL; /* Datatype structure for native int */
    H5T_t  *floatpt        = NULL; /* Datatype structure for native float */
    H5T_t  *string         = NULL; /* Datatype structure for C string */
    H5T_t  *bitfield       = NULL; /* Datatype structure for bitfield */
    H5T_t  *compound       = NULL; /* Datatype structure for compound objects */
    H5T_t  *enum_type      = NULL; /* Datatype structure for enum objects */
    H5T_t  *vlen           = NULL; /* Datatype structure for vlen objects */
    H5T_t  *array          = NULL; /* Datatype structure for array objects */
    H5T_t  *objref         = NULL; /* Datatype structure for deprecated reference objects */
    H5T_t  *regref         = NULL; /* Datatype structure for deprecated region references */
    H5T_t  *ref            = NULL; /* Datatype structure for opaque references */
    hsize_t dim[1]         = {1};  /* Dimension info for array datatype */
    herr_t  status;
    bool    copied_dtype =
        true; /* Flag to indicate whether datatype was copied or allocated (for error cleanup) */
#ifdef H5_HAVE__FLOAT16
    H5T_t *native_float16 = NULL; /* Datatype structure for native _Float16 type */
#endif
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    /* Initialize the ID group for the file IDs */
    if (H5I_register_type(H5I_DATATYPE_CLS) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to initialize interface");

    /* Make certain there aren't too many classes of datatypes defined */
    /* Only 16 (numbered 0-15) are supported in the current file format */
    HDcompile_assert(H5T_NCLASSES < 16);

    /* Initialize native floating-point datatypes */
    if (H5T__init_native_float_types() < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to initialize floating-point types");

    /* Initialize all other native types */
    if (H5T__init_native_internal() < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to initialize integers");

    /* Get the atomic datatype structures needed by the initialization code below */
    if (NULL == (native_schar = (H5T_t *)H5I_object(H5T_NATIVE_SCHAR_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");
    if (NULL == (native_uchar = (H5T_t *)H5I_object(H5T_NATIVE_UCHAR_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");
    if (NULL == (native_short = (H5T_t *)H5I_object(H5T_NATIVE_SHORT_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");
    if (NULL == (native_ushort = (H5T_t *)H5I_object(H5T_NATIVE_USHORT_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");
    if (NULL == (native_int = (H5T_t *)H5I_object(H5T_NATIVE_INT_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");
    if (NULL == (native_uint = (H5T_t *)H5I_object(H5T_NATIVE_UINT_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");
    if (NULL == (native_long = (H5T_t *)H5I_object(H5T_NATIVE_LONG_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");
    if (NULL == (native_ulong = (H5T_t *)H5I_object(H5T_NATIVE_ULONG_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");
    if (NULL == (native_llong = (H5T_t *)H5I_object(H5T_NATIVE_LLONG_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");
    if (NULL == (native_ullong = (H5T_t *)H5I_object(H5T_NATIVE_ULLONG_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");
#ifdef H5_HAVE__FLOAT16
    if (NULL == (native_float16 = (H5T_t *)H5I_object(H5T_NATIVE_FLOAT16_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");
#endif
    if (NULL == (native_float = (H5T_t *)H5I_object(H5T_NATIVE_FLOAT_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");
    if (NULL == (native_double = (H5T_t *)H5I_object(H5T_NATIVE_DOUBLE_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");
    if (NULL == (native_ldouble = (H5T_t *)H5I_object(H5T_NATIVE_LDOUBLE_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype object");

    /*------------------------------------------------------------
     * Derived native types
     *------------------------------------------------------------
     */

    /* 1-byte bit field */
    H5T_INIT_TYPE(BITFIELD, H5T_NATIVE_B8_g, COPY, native_uint, SET, 1)

    /* 2-byte bit field */
    H5T_INIT_TYPE(BITFIELD, H5T_NATIVE_B16_g, COPY, native_uint, SET, 2)

    /* 4-byte bit field */
    H5T_INIT_TYPE(BITFIELD, H5T_NATIVE_B32_g, COPY, native_uint, SET, 4)

    /* 8-byte bit field */
    H5T_INIT_TYPE(BITFIELD, H5T_NATIVE_B64_g, COPY, native_uint, SET, 8)

    /* haddr_t */
    H5T_INIT_TYPE(OFFSET, H5T_NATIVE_HADDR_g, COPY, native_uint, SET, sizeof(haddr_t))

    /* hsize_t */
    H5T_INIT_TYPE(OFFSET, H5T_NATIVE_HSIZE_g, COPY, native_uint, SET, sizeof(hsize_t))

    /* hssize_t */
    H5T_INIT_TYPE(OFFSET, H5T_NATIVE_HSSIZE_g, COPY, native_int, SET, sizeof(hssize_t))

    /* herr_t */
    H5T_INIT_TYPE(OFFSET, H5T_NATIVE_HERR_g, COPY, native_int, SET, sizeof(herr_t))

    /* bool */
    H5T_INIT_TYPE(OFFSET, H5T_NATIVE_HBOOL_g, COPY, native_uint, SET, sizeof(bool))

    /*------------------------------------------------------------
     * IEEE Types
     *------------------------------------------------------------
     */

    /* IEEE 2-byte little-endian float */
    H5T_INIT_TYPE(FLOAT16LE, H5T_IEEE_F16LE_g, COPY, native_double, SET, 2)

    /* IEEE 2-byte big-endian float */
    H5T_INIT_TYPE(FLOAT16BE, H5T_IEEE_F16BE_g, COPY, native_double, SET, 2)

    /* IEEE 4-byte little-endian float */
    H5T_INIT_TYPE(FLOATLE, H5T_IEEE_F32LE_g, COPY, native_double, SET, 4)

    /* IEEE 4-byte big-endian float */
    H5T_INIT_TYPE(FLOATBE, H5T_IEEE_F32BE_g, COPY, native_double, SET, 4)

    /* IEEE 8-byte little-endian float */
    H5T_INIT_TYPE(DOUBLELE, H5T_IEEE_F64LE_g, COPY, native_double, SET, 8)

    /* IEEE 8-byte big-endian float */
    H5T_INIT_TYPE(DOUBLEBE, H5T_IEEE_F64BE_g, COPY, native_double, SET, 8)

    /*------------------------------------------------------------
     * VAX Types
     *------------------------------------------------------------
     */

    /* VAX 4-byte float */
    H5T_INIT_TYPE(FLOATVAX, H5T_VAX_F32_g, COPY, native_double, SET, 4)

    /* VAX 8-byte double */
    H5T_INIT_TYPE(DOUBLEVAX, H5T_VAX_F64_g, COPY, native_double, SET, 8)

    /*------------------------------------------------------------
     * C99 types
     *------------------------------------------------------------
     */

    /* 1-byte little-endian (endianness is irrelevant) signed integer */
    H5T_INIT_TYPE(SINTLE, H5T_STD_I8LE_g, COPY, native_int, SET, 1)

    /* 1-byte big-endian (endianness is irrelevant) signed integer */
    H5T_INIT_TYPE(SINTBE, H5T_STD_I8BE_g, COPY, native_int, SET, 1)

    /* 2-byte little-endian signed integer */
    H5T_INIT_TYPE(SINTLE, H5T_STD_I16LE_g, COPY, native_int, SET, 2)

    /* 2-byte big-endian signed integer */
    H5T_INIT_TYPE(SINTBE, H5T_STD_I16BE_g, COPY, native_int, SET, 2)

    /* 4-byte little-endian signed integer */
    H5T_INIT_TYPE(SINTLE, H5T_STD_I32LE_g, COPY, native_int, SET, 4)

    /* 4-byte big-endian signed integer */
    H5T_INIT_TYPE(SINTBE, H5T_STD_I32BE_g, COPY, native_int, SET, 4)

    /* 8-byte little-endian signed integer */
    H5T_INIT_TYPE(SINTLE, H5T_STD_I64LE_g, COPY, native_int, SET, 8)

    /* 8-byte big-endian signed integer */
    H5T_INIT_TYPE(SINTBE, H5T_STD_I64BE_g, COPY, native_int, SET, 8)

    /* 1-byte little-endian (endianness is irrelevant) unsigned integer */
    H5T_INIT_TYPE(UINTLE, H5T_STD_U8LE_g, COPY, native_uint, SET, 1)
    std_u8le = dt; /* Keep type for later */

    /* 1-byte big-endian (endianness is irrelevant) unsigned integer */
    H5T_INIT_TYPE(UINTBE, H5T_STD_U8BE_g, COPY, native_uint, SET, 1)
    std_u8be = dt; /* Keep type for later */

    /* 2-byte little-endian unsigned integer */
    H5T_INIT_TYPE(UINTLE, H5T_STD_U16LE_g, COPY, native_uint, SET, 2)
    std_u16le = dt; /* Keep type for later */

    /* 2-byte big-endian unsigned integer */
    H5T_INIT_TYPE(UINTBE, H5T_STD_U16BE_g, COPY, native_uint, SET, 2)
    std_u16be = dt; /* Keep type for later */

    /* 4-byte little-endian unsigned integer */
    H5T_INIT_TYPE(UINTLE, H5T_STD_U32LE_g, COPY, native_uint, SET, 4)
    std_u32le = dt; /* Keep type for later */

    /* 4-byte big-endian unsigned integer */
    H5T_INIT_TYPE(UINTBE, H5T_STD_U32BE_g, COPY, native_uint, SET, 4)
    std_u32be = dt; /* Keep type for later */

    /* 8-byte little-endian unsigned integer */
    H5T_INIT_TYPE(UINTLE, H5T_STD_U64LE_g, COPY, native_uint, SET, 8)
    std_u64le = dt; /* Keep type for later */

    /* 8-byte big-endian unsigned integer */
    H5T_INIT_TYPE(UINTBE, H5T_STD_U64BE_g, COPY, native_uint, SET, 8)
    std_u64be = dt; /* Keep type for later */

    /*------------------------------------------------------------
     * Native, Little- & Big-endian bitfields
     *------------------------------------------------------------
     */

    /* little-endian (order is irrelevant) 8-bit bitfield */
    H5T_INIT_TYPE(BITFIELDLE, H5T_STD_B8LE_g, COPY, std_u8le, NOSET, -)
    bitfield = dt; /* Keep type for later */

    /* big-endian (order is irrelevant) 8-bit bitfield */
    H5T_INIT_TYPE(BITFIELDBE, H5T_STD_B8BE_g, COPY, std_u8be, NOSET, -)

    /* Little-endian 16-bit bitfield */
    H5T_INIT_TYPE(BITFIELDLE, H5T_STD_B16LE_g, COPY, std_u16le, NOSET, -)

    /* Big-endian 16-bit bitfield */
    H5T_INIT_TYPE(BITFIELDBE, H5T_STD_B16BE_g, COPY, std_u16be, NOSET, -)

    /* Little-endian 32-bit bitfield */
    H5T_INIT_TYPE(BITFIELDLE, H5T_STD_B32LE_g, COPY, std_u32le, NOSET, -)

    /* Big-endian 32-bit bitfield */
    H5T_INIT_TYPE(BITFIELDBE, H5T_STD_B32BE_g, COPY, std_u32be, NOSET, -)

    /* Little-endian 64-bit bitfield */
    H5T_INIT_TYPE(BITFIELDLE, H5T_STD_B64LE_g, COPY, std_u64le, NOSET, -)

    /* Big-endian 64-bit bitfield */
    H5T_INIT_TYPE(BITFIELDBE, H5T_STD_B64BE_g, COPY, std_u64be, NOSET, -)

    /*------------------------------------------------------------
     * The Unix architecture for dates and times.
     *------------------------------------------------------------
     */

    /* Little-endian 32-bit UNIX time_t */
    H5T_INIT_TYPE(TIME, H5T_UNIX_D32LE_g, COPY, std_u32le, NOSET, -)

    /* Big-endian 32-bit UNIX time_t */
    H5T_INIT_TYPE(TIME, H5T_UNIX_D32BE_g, COPY, std_u32be, NOSET, -)

    /* Little-endian 64-bit UNIX time_t */
    H5T_INIT_TYPE(TIME, H5T_UNIX_D64LE_g, COPY, std_u64le, NOSET, -)

    /* Big-endian 64-bit UNIX time_t */
    H5T_INIT_TYPE(TIME, H5T_UNIX_D64BE_g, COPY, std_u64be, NOSET, -)

    /* Indicate that the types that are created from here down are allocated
     * H5FL_ALLOC(), not copied with H5T_copy()
     */
    copied_dtype = false;

    /* Opaque data */
    H5T_INIT_TYPE(OPAQ, H5T_NATIVE_OPAQUE_g, ALLOC, -, SET, 1)

    /*------------------------------------------------------------
     * The `C' architecture
     *------------------------------------------------------------
     */

    /* One-byte character string */
    H5T_INIT_TYPE(CSTRING, H5T_C_S1_g, ALLOC, -, SET, 1)
    string = dt; /* Keep type for later */

    /*------------------------------------------------------------
     * The `Fortran' architecture
     *------------------------------------------------------------
     */

    /* One-byte character string */
    H5T_INIT_TYPE(FORSTRING, H5T_FORTRAN_S1_g, ALLOC, -, SET, 1)

    /*------------------------------------------------------------
     * Reference types
     *------------------------------------------------------------
     */

    /* Deprecated object reference type */
    H5T_INIT_TYPE(OBJREF, H5T_STD_REF_OBJ_g, ALLOC, -, NOSET, -)
    if (H5T_set_loc(dt, NULL, H5T_LOC_MEMORY) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "invalid datatype location");
    objref = dt; /* Keep type for later */

    /* Deprecated region reference type */
    H5T_INIT_TYPE(REGREF, H5T_STD_REF_DSETREG_g, ALLOC, -, NOSET, -)
    if (H5T_set_loc(dt, NULL, H5T_LOC_MEMORY) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "invalid datatype location");
    regref = dt; /* Keep type for later */

    /* Opaque reference type */
    H5T_INIT_TYPE(REF, H5T_STD_REF_g, ALLOC, -, NOSET, -)
    if (H5T_set_loc(dt, NULL, H5T_LOC_MEMORY) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "invalid datatype location");
    ref = dt; /* Keep type for later */

    /*
     * Register conversion functions beginning with the most general and
     * ending with the most specific.
     */
    fixedpt = native_int;
    floatpt = native_float;
    if (NULL == (compound = H5T__create(H5T_COMPOUND, (size_t)1)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");
    if (NULL == (enum_type = H5T__create(H5T_ENUM, (size_t)1)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");
    if (NULL == (vlen = H5T__vlen_create(native_int)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");
    if (NULL == (array = H5T__array_create(native_int, 1, dim)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");
    status = 0;

    status |= H5T__register_int(H5T_PERS_SOFT, "i_i", fixedpt, fixedpt, H5T__conv_i_i);
    status |= H5T__register_int(H5T_PERS_SOFT, "i_f", fixedpt, floatpt, H5T__conv_i_f);
    status |= H5T__register_int(H5T_PERS_SOFT, "f_f", floatpt, floatpt, H5T__conv_f_f);
    status |= H5T__register_int(H5T_PERS_SOFT, "f_i", floatpt, fixedpt, H5T__conv_f_i);
    status |= H5T__register_int(H5T_PERS_SOFT, "s_s", string, string, H5T__conv_s_s);
    status |= H5T__register_int(H5T_PERS_SOFT, "b_b", bitfield, bitfield, H5T__conv_b_b);
    status |= H5T__register_int(H5T_PERS_SOFT, "ibo", fixedpt, fixedpt, H5T__conv_order);
    status |= H5T__register_int(H5T_PERS_SOFT, "ibo(opt)", fixedpt, fixedpt, H5T__conv_order_opt);
    status |= H5T__register_int(H5T_PERS_SOFT, "fbo", floatpt, floatpt, H5T__conv_order);
    status |= H5T__register_int(H5T_PERS_SOFT, "fbo(opt)", floatpt, floatpt, H5T__conv_order_opt);
    status |= H5T__register_int(H5T_PERS_SOFT, "struct(no-opt)", compound, compound, H5T__conv_struct);
    status |= H5T__register_int(H5T_PERS_SOFT, "struct(opt)", compound, compound, H5T__conv_struct_opt);
    status |= H5T__register_int(H5T_PERS_SOFT, "enum", enum_type, enum_type, H5T__conv_enum);
    status |= H5T__register_int(H5T_PERS_SOFT, "enum_i", enum_type, fixedpt, H5T__conv_enum_numeric);
    status |= H5T__register_int(H5T_PERS_SOFT, "enum_f", enum_type, floatpt, H5T__conv_enum_numeric);
    status |= H5T__register_int(H5T_PERS_SOFT, "vlen", vlen, vlen, H5T__conv_vlen);
    status |= H5T__register_int(H5T_PERS_SOFT, "array", array, array, H5T__conv_array);
    status |= H5T__register_int(H5T_PERS_SOFT, "objref", objref, objref, H5T__conv_noop);
    status |= H5T__register_int(H5T_PERS_SOFT, "regref", regref, regref, H5T__conv_noop);
    status |= H5T__register_int(H5T_PERS_SOFT, "ref", ref, ref, H5T__conv_ref);
    status |= H5T__register_int(H5T_PERS_SOFT, "objref_ref", objref, ref, H5T__conv_ref);
    status |= H5T__register_int(H5T_PERS_SOFT, "regref_ref", regref, ref, H5T__conv_ref);

    /*
     * Native conversions should be listed last since we can use hardware to
     * perform the conversion.  We list the odd types like `llong', `long',
     * and `short' before the usual types like `int' and `char' so that when
     * diagnostics are printed we favor the usual names over the odd names
     * when two or more types are the same size.
     */

    /* floating point */
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt_dbl", native_float, native_double, H5T__conv_float_double);
    status |=
        H5T__register_int(H5T_PERS_HARD, "dbl_flt", native_double, native_float, H5T__conv_double_float);
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt_ldbl", native_float, native_ldouble, H5T__conv_float_ldouble);
    status |=
        H5T__register_int(H5T_PERS_HARD, "dbl_ldbl", native_double, native_ldouble, H5T__conv_double_ldouble);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ldbl_flt", native_ldouble, native_float, H5T__conv_ldouble_float);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ldbl_dbl", native_ldouble, native_double, H5T__conv_ldouble_double);
#ifdef H5_HAVE__FLOAT16
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt16_flt", native_float16, native_float, H5T__conv__Float16_float);
    status |= H5T__register_int(H5T_PERS_HARD, "flt16_dbl", native_float16, native_double,
                                H5T__conv__Float16_double);
    status |= H5T__register_int(H5T_PERS_HARD, "flt16_ldbl", native_float16, native_ldouble,
                                H5T__conv__Float16_ldouble);
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt_flt16", native_float, native_float16, H5T__conv_float__Float16);
    status |= H5T__register_int(H5T_PERS_HARD, "dbl_flt16", native_double, native_float16,
                                H5T__conv_double__Float16);
#ifdef H5T_CONV_INTERNAL_LDOUBLE_FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "ldbl_flt16", native_ldouble, native_float16,
                                H5T__conv_ldouble__Float16);
#endif
#endif

    /* from long long */
    status |=
        H5T__register_int(H5T_PERS_HARD, "llong_ullong", native_llong, native_ullong, H5T__conv_llong_ullong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ullong_llong", native_ullong, native_llong, H5T__conv_ullong_llong);
    status |= H5T__register_int(H5T_PERS_HARD, "llong_long", native_llong, native_long, H5T__conv_llong_long);
    status |=
        H5T__register_int(H5T_PERS_HARD, "llong_ulong", native_llong, native_ulong, H5T__conv_llong_ulong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ullong_long", native_ullong, native_long, H5T__conv_ullong_long);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ullong_ulong", native_ullong, native_ulong, H5T__conv_ullong_ulong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "llong_short", native_llong, native_short, H5T__conv_llong_short);
    status |=
        H5T__register_int(H5T_PERS_HARD, "llong_ushort", native_llong, native_ushort, H5T__conv_llong_ushort);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ullong_short", native_ullong, native_short, H5T__conv_ullong_short);
    status |= H5T__register_int(H5T_PERS_HARD, "ullong_ushort", native_ullong, native_ushort,
                                H5T__conv_ullong_ushort);
    status |= H5T__register_int(H5T_PERS_HARD, "llong_int", native_llong, native_int, H5T__conv_llong_int);
    status |= H5T__register_int(H5T_PERS_HARD, "llong_uint", native_llong, native_uint, H5T__conv_llong_uint);
    status |= H5T__register_int(H5T_PERS_HARD, "ullong_int", native_ullong, native_int, H5T__conv_ullong_int);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ullong_uint", native_ullong, native_uint, H5T__conv_ullong_uint);
    status |=
        H5T__register_int(H5T_PERS_HARD, "llong_schar", native_llong, native_schar, H5T__conv_llong_schar);
    status |=
        H5T__register_int(H5T_PERS_HARD, "llong_uchar", native_llong, native_uchar, H5T__conv_llong_uchar);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ullong_schar", native_ullong, native_schar, H5T__conv_ullong_schar);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ullong_uchar", native_ullong, native_uchar, H5T__conv_ullong_uchar);

    /* From long */
    status |= H5T__register_int(H5T_PERS_HARD, "long_llong", native_long, native_llong, H5T__conv_long_llong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "long_ullong", native_long, native_ullong, H5T__conv_long_ullong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ulong_llong", native_ulong, native_llong, H5T__conv_ulong_llong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ulong_ullong", native_ulong, native_ullong, H5T__conv_ulong_ullong);
    status |= H5T__register_int(H5T_PERS_HARD, "long_ulong", native_long, native_ulong, H5T__conv_long_ulong);
    status |= H5T__register_int(H5T_PERS_HARD, "ulong_long", native_ulong, native_long, H5T__conv_ulong_long);
    status |= H5T__register_int(H5T_PERS_HARD, "long_short", native_long, native_short, H5T__conv_long_short);
    status |=
        H5T__register_int(H5T_PERS_HARD, "long_ushort", native_long, native_ushort, H5T__conv_long_ushort);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ulong_short", native_ulong, native_short, H5T__conv_ulong_short);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ulong_ushort", native_ulong, native_ushort, H5T__conv_ulong_ushort);
    status |= H5T__register_int(H5T_PERS_HARD, "long_int", native_long, native_int, H5T__conv_long_int);
    status |= H5T__register_int(H5T_PERS_HARD, "long_uint", native_long, native_uint, H5T__conv_long_uint);
    status |= H5T__register_int(H5T_PERS_HARD, "ulong_int", native_ulong, native_int, H5T__conv_ulong_int);
    status |= H5T__register_int(H5T_PERS_HARD, "ulong_uint", native_ulong, native_uint, H5T__conv_ulong_uint);
    status |= H5T__register_int(H5T_PERS_HARD, "long_schar", native_long, native_schar, H5T__conv_long_schar);
    status |= H5T__register_int(H5T_PERS_HARD, "long_uchar", native_long, native_uchar, H5T__conv_long_uchar);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ulong_schar", native_ulong, native_schar, H5T__conv_ulong_schar);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ulong_uchar", native_ulong, native_uchar, H5T__conv_ulong_uchar);

    /* From short */
    status |=
        H5T__register_int(H5T_PERS_HARD, "short_llong", native_short, native_llong, H5T__conv_short_llong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "short_ullong", native_short, native_ullong, H5T__conv_short_ullong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ushort_llong", native_ushort, native_llong, H5T__conv_ushort_llong);
    status |= H5T__register_int(H5T_PERS_HARD, "ushort_ullong", native_ushort, native_ullong,
                                H5T__conv_ushort_ullong);
    status |= H5T__register_int(H5T_PERS_HARD, "short_long", native_short, native_long, H5T__conv_short_long);
    status |=
        H5T__register_int(H5T_PERS_HARD, "short_ulong", native_short, native_ulong, H5T__conv_short_ulong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ushort_long", native_ushort, native_long, H5T__conv_ushort_long);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ushort_ulong", native_ushort, native_ulong, H5T__conv_ushort_ulong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "short_ushort", native_short, native_ushort, H5T__conv_short_ushort);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ushort_short", native_ushort, native_short, H5T__conv_ushort_short);
    status |= H5T__register_int(H5T_PERS_HARD, "short_int", native_short, native_int, H5T__conv_short_int);
    status |= H5T__register_int(H5T_PERS_HARD, "short_uint", native_short, native_uint, H5T__conv_short_uint);
    status |= H5T__register_int(H5T_PERS_HARD, "ushort_int", native_ushort, native_int, H5T__conv_ushort_int);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ushort_uint", native_ushort, native_uint, H5T__conv_ushort_uint);
    status |=
        H5T__register_int(H5T_PERS_HARD, "short_schar", native_short, native_schar, H5T__conv_short_schar);
    status |=
        H5T__register_int(H5T_PERS_HARD, "short_uchar", native_short, native_uchar, H5T__conv_short_uchar);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ushort_schar", native_ushort, native_schar, H5T__conv_ushort_schar);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ushort_uchar", native_ushort, native_uchar, H5T__conv_ushort_uchar);

    /* From int */
    status |= H5T__register_int(H5T_PERS_HARD, "int_llong", native_int, native_llong, H5T__conv_int_llong);
    status |= H5T__register_int(H5T_PERS_HARD, "int_ullong", native_int, native_ullong, H5T__conv_int_ullong);
    status |= H5T__register_int(H5T_PERS_HARD, "uint_llong", native_uint, native_llong, H5T__conv_uint_llong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "uint_ullong", native_uint, native_ullong, H5T__conv_uint_ullong);
    status |= H5T__register_int(H5T_PERS_HARD, "int_long", native_int, native_long, H5T__conv_int_long);
    status |= H5T__register_int(H5T_PERS_HARD, "int_ulong", native_int, native_ulong, H5T__conv_int_ulong);
    status |= H5T__register_int(H5T_PERS_HARD, "uint_long", native_uint, native_long, H5T__conv_uint_long);
    status |= H5T__register_int(H5T_PERS_HARD, "uint_ulong", native_uint, native_ulong, H5T__conv_uint_ulong);
    status |= H5T__register_int(H5T_PERS_HARD, "int_short", native_int, native_short, H5T__conv_int_short);
    status |= H5T__register_int(H5T_PERS_HARD, "int_ushort", native_int, native_ushort, H5T__conv_int_ushort);
    status |= H5T__register_int(H5T_PERS_HARD, "uint_short", native_uint, native_short, H5T__conv_uint_short);
    status |=
        H5T__register_int(H5T_PERS_HARD, "uint_ushort", native_uint, native_ushort, H5T__conv_uint_ushort);
    status |= H5T__register_int(H5T_PERS_HARD, "int_uint", native_int, native_uint, H5T__conv_int_uint);
    status |= H5T__register_int(H5T_PERS_HARD, "uint_int", native_uint, native_int, H5T__conv_uint_int);
    status |= H5T__register_int(H5T_PERS_HARD, "int_schar", native_int, native_schar, H5T__conv_int_schar);
    status |= H5T__register_int(H5T_PERS_HARD, "int_uchar", native_int, native_uchar, H5T__conv_int_uchar);
    status |= H5T__register_int(H5T_PERS_HARD, "uint_schar", native_uint, native_schar, H5T__conv_uint_schar);
    status |= H5T__register_int(H5T_PERS_HARD, "uint_uchar", native_uint, native_uchar, H5T__conv_uint_uchar);

    /* From char */
    status |=
        H5T__register_int(H5T_PERS_HARD, "schar_llong", native_schar, native_llong, H5T__conv_schar_llong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "schar_ullong", native_schar, native_ullong, H5T__conv_schar_ullong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "uchar_llong", native_uchar, native_llong, H5T__conv_uchar_llong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "uchar_ullong", native_uchar, native_ullong, H5T__conv_uchar_ullong);
    status |= H5T__register_int(H5T_PERS_HARD, "schar_long", native_schar, native_long, H5T__conv_schar_long);
    status |=
        H5T__register_int(H5T_PERS_HARD, "schar_ulong", native_schar, native_ulong, H5T__conv_schar_ulong);
    status |= H5T__register_int(H5T_PERS_HARD, "uchar_long", native_uchar, native_long, H5T__conv_uchar_long);
    status |=
        H5T__register_int(H5T_PERS_HARD, "uchar_ulong", native_uchar, native_ulong, H5T__conv_uchar_ulong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "schar_short", native_schar, native_short, H5T__conv_schar_short);
    status |=
        H5T__register_int(H5T_PERS_HARD, "schar_ushort", native_schar, native_ushort, H5T__conv_schar_ushort);
    status |=
        H5T__register_int(H5T_PERS_HARD, "uchar_short", native_uchar, native_short, H5T__conv_uchar_short);
    status |=
        H5T__register_int(H5T_PERS_HARD, "uchar_ushort", native_uchar, native_ushort, H5T__conv_uchar_ushort);
    status |= H5T__register_int(H5T_PERS_HARD, "schar_int", native_schar, native_int, H5T__conv_schar_int);
    status |= H5T__register_int(H5T_PERS_HARD, "schar_uint", native_schar, native_uint, H5T__conv_schar_uint);
    status |= H5T__register_int(H5T_PERS_HARD, "uchar_int", native_uchar, native_int, H5T__conv_uchar_int);
    status |= H5T__register_int(H5T_PERS_HARD, "uchar_uint", native_uchar, native_uint, H5T__conv_uchar_uint);
    status |=
        H5T__register_int(H5T_PERS_HARD, "schar_uchar", native_schar, native_uchar, H5T__conv_schar_uchar);
    status |=
        H5T__register_int(H5T_PERS_HARD, "uchar_schar", native_uchar, native_schar, H5T__conv_uchar_schar);

    /* From char to floats */
    status |=
        H5T__register_int(H5T_PERS_HARD, "schar_flt", native_schar, native_float, H5T__conv_schar_float);
    status |=
        H5T__register_int(H5T_PERS_HARD, "schar_dbl", native_schar, native_double, H5T__conv_schar_double);
    status |=
        H5T__register_int(H5T_PERS_HARD, "schar_ldbl", native_schar, native_ldouble, H5T__conv_schar_ldouble);
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "schar_flt16", native_schar, native_float16,
                                H5T__conv_schar__Float16);
#endif

    /* From unsigned char to floats */
    status |=
        H5T__register_int(H5T_PERS_HARD, "uchar_flt", native_uchar, native_float, H5T__conv_uchar_float);
    status |=
        H5T__register_int(H5T_PERS_HARD, "uchar_dbl", native_uchar, native_double, H5T__conv_uchar_double);
    status |=
        H5T__register_int(H5T_PERS_HARD, "uchar_ldbl", native_uchar, native_ldouble, H5T__conv_uchar_ldouble);
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "uchar_flt16", native_uchar, native_float16,
                                H5T__conv_uchar__Float16);
#endif

    /* From short to floats */
    status |=
        H5T__register_int(H5T_PERS_HARD, "short_flt", native_short, native_float, H5T__conv_short_float);
    status |=
        H5T__register_int(H5T_PERS_HARD, "short_dbl", native_short, native_double, H5T__conv_short_double);
    status |=
        H5T__register_int(H5T_PERS_HARD, "short_ldbl", native_short, native_ldouble, H5T__conv_short_ldouble);
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "short_flt16", native_short, native_float16,
                                H5T__conv_short__Float16);
#endif

    /* From unsigned short to floats */
    status |=
        H5T__register_int(H5T_PERS_HARD, "ushort_flt", native_ushort, native_float, H5T__conv_ushort_float);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ushort_dbl", native_ushort, native_double, H5T__conv_ushort_double);
    status |= H5T__register_int(H5T_PERS_HARD, "ushort_ldbl", native_ushort, native_ldouble,
                                H5T__conv_ushort_ldouble);
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "ushort_flt16", native_ushort, native_float16,
                                H5T__conv_ushort__Float16);
#endif

    /* From int to floats */
    status |= H5T__register_int(H5T_PERS_HARD, "int_flt", native_int, native_float, H5T__conv_int_float);
    status |= H5T__register_int(H5T_PERS_HARD, "int_dbl", native_int, native_double, H5T__conv_int_double);
    status |= H5T__register_int(H5T_PERS_HARD, "int_ldbl", native_int, native_ldouble, H5T__conv_int_ldouble);
#ifdef H5_HAVE__FLOAT16
    status |=
        H5T__register_int(H5T_PERS_HARD, "int_flt16", native_int, native_float16, H5T__conv_int__Float16);
#endif

    /* From unsigned int to floats */
    status |= H5T__register_int(H5T_PERS_HARD, "uint_flt", native_uint, native_float, H5T__conv_uint_float);
    status |= H5T__register_int(H5T_PERS_HARD, "uint_dbl", native_uint, native_double, H5T__conv_uint_double);
    status |=
        H5T__register_int(H5T_PERS_HARD, "uint_ldbl", native_uint, native_ldouble, H5T__conv_uint_ldouble);
#ifdef H5_HAVE__FLOAT16
    status |=
        H5T__register_int(H5T_PERS_HARD, "uint_flt16", native_uint, native_float16, H5T__conv_uint__Float16);
#endif

    /* From long to floats */
    status |= H5T__register_int(H5T_PERS_HARD, "long_flt", native_long, native_float, H5T__conv_long_float);
    status |= H5T__register_int(H5T_PERS_HARD, "long_dbl", native_long, native_double, H5T__conv_long_double);
    status |=
        H5T__register_int(H5T_PERS_HARD, "long_ldbl", native_long, native_ldouble, H5T__conv_long_ldouble);
#ifdef H5_HAVE__FLOAT16
    status |=
        H5T__register_int(H5T_PERS_HARD, "long_flt16", native_long, native_float16, H5T__conv_long__Float16);
#endif

    /* From unsigned long to floats */
    status |=
        H5T__register_int(H5T_PERS_HARD, "ulong_flt", native_ulong, native_float, H5T__conv_ulong_float);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ulong_dbl", native_ulong, native_double, H5T__conv_ulong_double);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ulong_ldbl", native_ulong, native_ldouble, H5T__conv_ulong_ldouble);
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "ulong_flt16", native_ulong, native_float16,
                                H5T__conv_ulong__Float16);
#endif

    /* From long long to floats */
    status |=
        H5T__register_int(H5T_PERS_HARD, "llong_flt", native_llong, native_float, H5T__conv_llong_float);
    status |=
        H5T__register_int(H5T_PERS_HARD, "llong_dbl", native_llong, native_double, H5T__conv_llong_double);
#ifdef H5T_CONV_INTERNAL_LLONG_LDOUBLE
    status |=
        H5T__register_int(H5T_PERS_HARD, "llong_ldbl", native_llong, native_ldouble, H5T__conv_llong_ldouble);
#endif /* H5T_CONV_INTERNAL_LLONG_LDOUBLE */
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "llong_flt16", native_llong, native_float16,
                                H5T__conv_llong__Float16);
#endif

    /* From unsigned long long to floats */
    status |=
        H5T__register_int(H5T_PERS_HARD, "ullong_flt", native_ullong, native_float, H5T__conv_ullong_float);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ullong_dbl", native_ullong, native_double, H5T__conv_ullong_double);
#ifdef H5T_CONV_INTERNAL_ULLONG_LDOUBLE
    status |= H5T__register_int(H5T_PERS_HARD, "ullong_ldbl", native_ullong, native_ldouble,
                                H5T__conv_ullong_ldouble);
#endif /* H5T_CONV_INTERNAL_ULLONG_LDOUBLE */
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "ullong_flt16", native_ullong, native_float16,
                                H5T__conv_ullong__Float16);
#endif

    /* From floats to char */
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt_schar", native_float, native_schar, H5T__conv_float_schar);
    status |=
        H5T__register_int(H5T_PERS_HARD, "dbl_schar", native_double, native_schar, H5T__conv_double_schar);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ldbl_schar", native_ldouble, native_schar, H5T__conv_ldouble_schar);
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "flt16_schar", native_float16, native_schar,
                                H5T__conv__Float16_schar);
#endif

    /* From floats to unsigned char */
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt_uchar", native_float, native_uchar, H5T__conv_float_uchar);
    status |=
        H5T__register_int(H5T_PERS_HARD, "dbl_uchar", native_double, native_uchar, H5T__conv_double_uchar);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ldbl_uchar", native_ldouble, native_uchar, H5T__conv_ldouble_uchar);
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "flt16_uchar", native_float16, native_uchar,
                                H5T__conv__Float16_uchar);
#endif

    /* From floats to short */
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt_short", native_float, native_short, H5T__conv_float_short);
    status |=
        H5T__register_int(H5T_PERS_HARD, "dbl_short", native_double, native_short, H5T__conv_double_short);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ldbl_short", native_ldouble, native_short, H5T__conv_ldouble_short);
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "flt16_short", native_float16, native_short,
                                H5T__conv__Float16_short);
#endif

    /* From floats to unsigned short */
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt_ushort", native_float, native_ushort, H5T__conv_float_ushort);
    status |=
        H5T__register_int(H5T_PERS_HARD, "dbl_ushort", native_double, native_ushort, H5T__conv_double_ushort);
    status |= H5T__register_int(H5T_PERS_HARD, "ldbl_ushort", native_ldouble, native_ushort,
                                H5T__conv_ldouble_ushort);
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "flt16_ushort", native_float16, native_ushort,
                                H5T__conv__Float16_ushort);
#endif

    /* From floats to int */
    status |= H5T__register_int(H5T_PERS_HARD, "flt_int", native_float, native_int, H5T__conv_float_int);
    status |= H5T__register_int(H5T_PERS_HARD, "dbl_int", native_double, native_int, H5T__conv_double_int);
    status |= H5T__register_int(H5T_PERS_HARD, "ldbl_int", native_ldouble, native_int, H5T__conv_ldouble_int);
#ifdef H5_HAVE__FLOAT16
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt16_int", native_float16, native_int, H5T__conv__Float16_int);
#endif

    /* From floats to unsigned int */
    status |= H5T__register_int(H5T_PERS_HARD, "flt_uint", native_float, native_uint, H5T__conv_float_uint);
    status |= H5T__register_int(H5T_PERS_HARD, "dbl_uint", native_double, native_uint, H5T__conv_double_uint);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ldbl_uint", native_ldouble, native_uint, H5T__conv_ldouble_uint);
#ifdef H5_HAVE__FLOAT16
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt16_uint", native_float16, native_uint, H5T__conv__Float16_uint);
#endif

    /* From floats to long */
    status |= H5T__register_int(H5T_PERS_HARD, "flt_long", native_float, native_long, H5T__conv_float_long);
    status |= H5T__register_int(H5T_PERS_HARD, "dbl_long", native_double, native_long, H5T__conv_double_long);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ldbl_long", native_ldouble, native_long, H5T__conv_ldouble_long);
#ifdef H5_HAVE__FLOAT16
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt16_long", native_float16, native_long, H5T__conv__Float16_long);
#endif

    /* From floats to unsigned long */
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt_ulong", native_float, native_ulong, H5T__conv_float_ulong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "dbl_ulong", native_double, native_ulong, H5T__conv_double_ulong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "ldbl_ulong", native_ldouble, native_ulong, H5T__conv_ldouble_ulong);
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "flt16_ulong", native_float16, native_ulong,
                                H5T__conv__Float16_ulong);
#endif

    /* From floats to long long */
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt_llong", native_float, native_llong, H5T__conv_float_llong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "dbl_llong", native_double, native_llong, H5T__conv_double_llong);
#ifdef H5T_CONV_INTERNAL_LDOUBLE_LLONG
    status |=
        H5T__register_int(H5T_PERS_HARD, "ldbl_llong", native_ldouble, native_llong, H5T__conv_ldouble_llong);
#endif /* H5T_CONV_INTERNAL_LDOUBLE_LLONG */
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "flt16_llong", native_float16, native_llong,
                                H5T__conv__Float16_llong);
#endif

    /* From floats to unsigned long long */
    status |=
        H5T__register_int(H5T_PERS_HARD, "flt_ullong", native_float, native_ullong, H5T__conv_float_ullong);
    status |=
        H5T__register_int(H5T_PERS_HARD, "dbl_ullong", native_double, native_ullong, H5T__conv_double_ullong);
#if H5T_CONV_INTERNAL_LDOUBLE_ULLONG
    status |= H5T__register_int(H5T_PERS_HARD, "ldbl_ullong", native_ldouble, native_ullong,
                                H5T__conv_ldouble_ullong);
#endif /* H5T_CONV_INTERNAL_LDOUBLE_ULLONG */
#ifdef H5_HAVE__FLOAT16
    status |= H5T__register_int(H5T_PERS_HARD, "flt16_ullong", native_float16, native_ullong,
                                H5T__conv__Float16_ullong);
#endif

    /*
     * The special no-op conversion is the fastest, so we list it last. The
     * data types we use are not important as long as the source and
     * destination are equal.
     */
    status |= H5T__register_int(H5T_PERS_HARD, "no-op", native_int, native_int, H5T__conv_noop);

    /* Initialize the +/- Infinity values for floating-point types */
    status |= H5T__init_inf();

    if (status < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to register conversion function(s)");

    /* Register datatype creation property class properties here.  See similar
     * code in H5D_init(), etc. for example.
     */

    /* Only register the default property list if it hasn't been created yet */
    if (H5P_LST_DATATYPE_CREATE_ID_g == (-1)) {
        /* ========== Datatype Creation Property Class Initialization ============*/
        assert(H5P_CLS_DATATYPE_CREATE_g != NULL);

        /* Register the default datatype creation property list */
        if ((H5P_LST_DATATYPE_CREATE_ID_g = H5P_create_id(H5P_CLS_DATATYPE_CREATE_g, false)) < 0)
            HGOTO_ERROR(H5E_PLIST, H5E_CANTREGISTER, FAIL, "can't insert property into class");
    } /* end if */

done:
    /* General cleanup */
    if (compound != NULL)
        (void)H5T_close_real(compound);
    if (enum_type != NULL)
        (void)H5T_close_real(enum_type);
    if (vlen != NULL)
        (void)H5T_close_real(vlen);
    if (array != NULL)
        (void)H5T_close_real(array);

    /* Error cleanup */
    if (ret_value < 0) {
        if (dt) {
            /* Check if we should call H5T_close_real or H5FL_FREE */
            if (copied_dtype)
                (void)H5T_close_real(dt);
            else {
                if (dt->shared->owned_vol_obj && H5VL_free_object(dt->shared->owned_vol_obj) < 0)
                    HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "unable to close owned VOL object");
                dt->shared = H5FL_FREE(H5T_shared_t, dt->shared);
                dt         = H5FL_FREE(H5T_t, dt);
            } /* end else */
        }     /* end if */
    }         /* end if */

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_init() */

/*-------------------------------------------------------------------------
 * Function:   H5T__unlock_cb
 *
 * Purpose:    Clear the immutable flag for a datatype.  This function is
 *             called when the library is closing in order to unlock all
 *             registered datatypes and thus make them free-able.
 *
 * Return:     Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
static int
H5T__unlock_cb(void *_dt, hid_t H5_ATTR_UNUSED id, void *_udata)
{
    H5T_t *dt = (H5T_t *)_dt;
    int   *n  = (int *)_udata;

    FUNC_ENTER_PACKAGE_NOERR

    assert(dt);

    if (dt->shared && (H5T_STATE_IMMUTABLE == dt->shared->state)) {
        dt->shared->state = H5T_STATE_RDONLY;
        (*n)++;
    }

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* end H5T__unlock_cb() */

/*-------------------------------------------------------------------------
 * Function:   H5T_top_term_package
 *
 * Purpose:    Close the "top" of the interface, releasing IDs, etc.
 *
 * Return:     Success:    Positive if any action might have caused a
 *                            change in some other interface; zero otherwise.
 *             Failure:    Negative
 *
 *-------------------------------------------------------------------------
 */
int
H5T_top_term_package(void)
{
    int n = 0;

    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /* Unregister all conversion functions */
    if (H5T_g.path) {
        H5T_conv_ctx_t conv_ctx = {0};

        conv_ctx.u.free.src_type_id = H5I_INVALID_HID;
        conv_ctx.u.free.dst_type_id = H5I_INVALID_HID;

        for (int i = 0; i < H5T_g.npaths; i++) {
            H5T_path_t *path = H5T_g.path[i];

            (void)H5T__path_free(path, &conv_ctx);

            H5T_g.path[i] = NULL;
        } /* end for */

        /* Clear conversion tables */
        H5T_g.path   = (H5T_path_t **)H5MM_xfree(H5T_g.path);
        H5T_g.npaths = 0;
        H5T_g.apaths = 0;
        H5T_g.soft   = (H5T_soft_t *)H5MM_xfree(H5T_g.soft);
        H5T_g.nsoft  = 0;
        H5T_g.asoft  = 0;

        n++;
    } /* end if */

    /* Unlock all datatypes, then free them */
    /* note that we are ignoring the return value from H5I_iterate() */
    /* Also note that we are incrementing 'n' in the callback */
    H5I_iterate(H5I_DATATYPE, H5T__unlock_cb, &n, false);

    /* Release all datatype IDs */
    if (H5I_nmembers(H5I_DATATYPE) > 0) {
        (void)H5I_clear_type(H5I_DATATYPE, false, false);
        n++; /*H5I*/
    }        /* end if */

    /* Reset all the datatype IDs */
    if (H5T_IEEE_F32BE_g > 0) {
        H5T_IEEE_F16BE_g = H5I_INVALID_HID;
        H5T_IEEE_F16LE_g = H5I_INVALID_HID;
        H5T_IEEE_F32BE_g = H5I_INVALID_HID;
        H5T_IEEE_F32LE_g = H5I_INVALID_HID;
        H5T_IEEE_F64BE_g = H5I_INVALID_HID;
        H5T_IEEE_F64LE_g = H5I_INVALID_HID;

        H5T_STD_I8BE_g        = H5I_INVALID_HID;
        H5T_STD_I8LE_g        = H5I_INVALID_HID;
        H5T_STD_I16BE_g       = H5I_INVALID_HID;
        H5T_STD_I16LE_g       = H5I_INVALID_HID;
        H5T_STD_I32BE_g       = H5I_INVALID_HID;
        H5T_STD_I32LE_g       = H5I_INVALID_HID;
        H5T_STD_I64BE_g       = H5I_INVALID_HID;
        H5T_STD_I64LE_g       = H5I_INVALID_HID;
        H5T_STD_U8BE_g        = H5I_INVALID_HID;
        H5T_STD_U8LE_g        = H5I_INVALID_HID;
        H5T_STD_U16BE_g       = H5I_INVALID_HID;
        H5T_STD_U16LE_g       = H5I_INVALID_HID;
        H5T_STD_U32BE_g       = H5I_INVALID_HID;
        H5T_STD_U32LE_g       = H5I_INVALID_HID;
        H5T_STD_U64BE_g       = H5I_INVALID_HID;
        H5T_STD_U64LE_g       = H5I_INVALID_HID;
        H5T_STD_B8BE_g        = H5I_INVALID_HID;
        H5T_STD_B8LE_g        = H5I_INVALID_HID;
        H5T_STD_B16BE_g       = H5I_INVALID_HID;
        H5T_STD_B16LE_g       = H5I_INVALID_HID;
        H5T_STD_B32BE_g       = H5I_INVALID_HID;
        H5T_STD_B32LE_g       = H5I_INVALID_HID;
        H5T_STD_B64BE_g       = H5I_INVALID_HID;
        H5T_STD_B64LE_g       = H5I_INVALID_HID;
        H5T_STD_REF_OBJ_g     = H5I_INVALID_HID;
        H5T_STD_REF_DSETREG_g = H5I_INVALID_HID;
        H5T_STD_REF_g         = H5I_INVALID_HID;

        H5T_UNIX_D32BE_g = H5I_INVALID_HID;
        H5T_UNIX_D32LE_g = H5I_INVALID_HID;
        H5T_UNIX_D64BE_g = H5I_INVALID_HID;
        H5T_UNIX_D64LE_g = H5I_INVALID_HID;

        H5T_C_S1_g = H5I_INVALID_HID;

        H5T_FORTRAN_S1_g = H5I_INVALID_HID;

        H5T_NATIVE_SCHAR_g   = H5I_INVALID_HID;
        H5T_NATIVE_UCHAR_g   = H5I_INVALID_HID;
        H5T_NATIVE_SHORT_g   = H5I_INVALID_HID;
        H5T_NATIVE_USHORT_g  = H5I_INVALID_HID;
        H5T_NATIVE_INT_g     = H5I_INVALID_HID;
        H5T_NATIVE_UINT_g    = H5I_INVALID_HID;
        H5T_NATIVE_LONG_g    = H5I_INVALID_HID;
        H5T_NATIVE_ULONG_g   = H5I_INVALID_HID;
        H5T_NATIVE_LLONG_g   = H5I_INVALID_HID;
        H5T_NATIVE_ULLONG_g  = H5I_INVALID_HID;
        H5T_NATIVE_FLOAT16_g = H5I_INVALID_HID;
        H5T_NATIVE_FLOAT_g   = H5I_INVALID_HID;
        H5T_NATIVE_DOUBLE_g  = H5I_INVALID_HID;
        H5T_NATIVE_LDOUBLE_g = H5I_INVALID_HID;
        H5T_NATIVE_B8_g      = H5I_INVALID_HID;
        H5T_NATIVE_B16_g     = H5I_INVALID_HID;
        H5T_NATIVE_B32_g     = H5I_INVALID_HID;
        H5T_NATIVE_B64_g     = H5I_INVALID_HID;
        H5T_NATIVE_OPAQUE_g  = H5I_INVALID_HID;
        H5T_NATIVE_HADDR_g   = H5I_INVALID_HID;
        H5T_NATIVE_HSIZE_g   = H5I_INVALID_HID;
        H5T_NATIVE_HSSIZE_g  = H5I_INVALID_HID;
        H5T_NATIVE_HERR_g    = H5I_INVALID_HID;
        H5T_NATIVE_HBOOL_g   = H5I_INVALID_HID;

        H5T_NATIVE_INT8_g        = H5I_INVALID_HID;
        H5T_NATIVE_UINT8_g       = H5I_INVALID_HID;
        H5T_NATIVE_INT_LEAST8_g  = H5I_INVALID_HID;
        H5T_NATIVE_UINT_LEAST8_g = H5I_INVALID_HID;
        H5T_NATIVE_INT_FAST8_g   = H5I_INVALID_HID;
        H5T_NATIVE_UINT_FAST8_g  = H5I_INVALID_HID;

        H5T_NATIVE_INT16_g        = H5I_INVALID_HID;
        H5T_NATIVE_UINT16_g       = H5I_INVALID_HID;
        H5T_NATIVE_INT_LEAST16_g  = H5I_INVALID_HID;
        H5T_NATIVE_UINT_LEAST16_g = H5I_INVALID_HID;
        H5T_NATIVE_INT_FAST16_g   = H5I_INVALID_HID;
        H5T_NATIVE_UINT_FAST16_g  = H5I_INVALID_HID;

        H5T_NATIVE_INT32_g        = H5I_INVALID_HID;
        H5T_NATIVE_UINT32_g       = H5I_INVALID_HID;
        H5T_NATIVE_INT_LEAST32_g  = H5I_INVALID_HID;
        H5T_NATIVE_UINT_LEAST32_g = H5I_INVALID_HID;
        H5T_NATIVE_INT_FAST32_g   = H5I_INVALID_HID;
        H5T_NATIVE_UINT_FAST32_g  = H5I_INVALID_HID;

        H5T_NATIVE_INT64_g        = H5I_INVALID_HID;
        H5T_NATIVE_UINT64_g       = H5I_INVALID_HID;
        H5T_NATIVE_INT_LEAST64_g  = H5I_INVALID_HID;
        H5T_NATIVE_UINT_LEAST64_g = H5I_INVALID_HID;
        H5T_NATIVE_INT_FAST64_g   = H5I_INVALID_HID;
        H5T_NATIVE_UINT_FAST64_g  = H5I_INVALID_HID;

        n++;
    } /* end if */

    FUNC_LEAVE_NOAPI(n)
} /* end H5T_top_term_package() */

/*-------------------------------------------------------------------------
 * Function:   H5T_term_package
 *
 * Purpose:    Close this interface.
 *
 * Note:    Finishes shutting down the interface, after
 *          H5T_top_term_package() is called
 *
 * Return:   Success:    Positive if any action might have caused a
 *                change in some other interface; zero
 *                otherwise.
 *           Failure:    Negative
 *
 *-------------------------------------------------------------------------
 */
int
H5T_term_package(void)
{
    int n = 0;

    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /* Sanity check */
    assert(0 == H5I_nmembers(H5I_DATATYPE));

    /* Destroy the datatype object id group */
    n += (H5I_dec_type_ref(H5I_DATATYPE) > 0);

    FUNC_LEAVE_NOAPI(n)
} /* end H5T_term_package() */

/*-------------------------------------------------------------------------
 * Function:    H5T__close_cb
 *
 * Purpose:     Called when the ref count reaches zero on the datatype's ID
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5T__close_cb(H5T_t *dt, void **request)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Sanity check */
    assert(dt);

    /* If this datatype is VOL-managed (i.e.: has a VOL object),
     * close it through the VOL connector.
     */
    if (NULL != dt->vol_obj) {
        /* Close the connector-managed datatype data */
        if (H5VL_datatype_close(dt->vol_obj, H5P_DATASET_XFER_DEFAULT, request) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CLOSEERROR, FAIL, "unable to close datatype");

        /* Free the VOL object */
        if (H5VL_free_object(dt->vol_obj) < 0)
            HGOTO_ERROR(H5E_ATTR, H5E_CANTDEC, FAIL, "unable to free VOL object");
        dt->vol_obj = NULL;
    } /* end if */

    /* Close the datatype */
    if (H5T_close(dt) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CLOSEERROR, FAIL, "unable to close datatype");

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__close_cb() */

/*-------------------------------------------------------------------------
 * Function:   H5Tcreate
 *
 * Purpose:    Create a new type and initialize it to reasonable values.
 *             The type is a member of type class TYPE and is SIZE bytes.
 *
 * Return:    Success:    A new type identifier.
 *
 *            Failure:    Negative
 *
 * Errors:
 *        ARGS      BADVALUE    Invalid size.
 *        DATATYPE  CANTINIT    Can't create type.
 *        DATATYPE  CANTREGISTER    Can't register datatype ID.
 *
 *-------------------------------------------------------------------------
 */
hid_t
H5Tcreate(H5T_class_t type, size_t size)
{
    H5T_t *dt = NULL; /* New datatype constructed */
    hid_t  ret_value; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* check args. We support string (fixed-size or variable-length) now. */
    if (size <= 0 && size != H5T_VARIABLE)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "size must be positive");

    /* create the type */
    if (NULL == (dt = H5T__create(type, size)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to create type");

    /* Get an ID for the datatype */
    if ((ret_value = H5I_register(H5I_DATATYPE, dt, true)) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTREGISTER, FAIL, "unable to register datatype ID");

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tcreate() */

/*-------------------------------------------------------------------------
 * Function:    H5Tcopy
 *
 * Purpose:     Copies a datatype.  The resulting datatype is not locked.
 *              The datatype should be closed when no longer needed by
 *              calling H5Tclose().
 *
 * Return:      Success:    The ID of a new datatype
 *
 *              Failure:    H5I_INVALID_HID
 *
 * Note:        The returned type is always transient and unlocked. If the
 *              TYPE_ID argument is a dataset instead of a datatype then
 *              this function returns a transient, modifiable datatype
 *              which is a copy of the dataset's datatype.
 *
 *-------------------------------------------------------------------------
 */
hid_t
H5Tcopy(hid_t obj_id)
{
    H5T_t *dt        = NULL;            /* Pointer to the datatype to copy */
    H5T_t *new_dt    = NULL;            /* Pointer to the new datatype */
    hid_t  dset_tid  = H5I_INVALID_HID; /* Datatype ID from dataset */
    hid_t  ret_value = H5I_INVALID_HID; /* Return value */

    FUNC_ENTER_API(H5I_INVALID_HID)

    switch (H5I_get_type(obj_id)) {
        case H5I_DATATYPE:
            /* The argument is a datatype handle */
            if (NULL == (dt = (H5T_t *)H5I_object(obj_id)))
                HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, H5I_INVALID_HID, "obj_id is not a datatype ID");
            break;

        case H5I_DATASET: {
            H5VL_object_t          *vol_obj;     /* Object for obj_id */
            H5VL_dataset_get_args_t vol_cb_args; /* Arguments to VOL callback */

            /* The argument is a dataset handle */
            if (NULL == (vol_obj = (H5VL_object_t *)H5I_object_verify(obj_id, H5I_DATASET)))
                HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, H5I_INVALID_HID, "type_id is not a dataset ID");

            /* Set up VOL callback arguments */
            vol_cb_args.op_type               = H5VL_DATASET_GET_TYPE;
            vol_cb_args.args.get_type.type_id = H5I_INVALID_HID;

            /* Get the datatype from the dataset
             * NOTE: This will have to be closed after we're done with it.
             */
            if (H5VL_dataset_get(vol_obj, &vol_cb_args, H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, H5I_INVALID_HID,
                            "unable to get datatype from the dataset");
            dset_tid = vol_cb_args.args.get_type.type_id;

            /* Unwrap the type ID */
            if (NULL == (dt = (H5T_t *)H5I_object(dset_tid)))
                HGOTO_ERROR(H5E_DATATYPE, H5E_BADTYPE, H5I_INVALID_HID,
                            "received invalid datatype from the dataset");
        } break;

        case H5I_UNINIT:
        case H5I_BADID:
        case H5I_FILE:
        case H5I_GROUP:
        case H5I_DATASPACE:
        case H5I_ATTR:
        case H5I_MAP:
        case H5I_VFL:
        case H5I_VOL:
        case H5I_GENPROP_CLS:
        case H5I_GENPROP_LST:
        case H5I_ERROR_CLASS:
        case H5I_ERROR_MSG:
        case H5I_ERROR_STACK:
        case H5I_SPACE_SEL_ITER:
        case H5I_EVENTSET:
        case H5I_NTYPES:
        default:
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, H5I_INVALID_HID, "not a datatype or dataset");
    } /* end switch */

    /* Copy datatype */
    if (NULL == (new_dt = H5T_copy(dt, H5T_COPY_TRANSIENT)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, H5I_INVALID_HID, "unable to copy");

    /* Get an ID for the copied datatype */
    if ((ret_value = H5I_register(H5I_DATATYPE, new_dt, true)) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTREGISTER, H5I_INVALID_HID, "unable to register datatype atom");

done:

    /* If we got a type ID from a passed-in dataset, we need to close that */
    if (dset_tid != H5I_INVALID_HID)
        if (H5I_dec_app_ref(dset_tid) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_BADID, FAIL, "problem freeing temporary dataset type ID");

    /* Close the new datatype on errors */
    if (H5I_INVALID_HID == ret_value)
        if (new_dt && H5T_close_real(new_dt) < 0)
            HDONE_ERROR(H5E_DATATYPE, H5E_CANTRELEASE, H5I_INVALID_HID, "unable to release datatype info");

    FUNC_LEAVE_API(ret_value)
} /* end H5Tcopy() */

/*-------------------------------------------------------------------------
 * Function:    H5Tclose
 *
 * Purpose:     Frees a datatype and all associated memory.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Tclose(hid_t type_id)
{
    H5T_t *dt;                  /* Pointer to datatype to close */
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (NULL == (dt = (H5T_t *)H5I_object_verify(type_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");
    if (H5T_STATE_IMMUTABLE == dt->shared->state)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "immutable datatype");

    /* When the reference count reaches zero the resources are freed */
    if (H5I_dec_app_ref(type_id) < 0)
        HGOTO_ERROR(H5E_ID, H5E_BADID, FAIL, "problem freeing id");

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tclose() */

/*-------------------------------------------------------------------------
 * Function:    H5Tclose_async
 *
 * Purpose:     Asynchronous version of H5Tclose.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Tclose_async(const char *app_file, const char *app_func, unsigned app_line, hid_t type_id, hid_t es_id)
{
    H5T_t         *dt;                          /* Pointer to datatype to close */
    void          *token     = NULL;            /* Request token for async operation        */
    void         **token_ptr = H5_REQUEST_NULL; /* Pointer to request token for async operation        */
    H5VL_object_t *vol_obj   = NULL;            /* VOL object of dset_id */
    H5VL_t        *connector = NULL;            /* VOL connector */
    herr_t         ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (NULL == (dt = (H5T_t *)H5I_object_verify(type_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");
    if (H5T_STATE_IMMUTABLE == dt->shared->state)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "immutable datatype");

    /* Get dataset object's connector */
    if (NULL == (vol_obj = H5VL_vol_object(type_id)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, "can't get VOL object for dataset");

    /* Prepare for possible asynchronous operation */
    if (H5ES_NONE != es_id) {
        /* Increase connector's refcount, so it doesn't get closed if closing
         * the dataset closes the file */
        connector = vol_obj->connector;
        H5VL_conn_inc_rc(connector);

        /* Point at token for operation to set up */
        token_ptr = &token;
    } /* end if */

    /* When the reference count reaches zero the resources are freed */
    if (H5I_dec_app_ref_async(type_id, token_ptr) < 0)
        HGOTO_ERROR(H5E_ID, H5E_BADID, FAIL, "problem freeing id");

    /* If a token was created, add the token to the event set */
    if (NULL != token)
        if (H5ES_insert(es_id, vol_obj->connector, token,
                        H5ARG_TRACE5(__func__, "*s*sIuii", app_file, app_func, app_line, type_id, es_id)) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINSERT, FAIL, "can't insert token into event set");

done:
    if (connector && H5VL_conn_dec_rc(connector) < 0)
        HDONE_ERROR(H5E_DATATYPE, H5E_CANTDEC, FAIL, "can't decrement ref count on connector");

    FUNC_LEAVE_API(ret_value)
} /* end H5Tclose_async() */

/*-------------------------------------------------------------------------
 * Function:  H5Tequal
 *
 * Purpose:   Determines if two datatypes are equal.
 *
 * Return:    Success:    true if equal, false if unequal
 *
 *            Failure:    Negative
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5Tequal(hid_t type1_id, hid_t type2_id)
{
    const H5T_t *dt1;       /* Pointer to first datatype */
    const H5T_t *dt2;       /* Pointer to second datatype */
    htri_t       ret_value; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* check args */
    if (NULL == (dt1 = (H5T_t *)H5I_object_verify(type1_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");
    if (NULL == (dt2 = (H5T_t *)H5I_object_verify(type2_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");

    ret_value = (0 == H5T_cmp(dt1, dt2, false)) ? true : false;

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tequal() */

/*-------------------------------------------------------------------------
 * Function:    H5Tlock
 *
 * Purpose:    Locks a type, making it read only and non-destructable.
 *        This is normally done by the library for predefined datatypes so
 *        the application doesn't inadvertently change or delete a
 *        predefined type.
 *
 *        Once a datatype is locked it can never be unlocked unless
 *        the entire library is closed.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Tlock(hid_t type_id)
{
    H5T_t *dt;                  /* Datatype to operate on */
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (NULL == (dt = (H5T_t *)H5I_object_verify(type_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");
    if (H5T_STATE_NAMED == dt->shared->state || H5T_STATE_OPEN == dt->shared->state)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to lock named datatype");

    if (H5T_lock(dt, true) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to lock transient datatype");

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tlock() */

/*-------------------------------------------------------------------------
 * Function:  H5Tget_class
 *
 * Purpose:   Returns the datatype class identifier for datatype TYPE_ID.
 *
 * Return:    Success:    One of the non-negative datatype class constants.
 *
 *            Failure:    H5T_NO_CLASS (Negative)
 *
 *-------------------------------------------------------------------------
 */
H5T_class_t
H5Tget_class(hid_t type_id)
{
    H5T_t      *dt;        /* Pointer to datatype */
    H5T_class_t ret_value; /* Return value */

    FUNC_ENTER_API(H5T_NO_CLASS)

    /* Check args */
    if (NULL == (dt = (H5T_t *)H5I_object_verify(type_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, H5T_NO_CLASS, "not a datatype");

    /* Set return value */
    ret_value = H5T_get_class(dt, false);

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tget_class() */

/*-------------------------------------------------------------------------
 * Function:  H5T_get_class
 *
 * Purpose:   Returns the data type class identifier for a datatype ptr.
 *
 * Return:    Success:    One of the non-negative data type class constants.
 *
 *            Failure:    H5T_NO_CLASS (Negative)
 *
 *-------------------------------------------------------------------------
 */
H5T_class_t
H5T_get_class(const H5T_t *dt, htri_t internal)
{
    H5T_class_t ret_value = H5T_NO_CLASS; /* Return value */

    FUNC_ENTER_NOAPI_NOERR

    assert(dt);

    /* Externally, a VL string is a string; internally, a VL string is a VL. */
    if (internal) {
        ret_value = dt->shared->type;
    }
    else {
        if (H5T_IS_VL_STRING(dt->shared))
            ret_value = H5T_STRING;
        else
            ret_value = dt->shared->type;
    }

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_get_class() */

/*-------------------------------------------------------------------------
 * Function:   H5Tdetect_class
 *
 * Purpose:    Check whether a datatype contains (or is) a certain type of
 *             datatype.
 *
 * Return:     true (1) or false (0) on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5Tdetect_class(hid_t type, H5T_class_t cls)
{
    H5T_t *dt;        /* Datatype to query */
    htri_t ret_value; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (NULL == (dt = (H5T_t *)H5I_object_verify(type, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, H5T_NO_CLASS, "not a datatype");
    if (!(cls > H5T_NO_CLASS && cls < H5T_NCLASSES))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, H5T_NO_CLASS, "not a datatype class");

    /* Set return value */
    if ((ret_value = H5T_detect_class(dt, cls, true)) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, H5T_NO_CLASS, "can't get datatype class");

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tdetect_class() */

/*-------------------------------------------------------------------------
 * Function:   H5T_detect_class
 *
 * Purpose:    Check whether a datatype contains (or is) a certain type of
 *             datatype.
 *
 * Return:     true (1) or false (0) on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5T_detect_class(const H5T_t *dt, H5T_class_t cls, bool from_api)
{
    unsigned i;
    htri_t   ret_value = false; /* Return value */

    FUNC_ENTER_NOAPI_NOERR

    assert(dt);
    assert(cls > H5T_NO_CLASS && cls < H5T_NCLASSES);

    /* Consider VL string as a string for API, as a VL for internal use. */
    /* (note that this check must be performed before checking if the VL
     *  string belongs to the H5T_VLEN class, which would otherwise return
     *  true. -QAK)
     */
    if (from_api && H5T_IS_VL_STRING(dt->shared))
        HGOTO_DONE(H5T_STRING == cls);

    /* Check if this type is the correct type */
    if (dt->shared->type == cls)
        HGOTO_DONE(true);

    /* check for types that might have the correct type as a component */
    switch (dt->shared->type) {
        case H5T_COMPOUND:
            for (i = 0; i < dt->shared->u.compnd.nmembs; i++) {
                htri_t nested_ret; /* Return value from nested call */

                /* Check if this field's type is the correct type */
                if (dt->shared->u.compnd.memb[i].type->shared->type == cls)
                    HGOTO_DONE(true);

                /* Recurse if it's VL, compound, enum or array */
                if (H5T_IS_COMPLEX(dt->shared->u.compnd.memb[i].type->shared->type))
                    if ((nested_ret = H5T_detect_class(dt->shared->u.compnd.memb[i].type, cls, from_api)) !=
                        false)
                        HGOTO_DONE(nested_ret);
            } /* end for */
            break;

        case H5T_ARRAY:
        case H5T_VLEN:
        case H5T_ENUM:
            HGOTO_DONE(H5T_detect_class(dt->shared->parent, cls, from_api));
            break;
        case H5T_NO_CLASS:
        case H5T_INTEGER:
        case H5T_FLOAT:
        case H5T_TIME:
        case H5T_STRING:
        case H5T_BITFIELD:
        case H5T_OPAQUE:
        case H5T_REFERENCE:
        case H5T_NCLASSES:
        default:
            break;
    } /* end if */

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_detect_class() */

/*-------------------------------------------------------------------------
 * Function:  H5Tis_variable_str
 *
 * Purpose:   Check whether a datatype is a variable-length string
 *
 * Return:    true (1) or false (0) on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5Tis_variable_str(hid_t dtype_id)
{
    H5T_t *dt;        /* Datatype to query */
    htri_t ret_value; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (NULL == (dt = (H5T_t *)H5I_object_verify(dtype_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");

    /* Set return value */
    if ((ret_value = H5T_is_variable_str(dt)) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, "can't determine if datatype is VL-string");

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tis_variable_str() */

/*-------------------------------------------------------------------------
 * Function:  H5T_is_variable_str
 *
 * Purpose:   Check whether a datatype is a variable-length string
 *
 * Return:    true (1) or false (0) on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5T_is_variable_str(const H5T_t *dt)
{
    FUNC_ENTER_NOAPI_NOINIT_NOERR

    FUNC_LEAVE_NOAPI(H5T_IS_VL_STRING(dt->shared))
} /* end H5T_is_variable_str() */

/*-------------------------------------------------------------------------
 * Function:  H5Tget_size
 *
 * Purpose:   Determines the total size of a datatype in bytes.
 *
 * Return:    Success:    Size of the datatype in bytes.     The size of
 *                datatype is the size of an instance of that
 *                datatype.
 *
 *            Failure:    0 (valid datatypes are never zero size)
 *
 *-------------------------------------------------------------------------
 */
size_t
H5Tget_size(hid_t type_id)
{
    H5T_t *dt;        /* Datatype to query */
    size_t ret_value; /* Return value */

    FUNC_ENTER_API(0)

    /* Check args */
    if (NULL == (dt = (H5T_t *)H5I_object_verify(type_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, 0, "not a datatype");

    /* size */
    ret_value = H5T_GET_SIZE(dt);

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tget_size() */

/*-------------------------------------------------------------------------
 * Function:    H5Tset_size
 *
 * Purpose:    Sets the total size in bytes for a datatype (this operation
 *        is not permitted on reference datatypes).  If the size is
 *        decreased so that the significant bits of the datatype
 *        extend beyond the edge of the new size, then the `offset'
 *        property is decreased toward zero.  If the `offset' becomes
 *        zero and the significant bits of the datatype still hang
 *        over the edge of the new size, then the number of significant
 *        bits is decreased.
 *
 *        Adjusting the size of an H5T_STRING automatically sets the
 *        precision to 8*size.
 *
 *        All datatypes have a positive size.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Tset_size(hid_t type_id, size_t size)
{
    H5T_t *dt;                  /* Datatype to modify */
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (NULL == (dt = (H5T_t *)H5I_object_verify(type_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");
    if (H5T_STATE_TRANSIENT != dt->shared->state)
        HGOTO_ERROR(H5E_ARGS, H5E_CANTINIT, FAIL, "datatype is read-only");
    if (size <= 0 && size != H5T_VARIABLE)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "size must be positive");
    if (size == H5T_VARIABLE && !H5T_IS_STRING(dt->shared))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "only strings may be variable length");
    if (H5T_ENUM == dt->shared->type && dt->shared->u.enumer.nmembs > 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "operation not allowed after members are defined");
    if (H5T_REFERENCE == dt->shared->type)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "operation not defined for this datatype");

    /* Modify the datatype */
    if (H5T__set_size(dt, size) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to set size for datatype");

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tset_size() */

/*-------------------------------------------------------------------------
 * Function:  H5Tget_super
 *
 * Purpose:   Returns the type from which TYPE is derived. In the case of
 *            an enumeration type the return value is an integer type.
 *
 * Return:    Success:    Type ID for base datatype.
 *
 *            Failure:    negative
 *
 *-------------------------------------------------------------------------
 */
hid_t
H5Tget_super(hid_t type)
{
    H5T_t *dt;                          /* Datatype to query */
    H5T_t *super     = NULL;            /* Supertype */
    hid_t  ret_value = H5I_INVALID_HID; /* Return value */

    FUNC_ENTER_API(H5I_INVALID_HID)

    if (NULL == (dt = (H5T_t *)H5I_object_verify(type, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, H5I_INVALID_HID, "not a datatype");
    if (NULL == (super = H5T_get_super(dt)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, H5I_INVALID_HID, "not a datatype");
    if ((ret_value = H5I_register(H5I_DATATYPE, super, true)) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTREGISTER, H5I_INVALID_HID, "unable to register parent datatype");

done:
    if (H5I_INVALID_HID == ret_value)
        if (super && H5T_close_real(super) < 0)
            HDONE_ERROR(H5E_DATATYPE, H5E_CANTRELEASE, H5I_INVALID_HID,
                        "unable to release super datatype info")

    FUNC_LEAVE_API(ret_value)
} /* end H5Tget_super() */

/*-------------------------------------------------------------------------
 * Function:  H5T_get_super
 *
 * Purpose:   Private function for H5Tget_super.  Returns the type from
 *            which TYPE is derived. In the case of an enumeration type
 *            the return value is an integer type.
 *
 * Return:    Success:    Data type for base data type.
 *            Failure:        NULL
 *
 *-------------------------------------------------------------------------
 */
H5T_t *
H5T_get_super(const H5T_t *dt)
{
    H5T_t *ret_value = NULL;

    FUNC_ENTER_NOAPI(NULL)

    assert(dt);

    if (!dt->shared->parent)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "not a derived data type");
    if (NULL == (ret_value = H5T_copy(dt->shared->parent, H5T_COPY_ALL)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "unable to copy parent data type");

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_get_super() */

/*-------------------------------------------------------------------------
 * Function:    H5T__register_int
 *
 * Purpose:    Register a library internal datatype conversion routine.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5T__register_int(H5T_pers_t pers, const char *name, H5T_t *src, H5T_t *dst, H5T_lib_conv_t func)
{
    H5T_conv_func_t conv_func;           /* Conversion function wrapper */
    herr_t          ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Check args */
    assert(H5T_PERS_HARD == pers || H5T_PERS_SOFT == pers);
    assert(name && *name);
    assert(src);
    assert(dst);
    assert(func);

    /* Set up conversion function wrapper */
    conv_func.is_app     = false;
    conv_func.u.lib_func = func;

    /* Register conversion */
    if (H5T__register(pers, name, src, dst, &conv_func) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL,
                    "unable to register internal datatype conversion routine");

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__register_int() */

/*-------------------------------------------------------------------------
 * Function:    H5T__register
 *
 * Purpose:    Register a hard or soft conversion function for a data type
 *        conversion path.  The path is specified by the source and
 *        destination data types SRC and DST (for soft functions only the
 *        class of these types is important). If FUNC is a hard function
 *        then it replaces any previous path; if it's a soft function then
 *        it replaces all existing paths to which it applies and is used
 *        for any new path to which it applies as long as that path doesn't
 *        have a hard function.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5T__register(H5T_pers_t pers, const char *name, H5T_t *src, H5T_t *dst, H5T_conv_func_t *conv)
{
    H5T_path_t *old_path = NULL;             /*existing conversion path   */
    H5T_path_t *new_path = NULL;             /*new conversion path        */
    H5T_cdata_t cdata;                       /*temporary conversion data  */
    H5T_t      *tmp_stype = NULL;            /*temporary source datatype */
    H5T_t      *tmp_dtype = NULL;            /*temporary destination datatype */
    hid_t       tmp_sid   = H5I_INVALID_HID; /*temporary datatype ID */
    hid_t       tmp_did   = H5I_INVALID_HID; /*temporary datatype ID */
    int         i;                           /*counter                    */
    herr_t      ret_value = SUCCEED;         /*return value               */

    FUNC_ENTER_PACKAGE

    /* Check args */
    assert(src);
    assert(dst);
    assert(conv);
    assert(H5T_PERS_HARD == pers || H5T_PERS_SOFT == pers);
    assert(name && *name);

    if (H5T_PERS_HARD == pers) {
        /* Only bother to register the path if it's not a no-op path (for this machine) */
        if (H5T_cmp(src, dst, false)) {
            /* Locate or create a new conversion path */
            if (NULL == (new_path = H5T__path_find_real(src, dst, name, conv)))
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to locate/allocate conversion path");

            /* Notify all other functions to recalculate private data since some
             * functions might cache a list of conversion functions.  For
             * instance, the compound type converter caches a list of conversion
             * functions for the members, so adding a new function should cause
             * the list to be recalculated to use the new function.
             */
            for (i = 0; i < H5T_g.npaths; i++)
                if (new_path != H5T_g.path[i])
                    H5T_g.path[i]->cdata.recalc = true;
        } /* end if */
    }     /* end if */
    else {
        H5T_conv_ctx_t conv_ctx = {0};

        /*
         * Get the datatype conversion exception callback structure.
         * Note that we have to first check if an API context has been
         * pushed, since we could have arrived here during library
         * initialization of the H5T package.
         */
        if (!conv->is_app && H5CX_pushed() && (H5CX_get_dt_conv_cb(&conv_ctx.u.init.cb_struct) < 0))
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, "unable to get conversion exception callback");

        /* Add function to end of soft list */
        if ((size_t)H5T_g.nsoft >= H5T_g.asoft) {
            size_t      na = MAX(32, 2 * H5T_g.asoft);
            H5T_soft_t *x;

            if (NULL == (x = (H5T_soft_t *)H5MM_realloc(H5T_g.soft, na * sizeof(H5T_soft_t))))
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
            H5T_g.asoft = na;
            H5T_g.soft  = x;
        } /* end if */
        strncpy(H5T_g.soft[H5T_g.nsoft].name, name, (size_t)H5T_NAMELEN - 1);
        H5T_g.soft[H5T_g.nsoft].name[H5T_NAMELEN - 1] = '\0';
        H5T_g.soft[H5T_g.nsoft].src                   = src->shared->type;
        H5T_g.soft[H5T_g.nsoft].dst                   = dst->shared->type;
        H5T_g.soft[H5T_g.nsoft].conv                  = *conv;
        H5T_g.nsoft++;

        /*
         * Any existing path (except the no-op path) to which this new soft
         * conversion function applies should be replaced by a new path that
         * uses this function.
         */
        for (i = 1; i < H5T_g.npaths; i++) {
            old_path = H5T_g.path[i];
            assert(old_path);

            /* Does the new soft conversion function apply to this path? */
            if (old_path->is_hard || old_path->src->shared->type != src->shared->type ||
                old_path->dst->shared->type != dst->shared->type)
                continue;

            memset(&cdata, 0, sizeof cdata);
            cdata.command = H5T_CONV_INIT;
            if (conv->is_app) {
                if (NULL == (tmp_stype = H5T_copy(old_path->src, H5T_COPY_ALL)))
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "unable to copy src datatype");
                if (NULL == (tmp_dtype = H5T_copy(old_path->dst, H5T_COPY_ALL)))
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "unable to copy dst datatype");

                if ((tmp_sid = H5I_register(H5I_DATATYPE, tmp_stype, false)) < 0)
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTREGISTER, FAIL,
                                "unable to register ID for source datatype");
                if ((tmp_did = H5I_register(H5I_DATATYPE, tmp_dtype, false)) < 0)
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTREGISTER, FAIL,
                                "unable to register ID for destination datatype");

                if ((conv->u.app_func)(tmp_sid, tmp_did, &cdata, 0, 0, 0, NULL, NULL, H5CX_get_dxpl()) < 0) {
                    if (H5I_dec_ref(tmp_sid) < 0)
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTDEC, FAIL,
                                    "unable to decrement reference count on temporary ID");
                    if (H5I_dec_ref(tmp_did) < 0)
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTDEC, FAIL,
                                    "unable to decrement reference count on temporary ID");
                    tmp_sid = tmp_did = H5I_INVALID_HID;
                    tmp_stype = tmp_dtype = NULL;
                    if (H5E_clear_stack() < 0)
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTRESET, FAIL, "unable to clear current error stack");
                    continue;
                } /* end if */
            }     /* end if */
            else if ((conv->u.lib_func)(old_path->src, old_path->dst, &cdata, &conv_ctx, 0, 0, 0, NULL,
                                        NULL) < 0) {
                if (H5E_clear_stack() < 0)
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTRESET, FAIL, "unable to clear current error stack");
                continue;
            } /* end if */

            /* Create a new conversion path */
            if (NULL == (new_path = H5FL_CALLOC(H5T_path_t)))
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
            strncpy(new_path->name, name, (size_t)H5T_NAMELEN - 1);
            new_path->name[H5T_NAMELEN - 1] = '\0';
            if (NULL == (new_path->src = H5T_copy(old_path->src, H5T_COPY_ALL)) ||
                NULL == (new_path->dst = H5T_copy(old_path->dst, H5T_COPY_ALL)))
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to copy data types");
            new_path->conv    = *conv;
            new_path->is_hard = false;
            new_path->cdata   = cdata;

            /* Replace previous path */
            H5T_g.path[i] = new_path;
            new_path      = NULL; /*so we don't free it on error*/

            /* Free old path */
            conv_ctx.u.free.src_type_id = tmp_sid;
            conv_ctx.u.free.dst_type_id = tmp_did;
            if (H5T__path_free(old_path, &conv_ctx) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTFREE, FAIL, "unable to free datatype conversion path");

            /* Release temporary atoms */
            if (tmp_sid >= 0) {
                if (H5I_dec_ref(tmp_sid) < 0)
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTDEC, FAIL, "can't decrement reference on temporary ID");
                tmp_sid   = H5I_INVALID_HID;
                tmp_stype = NULL;
            }
            if (tmp_did >= 0) {
                if (H5I_dec_ref(tmp_did) < 0)
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTDEC, FAIL, "can't decrement reference on temporary ID");
                tmp_did   = H5I_INVALID_HID;
                tmp_dtype = NULL;
            }

            /* We don't care about any failures during the freeing process */
            if (H5E_clear_stack() < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTRESET, FAIL, "unable to clear current error stack");
        } /* end for */
    }     /* end else */

done:
    if (ret_value < 0) {
        if (new_path) {
            if (new_path->src && (H5T_close_real(new_path->src) < 0))
                HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "unable to close datatype");
            if (new_path->dst && (H5T_close_real(new_path->dst) < 0))
                HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "unable to close datatype");
            new_path = H5FL_FREE(H5T_path_t, new_path);
        } /* end if */
    }     /* end if */

    if (tmp_sid >= 0) {
        if (H5I_dec_ref(tmp_sid) < 0)
            HDONE_ERROR(H5E_DATATYPE, H5E_CANTDEC, FAIL, "can't decrement reference on temporary ID");
    }
    else if (tmp_stype) {
        if (H5T_close(tmp_stype) < 0)
            HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "can't close temporary datatype");
    }
    if (tmp_did >= 0) {
        if (H5I_dec_ref(tmp_did) < 0)
            HDONE_ERROR(H5E_DATATYPE, H5E_CANTDEC, FAIL, "can't decrement reference on temporary ID");
    }
    else if (tmp_dtype) {
        if (H5T_close(tmp_dtype) < 0)
            HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "can't close temporary datatype");
    }

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__register() */

/*-------------------------------------------------------------------------
 * Function:    H5Tregister
 *
 * Purpose:    Register a hard or soft conversion function for a data type
 *        conversion path.  The path is specified by the source and
 *        destination data types SRC_ID and DST_ID (for soft functions
 *        only the class of these types is important). If FUNC is a
 *        hard function then it replaces any previous path; if it's a
 *        soft function then it replaces all existing paths to which it
 *        applies and is used for any new path to which it applies as
 *        long as that path doesn't have a hard function.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Tregister(H5T_pers_t pers, const char *name, hid_t src_id, hid_t dst_id, H5T_conv_t func)
{
    H5T_t          *src;                 /*source data type descriptor    */
    H5T_t          *dst;                 /*destination data type desc     */
    H5T_conv_func_t conv_func;           /* Conversion function wrapper */
    herr_t          ret_value = SUCCEED; /*return value                   */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (H5T_PERS_HARD != pers && H5T_PERS_SOFT != pers)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid function persistence");
    if (!name || !*name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "conversion must have a name for debugging");
    if (NULL == (src = (H5T_t *)H5I_object_verify(src_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a data type");
    if (NULL == (dst = (H5T_t *)H5I_object_verify(dst_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a data type");
    if (!func)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no conversion function specified");

    /* Set up conversion function wrapper */
    conv_func.is_app     = true;
    conv_func.u.app_func = func;

    /* Go register the function */
    if (H5T__register(pers, name, src, dst, &conv_func) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "can't register conversion function");

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tregister() */

/*-------------------------------------------------------------------------
 * Function:   H5T_unregister
 *
 * Purpose:    Removes conversion paths that match the specified criteria.
 *        All arguments are optional. Missing arguments are wild cards.
 *        The special no-op path cannot be removed.
 *
 * Return:    Success:    non-negative
 *            Failure:    negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T_unregister(H5T_pers_t pers, const char *name, H5T_t *src, H5T_t *dst, H5VL_object_t *owned_vol_obj,
               H5T_conv_t func)
{
    H5T_conv_ctx_t conv_ctx  = {0};  /* Conversion context object   */
    H5T_path_t    *path      = NULL; /* Conversion path             */
    H5T_soft_t    *soft      = NULL; /* Soft conversion information */
    herr_t         ret_value = SUCCEED;

    FUNC_ENTER_NOAPI(FAIL)

    conv_ctx.u.free.src_type_id = H5I_INVALID_HID;
    conv_ctx.u.free.dst_type_id = H5I_INVALID_HID;

    /*
     * Remove matching entries from the soft list if:
     *
     *  - The caller didn't specify a particular type (soft or hard)
     *    of conversion path to match against or specified that soft
     *    conversion paths should be matched against
     *
     *  AND
     *
     *  - The caller didn't provide the `owned_vol_obj` parameter;
     *    if this parameter is provided, we want to leave the soft
     *    list untouched and only remove cached conversion paths
     *    below where the file VOL object associated with the path's
     *    source or destination types matches the given VOL object.
     */
    if ((H5T_PERS_DONTCARE == pers || H5T_PERS_SOFT == pers) && !owned_vol_obj) {
        for (int i = H5T_g.nsoft - 1; i >= 0; --i) {
            soft = H5T_g.soft + i;
            assert(soft);
            if (name && *name && strcmp(name, soft->name) != 0)
                continue;
            if (src && src->shared->type != soft->src)
                continue;
            if (dst && dst->shared->type != soft->dst)
                continue;
            if (func && func != soft->conv.u.app_func)
                continue;

            memmove(H5T_g.soft + i, H5T_g.soft + i + 1, (size_t)(H5T_g.nsoft - (i + 1)) * sizeof(H5T_soft_t));
            --H5T_g.nsoft;
        }
    }

    /* Remove matching conversion paths, except no-op path */
    for (int i = H5T_g.npaths - 1; i > 0; --i) {
        bool nomatch;

        path = H5T_g.path[i];
        assert(path);

        nomatch = !H5T_path_match(path, pers, name, src, dst, owned_vol_obj, func);

        /* Not a match */
        if (nomatch) {
            /*
             * Notify all other functions to recalculate private data since some
             * functions might cache a list of conversion functions.  For
             * instance, the compound type converter caches a list of conversion
             * functions for the members, so removing a function should cause
             * the list to be recalculated to avoid the removed function.
             */
            path->cdata.recalc = true;
        }
        else {
            /* Remove from table */
            memmove(H5T_g.path + i, H5T_g.path + i + 1,
                    (size_t)(H5T_g.npaths - (i + 1)) * sizeof(H5T_path_t *));
            --H5T_g.npaths;

            if (H5T__path_free(path, &conv_ctx) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTFREE, FAIL, "unable to free datatype conversion path");
        }
    }

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_unregister() */

/*-------------------------------------------------------------------------
 * Function:  H5Tunregister
 *
 * Purpose:   Removes conversion paths that match the specified criteria.
 *        All arguments are optional. Missing arguments are wild cards.
 *        The special no-op path cannot be removed.
 *
 * Return:    Success:    non-negative
 *
 *            Failure:    negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Tunregister(H5T_pers_t pers, const char *name, hid_t src_id, hid_t dst_id, H5T_conv_t func)
{
    H5T_t *src = NULL, *dst = NULL; /* Datatype descriptors */
    herr_t ret_value = SUCCEED;     /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check arguments */
    if (src_id > 0 && (NULL == (src = (H5T_t *)H5I_object_verify(src_id, H5I_DATATYPE))))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "src is not a data type");
    if (dst_id > 0 && (NULL == (dst = (H5T_t *)H5I_object_verify(dst_id, H5I_DATATYPE))))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "dst is not a data type");

    if (H5T_unregister(pers, name, src, dst, NULL, func) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTDELETE, FAIL, "internal unregister function failed");

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tunregister() */

/*-------------------------------------------------------------------------
 * Function:  H5Tfind
 *
 * Purpose:   Finds a conversion function that can handle a conversion from
 *        type SRC_ID to type DST_ID.  The PCDATA argument is a pointer
 *        to a pointer to type conversion data which was created and
 *        initialized by the type conversion function of this path
 *        when the conversion function was installed on the path.
 *
 * Return:    Success:    A pointer to a suitable conversion function.
 *
 *            Failure:    NULL
 *
 *-------------------------------------------------------------------------
 */
H5T_conv_t
H5Tfind(hid_t src_id, hid_t dst_id, H5T_cdata_t **pcdata /*out*/)
{
    H5T_t      *src, *dst;
    H5T_path_t *path;
    H5T_conv_t  ret_value; /* Return value */

    FUNC_ENTER_API(NULL)

    /* Check args */
    if (NULL == (src = (H5T_t *)H5I_object_verify(src_id, H5I_DATATYPE)) ||
        NULL == (dst = (H5T_t *)H5I_object_verify(dst_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a data type");
    if (!pcdata)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "no address to receive cdata pointer");

    /* Find it */
    if (NULL == (path = H5T_path_find(src, dst)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_NOTFOUND, NULL, "conversion function not found");

    if (pcdata)
        *pcdata = &(path->cdata);

    /* Set return value */
    ret_value = path->conv.u.app_func;

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tfind() */

/*-------------------------------------------------------------------------
 * Function:  H5Tcompiler_conv
 *
 * Purpose:   Finds out whether the library's conversion function from
 *            type src_id to type dst_id is a compiler (hard) conversion.
 *            A hard conversion uses compiler's casting; a soft conversion
 *            uses the library's own conversion function.
 *
 * Return:    true:           hard conversion.
 *            false:          soft conversion.
 *            FAIL:           failed.
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5Tcompiler_conv(hid_t src_id, hid_t dst_id)
{
    H5T_t *src, *dst;
    htri_t ret_value; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (NULL == (src = (H5T_t *)H5I_object_verify(src_id, H5I_DATATYPE)) ||
        NULL == (dst = (H5T_t *)H5I_object_verify(dst_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a data type");

    /* Find it */
    if ((ret_value = H5T__compiler_conv(src, dst)) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_NOTFOUND, FAIL, "conversion function not found");

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tcompiler_conv() */

/*-------------------------------------------------------------------------
 * Function:  H5Tconvert
 *
 * Purpose:   Convert NELMTS elements from type SRC_ID to type DST_ID.  The
 *        source elements are packed in BUF and on return the
 *        destination will be packed in BUF.  That is, the conversion
 *        is performed in place.  The optional background buffer is an
 *        array of NELMTS values of destination type which are merged
 *        with the converted values to fill in cracks (for instance,
 *        BACKGROUND might be an array of structs with the `a' and `b'
 *        fields already initialized and the conversion of BUF supplies
 *        the `c' and `d' field values).  The PLIST_ID a dataset transfer
 *        property list which is passed to the conversion functions.  (It's
 *        currently only used to pass along the VL datatype custom allocation
 *        information -QAK 7/1/99)
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Tconvert(hid_t src_id, hid_t dst_id, size_t nelmts, void *buf, void *background, hid_t dxpl_id)
{
    H5T_path_t *tpath;               /* type conversion info    */
    H5T_t      *src, *dst;           /* unregistered types      */
    herr_t      ret_value = SUCCEED; /* Return value            */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (NULL == (src = (H5T_t *)H5I_object_verify(src_id, H5I_DATATYPE)) ||
        NULL == (dst = (H5T_t *)H5I_object_verify(dst_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a data type");
    if (H5P_DEFAULT == dxpl_id)
        dxpl_id = H5P_DATASET_XFER_DEFAULT;
    else if (true != H5P_isa_class(dxpl_id, H5P_DATASET_XFER))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not dataset transfer property list");

    /* Set DXPL for operation */
    H5CX_set_dxpl(dxpl_id);

    /* Find the conversion function */
    if (NULL == (tpath = H5T_path_find(src, dst)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to convert between src and dst data types");

    if (H5T_convert(tpath, src, dst, nelmts, 0, 0, buf, background) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "data type conversion failed");

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tconvert() */

/*-------------------------------------------------------------------------
 * Function:    H5Treclaim
 *
 * Purpose: Frees the buffers allocated for storing variable-length data
 *      in memory.  Only frees the VL data in the selection defined in the
 *      dataspace.  The dataset transfer property list is required to find the
 *      correct allocation/free methods for the VL data in the buffer.
 *
 * Return:  Non-negative on success, negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Treclaim(hid_t type_id, hid_t space_id, hid_t dxpl_id, void *buf)
{
    const H5T_t *type;
    H5S_t       *space;     /* Dataspace for iteration */
    herr_t       ret_value; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (buf == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "'buf' pointer is NULL");
    if (NULL == (type = (const H5T_t *)H5I_object_verify(type_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid datatype");
    if (NULL == (space = (H5S_t *)H5I_object_verify(space_id, H5I_DATASPACE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid dataspace");
    if (!(H5S_has_extent(space)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "dataspace does not have extent set");

    /* Get the default dataset transfer property list if the user didn't provide one */
    if (H5P_DEFAULT == dxpl_id)
        dxpl_id = H5P_DATASET_XFER_DEFAULT;
    else if (true != H5P_isa_class(dxpl_id, H5P_DATASET_XFER))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not xfer parms");

    /* Set DXPL for operation */
    H5CX_set_dxpl(dxpl_id);

    /* Call internal routine */
    ret_value = H5T_reclaim(type, space, buf);

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Treclaim() */

/*-------------------------------------------------------------------------
 * Function:  H5Tencode
 *
 * Purpose:   Given an datatype ID, converts the object description into
 *            binary in a buffer.
 *
 * Return:    Success:    non-negative
 *
 *            Failure:    negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Tencode(hid_t obj_id, void *buf, size_t *nalloc)
{
    H5T_t *dtype;
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_API(FAIL)

    /* Check argument and retrieve object */
    if (NULL == (dtype = (H5T_t *)H5I_object_verify(obj_id, H5I_DATATYPE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype");
    if (nalloc == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL pointer for buffer size");

    /* Go encode the datatype */
    if (H5T_encode(dtype, (unsigned char *)buf, nalloc) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTENCODE, FAIL, "can't encode datatype");

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tencode() */

/*-------------------------------------------------------------------------
 * Function:  H5Tdecode
 *
 * Purpose:   Decode a binary object description and return a new object
 *            handle.
 *
 * Return:    Success:    datatype ID(non-negative)
 *
 *            Failure:    negative
 *
 *-------------------------------------------------------------------------
 */
hid_t
H5Tdecode(const void *buf)
{
    H5T_t *dt;
    hid_t  ret_value; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (buf == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "empty buffer");

    /* Create datatype by decoding buffer
     * There is no way to get the size of the buffer, so we pass in
     * SIZE_MAX and assume the caller knows what they are doing.
     * Really fixing this will require an H5Tdecode2() call that
     * takes a size parameter.
     */
    if (NULL == (dt = H5T_decode(SIZE_MAX, (const unsigned char *)buf)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTDECODE, FAIL, "can't decode object");

    /* Register the type and return the ID */
    if ((ret_value = H5I_register(H5I_DATATYPE, dt, true)) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTREGISTER, FAIL, "unable to register data type");

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Tdecode() */

/*-------------------------------------------------------------------------
 * API functions are above; library-private functions are below...
 *-------------------------------------------------------------------------
 */

/*-------------------------------------------------------------------------
 * Function:  H5T_encode
 *
 * Purpose:   Private function for H5Tencode.  Converts an object
 *            description into binary in a buffer.
 *
 * Return:    Success:    non-negative
 *
 *            Failure:    negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T_encode(H5T_t *obj, unsigned char *buf, size_t *nalloc)
{
    size_t buf_size;         /* Encoded size of datatype */
    H5F_t *f         = NULL; /* Fake file structure*/
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_NOAPI_NOINIT

    /* Allocate "fake" file structure */
    if (NULL == (f = H5F_fake_alloc((uint8_t)0)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "can't allocate fake file struct");

    /* Find out the size of buffer needed */
    if ((buf_size = H5O_msg_raw_size(f, H5O_DTYPE_ID, true, obj)) == 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_BADSIZE, FAIL, "can't find datatype size");

    /* Don't encode if buffer size isn't big enough or buffer is empty */
    if (!buf || *nalloc < (buf_size + 1 + 1))
        *nalloc = buf_size + 1 + 1;
    else {
        /* Encode the type of the information */
        *buf++ = H5O_DTYPE_ID;

        /* Encode the version of the dataspace information */
        *buf++ = H5T_ENCODE_VERSION;

        /* Encode into user's buffer */
        if (H5O_msg_encode(f, H5O_DTYPE_ID, true, buf, obj) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTENCODE, FAIL, "can't encode object");
    } /* end else */

done:
    /* Release fake file structure */
    if (f && H5F_fake_free(f) < 0)
        HDONE_ERROR(H5E_DATATYPE, H5E_CANTRELEASE, FAIL, "unable to release fake file struct");

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_encode() */

/*-------------------------------------------------------------------------
 * Function:  H5T_decode
 *
 * Purpose:   Private function for H5Tdecode.  Reconstructs a binary
 *            description of datatype and returns a new object handle.
 *
 * Return:    Success:    datatype ID(non-negative)
 *
 *            Failure:    negative
 *
 *-------------------------------------------------------------------------
 */
H5T_t *
H5T_decode(size_t buf_size, const unsigned char *buf)
{
    H5F_t *f         = NULL; /* Fake file structure*/
    H5T_t *ret_value = NULL; /* Return value */

    FUNC_ENTER_NOAPI_NOINIT

    /* Allocate "fake" file structure */
    if (NULL == (f = H5F_fake_alloc((uint8_t)0)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, NULL, "can't allocate fake file struct");

    /* Decode the type of the information */
    if (*buf++ != H5O_DTYPE_ID)
        HGOTO_ERROR(H5E_DATATYPE, H5E_BADMESG, NULL, "not an encoded datatype");

    /* Decode the version of the datatype information */
    if (*buf++ != H5T_ENCODE_VERSION)
        HGOTO_ERROR(H5E_DATATYPE, H5E_VERSION, NULL, "unknown version of encoded datatype");

    /* Decode the serialized datatype message */
    if (NULL == (ret_value = (H5T_t *)H5O_msg_decode(f, NULL, H5O_DTYPE_ID, buf_size, buf)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTDECODE, NULL, "can't decode object");

    /* Mark datatype as being in memory now */
    if (H5T_set_loc(ret_value, NULL, H5T_LOC_MEMORY) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "invalid datatype location");

    /* No VOL object */
    ret_value->vol_obj = NULL;
done:
    /* Release fake file structure */
    if (f && H5F_fake_free(f) < 0)
        HDONE_ERROR(H5E_DATATYPE, H5E_CANTRELEASE, NULL, "unable to release fake file struct");

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_decode() */

/*-------------------------------------------------------------------------
 * Function:  H5T__create
 *
 * Purpose:   Creates a new data type and initializes it to reasonable
 *            values.     The new data type is SIZE bytes and an instance of
 *            the class TYPE.
 *
 * Return:    Success:    Pointer to the new type.
 *
 *            Failure:    NULL
 *
 *-------------------------------------------------------------------------
 */
H5T_t *
H5T__create(H5T_class_t type, size_t size)
{
    H5T_t *dt        = NULL;
    H5T_t *ret_value = NULL;

    FUNC_ENTER_PACKAGE

    switch (type) {
        case H5T_INTEGER:
        case H5T_FLOAT:
        case H5T_TIME:
        case H5T_STRING: {
            H5T_t *origin_dt = NULL;

            if (NULL == (origin_dt = (H5T_t *)H5I_object(H5T_C_S1)))
                HGOTO_ERROR(H5E_DATATYPE, H5E_BADTYPE, NULL, "can't get structure for string type");

            /* Copy the default string datatype */
            if (NULL == (dt = H5T_copy(origin_dt, H5T_COPY_TRANSIENT)))
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "unable to copy");
            dt->shared->type = type;

            /* Modify the datatype */
            if (H5T__set_size(dt, size) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "unable to set size for string type");
        } break;

        case H5T_BITFIELD:
            HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, NULL, "type class is not appropriate - use H5Tcopy()");

        case H5T_OPAQUE:
        case H5T_COMPOUND:
            if (NULL == (dt = H5T__alloc()))
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
            dt->shared->type = type;

            if (type == H5T_COMPOUND) {
                dt->shared->u.compnd.packed    = false; /* Start out unpacked */
                dt->shared->u.compnd.memb_size = 0;
            } /* end if */
            else if (type == H5T_OPAQUE)
                /* Initialize the tag in case it's not set later.  A null tag will
                 * cause problems for later operations. */
                dt->shared->u.opaque.tag = H5MM_strdup("");
            break;

        case H5T_ENUM: {
            hid_t  subtype;
            H5T_t *sub_t_obj;

            if (sizeof(char) == size)
                subtype = H5T_NATIVE_SCHAR_g;
            else if (sizeof(short) == size)
                subtype = H5T_NATIVE_SHORT_g;
            else if (sizeof(int) == size)
                subtype = H5T_NATIVE_INT_g;
            else if (sizeof(long) == size)
                subtype = H5T_NATIVE_LONG_g;
#if H5_SIZEOF_LONG != H5_SIZEOF_LONG_LONG
            else if (sizeof(long long) == size)
                subtype = H5T_NATIVE_LLONG_g;
#endif /* H5_SIZEOF_LONG != H5_SIZEOF_LONG_LONG */
            else
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "no applicable native integer type");
            if (NULL == (dt = H5T__alloc()))
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
            dt->shared->type = type;
            if (NULL == (sub_t_obj = (H5T_t *)H5I_object(subtype)))
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, NULL, "unable to get datatype object");
            if (NULL == (dt->shared->parent = H5T_copy(sub_t_obj, H5T_COPY_ALL)))
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, NULL, "unable to copy base datatype");
        } break;

        case H5T_VLEN: /* Variable length datatype */
            HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, NULL, "base type required - use H5Tvlen_create()");

        case H5T_ARRAY: /* Array datatype */
            HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, NULL, "base type required - use H5Tarray_create2()");

        case H5T_NO_CLASS:
        case H5T_REFERENCE:
        case H5T_NCLASSES:
        default:
            HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, NULL, "unknown data type class");
    } /* end switch */

    /* Set the size except VL string */
    if (H5T_STRING != type || H5T_VARIABLE != size)
        dt->shared->size = size;

    /* No VOL object */
    dt->vol_obj = NULL;

    /* Set return value */
    ret_value = dt;

done:
    if (NULL == ret_value) {
        if (dt) {
            if (dt->shared->owned_vol_obj && H5VL_free_object(dt->shared->owned_vol_obj) < 0)
                HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, NULL, "unable to close owned VOL object");
            dt->shared = H5FL_FREE(H5T_shared_t, dt->shared);
            dt         = H5FL_FREE(H5T_t, dt);
        }
    }

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__create() */

/*-------------------------------------------------------------------------
 * Function:  H5T__initiate_copy
 *
 * Purpose:   Allocates datatype structures, copies core fields, and initializes
 *            VOL fields.
 *
 * Return:    Success:    Pointer to a new copy of the OLD_DT argument.
 *            Failure:    NULL
 *
 * Note:      Common code for both H5T_copy and H5T_copy_reopen, as part of
 *            the const-correct datatype copying routines.
 *
 *-------------------------------------------------------------------------
 */
static H5T_t *
H5T__initiate_copy(const H5T_t *old_dt)
{
    H5T_t *new_dt    = NULL; /* Copy of datatype */
    H5T_t *ret_value = NULL; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Allocate space */
    if (NULL == (new_dt = H5FL_MALLOC(H5T_t)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, NULL, "H5T_t memory allocation failed");
    if (NULL == (new_dt->shared = H5FL_MALLOC(H5T_shared_t)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, NULL, "H5T_shared_t memory allocation failed");

    /* Copy shared information */
    *(new_dt->shared) = *(old_dt->shared);

    /* Increment ref count on owned VOL object */
    if (new_dt->shared->owned_vol_obj)
        (void)H5VL_object_inc_rc(new_dt->shared->owned_vol_obj);

    /* Reset vol_obj field */
    new_dt->vol_obj = NULL;

    /* Set return value */
    ret_value = new_dt;

done:
    if (ret_value == NULL)
        if (new_dt) {
            if (new_dt->shared) {
                if (new_dt->shared->owned_vol_obj && H5VL_free_object(new_dt->shared->owned_vol_obj) < 0)
                    HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, NULL, "unable to close owned VOL object");
                new_dt->shared = H5FL_FREE(H5T_shared_t, new_dt->shared);
            } /* end if */
            new_dt = H5FL_FREE(H5T_t, new_dt);
        } /* end if */

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__initiate_copy() */

/*-------------------------------------------------------------------------
 * Function:  H5T__copy_transient
 *
 * Purpose:   Part of recursive framework for const-correct datatype copying.
 *
 * Return:    Success:    Pointer to a new copy of the OLD_DT argument.
 *            Failure:    NULL
 *
 *-------------------------------------------------------------------------
 */
static H5T_t *
H5T__copy_transient(H5T_t *old_dt)
{
    H5T_t *ret_value = NULL; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Copy datatype, with correct method */
    if (NULL == (ret_value = H5T_copy(old_dt, H5T_COPY_TRANSIENT)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, NULL, "can't make 'transient' copy of datatype");

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__copy_transient() */

/*-------------------------------------------------------------------------
 * Function:  H5T__copy_all
 *
 * Purpose:   Part of recursive framework for const-correct datatype copying.
 *
 * Return:    Success:    Pointer to a new copy of the OLD_DT argument.
 *            Failure:    NULL
 *
 *-------------------------------------------------------------------------
 */
static H5T_t *
H5T__copy_all(H5T_t *old_dt)
{
    H5T_t *ret_value = NULL; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Copy datatype, with correct method */
    if (NULL == (ret_value = H5T_copy(old_dt, H5T_COPY_ALL)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, NULL, "can't make 'all' copy of datatype");

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__copy_transient() */

/*-------------------------------------------------------------------------
 * Function:  H5T__complete_copy
 *
 * Purpose:   Completes copying datatype fields, as part of the recursive
 *            const-correct datatype copy routines.
 *
 * Return:    Success:    non-negative
 *            Failure:    negative
 *
 * Note:      Common code for both H5T_copy and H5T_copy_reopen.
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5T__complete_copy(H5T_t *new_dt, const H5T_t *old_dt, H5T_shared_t *reopened_fo, bool set_memory_type,
                   H5T_copy_func_t copyfn)
{
    H5T_t   *tmp = NULL;          /* Temporary copy of compound field's datatype */
    char    *s;                   /* Temporary copy of compound field name / enum value name */
    unsigned i;                   /* Local index variable */
    herr_t   ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Update fields in the new struct, if we aren't sharing an already opened
     * committed datatype */
    if (!reopened_fo) {
        /* Copy parent information */
        if (old_dt->shared->parent)
            if (NULL == (new_dt->shared->parent = (*copyfn)(old_dt->shared->parent)))
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "can't copy datatype's parent type");

        switch (new_dt->shared->type) {
            case H5T_COMPOUND: {
                ssize_t accum_change = 0; /* Amount of change in the offset of the fields */

                /*
                 * Copy all member fields to new type, then overwrite the
                 * name and type fields of each new member with copied values.
                 * That is, H5T_copy() is a deep copy.
                 */
                /* Only malloc if space has been allocated for members - NAF */
                if (new_dt->shared->u.compnd.nalloc > 0) {
                    if (NULL == (new_dt->shared->u.compnd.memb =
                                     H5MM_malloc(new_dt->shared->u.compnd.nalloc * sizeof(H5T_cmemb_t))))
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "memory allocation failed");

                    H5MM_memcpy(new_dt->shared->u.compnd.memb, old_dt->shared->u.compnd.memb,
                                new_dt->shared->u.compnd.nmembs * sizeof(H5T_cmemb_t));
                } /* end if */

                for (i = 0; i < new_dt->shared->u.compnd.nmembs; i++) {
                    unsigned j;
                    int      old_match;

                    if (NULL == (s = H5MM_xstrdup(new_dt->shared->u.compnd.memb[i].name)))
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL,
                                    "can't copy string for compound field's name");
                    new_dt->shared->u.compnd.memb[i].name = s;
                    if (NULL == (tmp = (*copyfn)(old_dt->shared->u.compnd.memb[i].type)))
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "can't copy compound field's datatype");
                    new_dt->shared->u.compnd.memb[i].type = tmp;
                    assert(tmp != NULL);

                    /* Range check against compound member's offset */
                    if ((accum_change < 0) &&
                        ((ssize_t)new_dt->shared->u.compnd.memb[i].offset < accum_change))
                        HGOTO_ERROR(H5E_DATATYPE, H5E_BADVALUE, FAIL, "invalid field size in datatype");

                    /* Apply the accumulated size change to the offset of the field */
                    new_dt->shared->u.compnd.memb[i].offset += (size_t)accum_change;

                    if (old_dt->shared->u.compnd.sorted != H5T_SORT_VALUE) {
                        for (old_match = -1, j = 0; j < old_dt->shared->u.compnd.nmembs; j++) {
                            if (!strcmp(new_dt->shared->u.compnd.memb[i].name,
                                        old_dt->shared->u.compnd.memb[j].name)) {
                                old_match = (int)j;
                                break;
                            } /* end if */
                        }     /* end for */

                        /* check if we couldn't find a match */
                        if (old_match < 0)
                            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "fields in datatype corrupted");
                    } /* end if */
                    else
                        old_match = (int)i;

                    /* If the field changed size, add that change to the accumulated size change */
                    if (new_dt->shared->u.compnd.memb[i].type->shared->size !=
                        old_dt->shared->u.compnd.memb[old_match].type->shared->size) {
                        /* Adjust the size of the member */
                        new_dt->shared->u.compnd.memb[i].size =
                            (old_dt->shared->u.compnd.memb[old_match].size * tmp->shared->size) /
                            old_dt->shared->u.compnd.memb[old_match].type->shared->size;

                        accum_change +=
                            (ssize_t)(new_dt->shared->u.compnd.memb[i].type->shared->size -
                                      old_dt->shared->u.compnd.memb[old_match].type->shared->size);
                    } /* end if */
                }     /* end for */

                /* Range check against datatype size */
                if ((accum_change < 0) && ((ssize_t)new_dt->shared->size < accum_change))
                    HGOTO_ERROR(H5E_DATATYPE, H5E_BADVALUE, FAIL, "invalid field size in datatype");

                /* Apply the accumulated size change to the size of the compound struct */
                new_dt->shared->size += (size_t)accum_change;
            } break;

            case H5T_ENUM:
                /*
                 * Copy all member fields to new type, then overwrite the name fields
                 * of each new member with copied values. That is, H5T_copy() is a
                 * deep copy.
                 */
                if (old_dt->shared->u.enumer.nalloc > 0) {
                    if (NULL == (new_dt->shared->u.enumer.name =
                                     H5MM_malloc(new_dt->shared->u.enumer.nalloc * sizeof(char *))))
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL,
                                    "enam name array memory allocation failed");
                    if (NULL == (new_dt->shared->u.enumer.value =
                                     H5MM_malloc(new_dt->shared->u.enumer.nalloc * new_dt->shared->size)))
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL,
                                    "enam value array memory allocation failed");
                    H5MM_memcpy(new_dt->shared->u.enumer.value, old_dt->shared->u.enumer.value,
                                new_dt->shared->u.enumer.nmembs * new_dt->shared->size);
                    for (i = 0; i < new_dt->shared->u.enumer.nmembs; i++) {
                        if (NULL == (s = H5MM_xstrdup(old_dt->shared->u.enumer.name[i])))
                            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL,
                                        "can't copy string for enum value's name");
                        new_dt->shared->u.enumer.name[i] = s;
                    }
                }
                else {
                    /* Empty enum */
                    memset(&new_dt->shared->u.enumer, 0, sizeof(H5T_enum_t));
                }
                break;

            case H5T_VLEN:
            case H5T_REFERENCE:
                if (set_memory_type)
                    /* H5T_copy converts any type into a memory type */
                    if (H5T_set_loc(new_dt, NULL, H5T_LOC_MEMORY) < 0)
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "invalid datatype location");
                break;

            case H5T_OPAQUE:
                /*
                 * Copy the tag name.
                 */
                new_dt->shared->u.opaque.tag = H5MM_xstrdup(new_dt->shared->u.opaque.tag);
                break;

            case H5T_ARRAY:
                /* Re-compute the array's size, in case it's base type changed size */
                new_dt->shared->size = new_dt->shared->u.array.nelem * new_dt->shared->parent->shared->size;
                break;

            case H5T_NO_CLASS:
            case H5T_INTEGER:
            case H5T_FLOAT:
            case H5T_TIME:
            case H5T_STRING:
            case H5T_BITFIELD:
            case H5T_NCLASSES:
            default:
                break;
        } /* end switch */
    }     /* end if */

    /* Set the cached location & name path if the original type was a named
     * type and the new type is also named.
     */
    if (H5O_loc_reset(&new_dt->oloc) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTRESET, FAIL, "unable to initialize location");
    if (H5G_name_reset(&new_dt->path) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTOPENOBJ, FAIL, "unable to reset path");

    if (new_dt->shared->state == H5T_STATE_NAMED || new_dt->shared->state == H5T_STATE_OPEN) {
        if (H5O_loc_copy_deep(&(new_dt->oloc), &(old_dt->oloc)) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "can't copy object location");
        if (H5G_name_copy(&(new_dt->path), &(old_dt->path), H5_COPY_DEEP) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTOPENOBJ, FAIL, "unable to copy path");
    } /* end if */

    /* Copy shared location information if the new type is named or if it is
     * shared in the heap.
     */
    if ((old_dt->sh_loc.type == H5O_SHARE_TYPE_SOHM || old_dt->sh_loc.type == H5O_SHARE_TYPE_HERE) ||
        new_dt->shared->state == H5T_STATE_NAMED || new_dt->shared->state == H5T_STATE_OPEN) {
        if (H5O_set_shared(&(new_dt->sh_loc), &(old_dt->sh_loc)) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "can't copy shared information");
    } /* end if */
    else
        /* Reset shared component info */
        H5O_msg_reset_share(H5O_DTYPE_ID, new_dt);

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__complete_copy() */

/*-------------------------------------------------------------------------
 * Function:  H5T_copy
 *
 * Purpose:   Copies datatype OLD_DT.     The resulting data type is not
 *            locked and is a transient type.
 *
 * Return:    Success:    Pointer to a new copy of the OLD_DT argument.
 *            Failure:    NULL
 *
 *-------------------------------------------------------------------------
 */
H5T_t *
H5T_copy(const H5T_t *old_dt, H5T_copy_t method)
{
    H5T_t          *new_dt = NULL;    /* New datatype */
    H5T_copy_func_t copyfn;           /* Pointer to correct copy routine */
    H5T_t          *ret_value = NULL; /* Return value */

    FUNC_ENTER_NOAPI(NULL)

    /* check args */
    assert(old_dt);

    /* Allocate and copy core datatype information */
    if (NULL == (new_dt = H5T__initiate_copy(old_dt)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, NULL, "can't copy core datatype info");

    /* Check what sort of copy we are making */
    switch (method) {
        case H5T_COPY_TRANSIENT:
            /*
             * Return an unlocked transient type.
             */
            new_dt->shared->state = H5T_STATE_TRANSIENT;
            copyfn                = H5T__copy_transient;
            break;

        case H5T_COPY_ALL:
            /*
             * Return a transient type (locked or unlocked) or an unopened named
             * type.  Immutable transient types are degraded to read-only.
             */
            if (H5T_STATE_OPEN == old_dt->shared->state)
                new_dt->shared->state = H5T_STATE_NAMED;
            else if (H5T_STATE_IMMUTABLE == old_dt->shared->state)
                new_dt->shared->state = H5T_STATE_RDONLY;
            copyfn = H5T__copy_all;
            break;

        default:
            HGOTO_ERROR(H5E_DATATYPE, H5E_BADTYPE, NULL, "invalid copy method type");
    } /* end switch */

    /* Finish making the copy of the datatype */
    if (H5T__complete_copy(new_dt, old_dt, NULL, (method == H5T_COPY_TRANSIENT), copyfn) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "can't complete datatype initialization");

    /* Set return value */
    ret_value = new_dt;

done:
    if (ret_value == NULL)
        if (new_dt) {
            assert(new_dt->shared);
            if (new_dt->shared->owned_vol_obj && H5VL_free_object(new_dt->shared->owned_vol_obj) < 0)
                HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, NULL, "unable to close owned VOL object");
            new_dt->shared = H5FL_FREE(H5T_shared_t, new_dt->shared);
            new_dt         = H5FL_FREE(H5T_t, new_dt);
        } /* end if */

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_copy() */

/*-------------------------------------------------------------------------
 * Function:  H5T_copy_reopen
 *
 * Purpose:   Copy a datatype, possibly reopening a named datatype, as part
 *            the const-correct datatype copying routines.
 *
 * Return:    Success:    Pointer to a new copy of the OLD_DT argument.
 *            Failure:    NULL
 *
 *-------------------------------------------------------------------------
 */
H5T_t *
H5T_copy_reopen(H5T_t *old_dt)
{
    H5T_t        *new_dt      = NULL; /* New datatype */
    H5T_shared_t *reopened_fo = NULL; /* Pointer to reopened existing named datatype */
    H5T_t        *ret_value   = NULL; /* Return value */

    FUNC_ENTER_NOAPI(NULL)

    /* check args */
    assert(old_dt);

    /* Allocate and copy core datatype information */
    if (NULL == (new_dt = H5T__initiate_copy(old_dt)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, NULL, "can't copy core datatype info");

    /*
     * Return a transient type (locked or unlocked) or an opened named
     * type.  Immutable transient types are degraded to read-only.
     */
    if (old_dt->sh_loc.type == H5O_SHARE_TYPE_COMMITTED) {
        /* Check if the object is already open */
        if (NULL ==
            (reopened_fo = (H5T_shared_t *)H5FO_opened(old_dt->sh_loc.file, old_dt->sh_loc.u.loc.oh_addr))) {
            /* Clear any errors from H5FO_opened() */
            H5E_clear_stack();

            /* Open named datatype again */
            if (H5O_open(&old_dt->oloc) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTOPENOBJ, NULL, "unable to reopen named data type");

            /* Insert opened named datatype into opened object list for the file */
            if (H5FO_insert(old_dt->sh_loc.file, old_dt->sh_loc.u.loc.oh_addr, new_dt->shared, false) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINSERT, NULL,
                            "can't insert datatype into list of open objects");

            /* Increment object count for the object in the top file */
            if (H5FO_top_incr(old_dt->sh_loc.file, old_dt->sh_loc.u.loc.oh_addr) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINC, NULL, "can't increment object count");

            new_dt->shared->fo_count = 1;
        } /* end if */
        else {
            /* The object is already open.  Free the H5T_shared_t struct
             * we had been using and use the one that already exists.
             * Not terribly efficient. */
            if (new_dt->shared->owned_vol_obj && H5VL_free_object(new_dt->shared->owned_vol_obj) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, NULL, "unable to close owned VOL object");
            new_dt->shared = H5FL_FREE(H5T_shared_t, new_dt->shared);
            new_dt->shared = reopened_fo;

            reopened_fo->fo_count++;

            /* Check if the object has been opened through the top file yet */
            if (H5FO_top_count(old_dt->sh_loc.file, old_dt->sh_loc.u.loc.oh_addr) == 0) {
                /* Open the object through this top file */
                if (H5O_open(&old_dt->oloc) < 0)
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTOPENOBJ, NULL, "unable to open object header");
            } /* end if */

            /* Increment object count for the object in the top file */
            if (H5FO_top_incr(old_dt->sh_loc.file, old_dt->sh_loc.u.loc.oh_addr) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINC, NULL, "can't increment object count");
        } /* end else */

        /* Set state for new datatype */
        new_dt->shared->state = H5T_STATE_OPEN;
    } /* end if */
    else
        /* Downgrade immutable datatypes to read-only */
        if (H5T_STATE_IMMUTABLE == old_dt->shared->state)
            new_dt->shared->state = H5T_STATE_RDONLY;

    /* Finish making the copy of the datatype */
    if (H5T__complete_copy(new_dt, old_dt, reopened_fo, true, H5T_copy_reopen) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "can't complete datatype initialization");

    /* Set return value */
    ret_value = new_dt;

done:
    if (ret_value == NULL)
        if (new_dt) {
            assert(new_dt->shared);
            if (new_dt->shared->owned_vol_obj && H5VL_free_object(new_dt->shared->owned_vol_obj) < 0)
                HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, NULL, "unable to close owned VOL object");
            new_dt->shared = H5FL_FREE(H5T_shared_t, new_dt->shared);
            new_dt         = H5FL_FREE(H5T_t, new_dt);
        } /* end if */

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_copy_reopen() */

/*-------------------------------------------------------------------------
 * Function:  H5T_lock
 *
 * Purpose:   Lock a transient data type making it read-only.  If IMMUTABLE
 *        is set then the type cannot be closed except when the library
 *        itself closes.
 *
 *        This function is a no-op if the type is not transient or if
 *        the type is already read-only or immutable.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T_lock(H5T_t *dt, bool immutable)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    assert(dt);

    switch (dt->shared->state) {
        case H5T_STATE_TRANSIENT:
            dt->shared->state = immutable ? H5T_STATE_IMMUTABLE : H5T_STATE_RDONLY;
            break;
        case H5T_STATE_RDONLY:
            if (immutable)
                dt->shared->state = H5T_STATE_IMMUTABLE;
            break;
        case H5T_STATE_IMMUTABLE:
        case H5T_STATE_NAMED:
        case H5T_STATE_OPEN:
            /*void*/
            break;
        default:
            HGOTO_ERROR(H5E_DATATYPE, H5E_BADTYPE, FAIL, "invalid datatype state");
    }

done:
    FUNC_LEAVE_NOAPI(ret_value)
}

/*-------------------------------------------------------------------------
 * Function:  H5T__alloc
 *
 * Purpose:   Allocates a new H5T_t structure, initializing it correctly.
 *
 * Return:    Pointer to new H5T_t on success/NULL on failure
 *
 *-------------------------------------------------------------------------
 */
H5T_t *
H5T__alloc(void)
{
    H5T_t *dt        = NULL; /* Pointer to datatype allocated */
    H5T_t *ret_value = NULL; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Allocate & initialize datatype wrapper info */
    if (NULL == (dt = H5FL_CALLOC(H5T_t)))
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
    H5O_loc_reset(&(dt->oloc));
    H5G_name_reset(&(dt->path));
    H5O_msg_reset_share(H5O_DTYPE_ID, dt);

    /* Allocate & initialize shared datatype structure */
    if (NULL == (dt->shared = H5FL_CALLOC(H5T_shared_t)))
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
    dt->shared->version = H5O_DTYPE_VERSION_1;

    /* No VOL object initially */
    dt->vol_obj = NULL;

    /* Assign return value */
    ret_value = dt;

done:
    if (ret_value == NULL)
        if (dt) {
            if (dt->shared) {
                assert(!dt->shared->owned_vol_obj);
                dt->shared = H5FL_FREE(H5T_shared_t, dt->shared);
            } /* end if */
            dt = H5FL_FREE(H5T_t, dt);
        } /* end if */

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__alloc() */

/*-------------------------------------------------------------------------
 * Function:  H5T__free
 *
 * Purpose:   Frees all memory associated with a datatype, but does not
 *            free the H5T_t or H5T_shared_t structures (which should
 *            be done in H5T_close / H5T_close_real).
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T__free(H5T_t *dt)
{
    unsigned i;
    herr_t   ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    assert(dt && dt->shared);

    /* Free the ID to name info */
    H5G_name_free(&(dt->path));

    /* Don't free locked datatypes */
    if (H5T_STATE_IMMUTABLE == dt->shared->state)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "unable to close immutable datatype");

    /* Close the datatype */
    switch (dt->shared->type) {
        case H5T_COMPOUND:
            for (i = 0; i < dt->shared->u.compnd.nmembs; i++) {
                dt->shared->u.compnd.memb[i].name = (char *)H5MM_xfree(dt->shared->u.compnd.memb[i].name);
                if (H5T_close_real(dt->shared->u.compnd.memb[i].type) < 0)
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL,
                                "unable to close datatype for compound member");
            }
            dt->shared->u.compnd.memb   = (H5T_cmemb_t *)H5MM_xfree(dt->shared->u.compnd.memb);
            dt->shared->u.compnd.nmembs = 0;
            break;

        case H5T_ENUM:
            for (i = 0; i < dt->shared->u.enumer.nmembs; i++)
                dt->shared->u.enumer.name[i] = (char *)H5MM_xfree(dt->shared->u.enumer.name[i]);
            dt->shared->u.enumer.name   = (char **)H5MM_xfree(dt->shared->u.enumer.name);
            dt->shared->u.enumer.value  = (uint8_t *)H5MM_xfree(dt->shared->u.enumer.value);
            dt->shared->u.enumer.nmembs = 0;
            break;

        case H5T_OPAQUE:
            dt->shared->u.opaque.tag = (char *)H5MM_xfree(dt->shared->u.opaque.tag);
            break;

        case H5T_NO_CLASS:
        case H5T_INTEGER:
        case H5T_FLOAT:
        case H5T_TIME:
        case H5T_STRING:
        case H5T_BITFIELD:
        case H5T_REFERENCE:
        case H5T_VLEN:
        case H5T_ARRAY:
        case H5T_NCLASSES:
        default:
            break;
    } /* end switch */
    dt->shared->type = H5T_NO_CLASS;

    /* Close the parent */
    assert(dt->shared->parent != dt);
    if (dt->shared->parent && H5T_close_real(dt->shared->parent) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "unable to close parent data type");
    dt->shared->parent = NULL;

    /* Close the owned VOL object */
    if (dt->shared->owned_vol_obj && H5VL_free_object(dt->shared->owned_vol_obj) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "unable to close owned VOL object");
    dt->shared->owned_vol_obj = NULL;

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__free() */

/*-------------------------------------------------------------------------
 * Function:  H5T_close_real
 *
 * Purpose:   Frees a datatype and all associated memory.
 *
 * Note:      Does _not_ deal with open named datatypes, etc. so this
 *            should never see a type managed by a VOL connector.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T_close_real(H5T_t *dt)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    /* Sanity check */
    assert(dt);

    /* Clean up resources, depending on shared state */
    if (dt->shared && (dt->shared->state != H5T_STATE_OPEN)) {
        if (H5T__free(dt) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTFREE, FAIL, "unable to free datatype");

        assert(!dt->shared->owned_vol_obj);
        dt->shared = H5FL_FREE(H5T_shared_t, dt->shared);
    } /* end if */
    else
        /* Free the group hier. path since we're not calling H5T__free() */
        H5G_name_free(&(dt->path));

    /* Free the 'top' datatype struct */
    dt = H5FL_FREE(H5T_t, dt);

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_close_real() */

/*-------------------------------------------------------------------------
 * Function:    H5T_close
 *
 * Purpose:     Frees a data type and all associated memory.  Deals with
 *              open named datatypes appropriately.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T_close(H5T_t *dt)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    /* Sanity check */
    assert(dt);

    /* Named datatype cleanups */
    if (dt->shared && (dt->shared->state == H5T_STATE_OPEN)) {
        /* Decrement refcount count on open named datatype */
        dt->shared->fo_count--;

        /* Sanity checks */
        assert(dt->sh_loc.type == H5O_SHARE_TYPE_COMMITTED);
        assert(H5_addr_defined(dt->sh_loc.u.loc.oh_addr));
        assert(H5_addr_defined(dt->oloc.addr));

        /* If a named type is being closed then close the object header and
         * remove from the list of open objects in the file.
         */

        /* Decrement the ref. count for this object in the top file */
        if (H5FO_top_decr(dt->sh_loc.file, dt->sh_loc.u.loc.oh_addr) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTRELEASE, FAIL, "can't decrement count for object");

        /* Close things down if this is the last reference to the open named datatype */
        if (0 == dt->shared->fo_count) {
            bool corked; /* Whether the named datatype is corked or not */

            /* Uncork cache entries with object address tag for named datatype */
            if (H5AC_cork(dt->oloc.file, dt->oloc.addr, H5AC__GET_CORKED, &corked) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, "unable to retrieve an object's cork status");
            if (corked)
                if (H5AC_cork(dt->oloc.file, dt->oloc.addr, H5AC__UNCORK, NULL) < 0)
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTUNCORK, FAIL, "unable to uncork an object");

            /* Remove the datatype from the list of opened objects in the file */
            if (H5FO_delete(dt->sh_loc.file, dt->sh_loc.u.loc.oh_addr) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTRELEASE, FAIL,
                            "can't remove datatype from list of open objects");
            if (H5O_close(&dt->oloc, NULL) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to close data type object header");

            /* Mark named datatype closed now */
            dt->shared->state = H5T_STATE_NAMED;
        } /* end if */
        else {
            /* Check reference count for this object in the top file */
            if (H5FO_top_count(dt->sh_loc.file, dt->sh_loc.u.loc.oh_addr) == 0) {
                /* Close object location for named datatype */
                if (H5O_close(&dt->oloc, NULL) < 0)
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to close");
            } /* end if */
            else
                /* Free object location (i.e. "unhold" the file if appropriate) */
                if (H5O_loc_free(&(dt->oloc)) < 0)
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTRELEASE, FAIL, "problem attempting to free location");
        } /* end else */
    }     /* end if */

    /* Clean up resources */
    if (H5T_close_real(dt) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTRELEASE, FAIL, "unable to free datatype");

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_close() */

/*-------------------------------------------------------------------------
 * Function:    H5T__set_size
 *
 * Purpose:    Sets the total size in bytes for a data type (this operation
 *        is not permitted on reference data types).  If the size is
 *        decreased so that the significant bits of the data type
 *        extend beyond the edge of the new size, then the `offset'
 *        property is decreased toward zero.  If the `offset' becomes
 *        zero and the significant bits of the data type still hang
 *        over the edge of the new size, then the number of significant
 *        bits is decreased.
 *
 *        Adjusting the size of an H5T_STRING automatically sets the
 *        precision to 8*size.
 *
 *        All data types have a positive size.
 *
 * Return:    Success:    non-negative
 *
 *            Failure:    negative
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5T__set_size(H5T_t *dt, size_t size)
{
    size_t prec, offset;
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Check args */
    assert(dt);
    assert(dt->shared);
    assert(size != 0);
    assert(H5T_REFERENCE != dt->shared->type);
    assert(!(H5T_ENUM == dt->shared->type && 0 == dt->shared->u.enumer.nmembs));

    if (dt->shared->parent) {
        if (H5T__set_size(dt->shared->parent, size) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to set size for parent data type");

        /* Adjust size of datatype appropriately */
        if (dt->shared->type == H5T_ARRAY)
            dt->shared->size = dt->shared->parent->shared->size * dt->shared->u.array.nelem;
        else if (dt->shared->type != H5T_VLEN)
            dt->shared->size = dt->shared->parent->shared->size;
    }
    else {
        if (H5T_IS_ATOMIC(dt->shared)) {
            offset = dt->shared->u.atomic.offset;
            prec   = dt->shared->u.atomic.prec;

            /* Decrement the offset and precision if necessary */
            if (prec > 8 * size)
                offset = 0;
            else if (offset + prec > 8 * size)
                offset = 8 * size - prec;
            if (prec > 8 * size)
                prec = 8 * size;
        }
        else
            prec = offset = 0;

        switch (dt->shared->type) {
            case H5T_INTEGER:
            case H5T_TIME:
            case H5T_BITFIELD:
            case H5T_OPAQUE:
                /* nothing to check */
                break;

            case H5T_COMPOUND:
                /* If decreasing size, check the last member isn't being cut. */
                if (size < dt->shared->size) {
                    int      num_membs = 0;
                    unsigned i, max_index            = 0;
                    size_t   memb_offset, max_offset = 0;
                    size_t   max_size;

                    if ((num_membs = H5T_get_nmembers(dt)) < 0)
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to get number of members");

                    if (num_membs) {
                        for (i = 0; i < (unsigned)num_membs; i++) {
                            memb_offset = H5T_get_member_offset(dt, i);
                            if (memb_offset > max_offset) {
                                max_offset = memb_offset;
                                max_index  = i;
                            } /* end if */
                        }     /* end for */

                        max_size = H5T__get_member_size(dt, max_index);

                        if (size < (max_offset + max_size))
                            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
                                        "size shrinking will cut off last member ");
                    } /* end if */

                    /* Compound must not have been packed previously */
                    /* We will check if resizing changed the packed state of
                     * this type at the end of this function */
                    assert(!dt->shared->u.compnd.packed);
                } /* end if */

                break;

            case H5T_STRING:
                /* Convert string to variable-length datatype */
                if (size == H5T_VARIABLE) {
                    H5T_t     *base = NULL; /* base data type */
                    H5T_cset_t tmp_cset;    /* Temp. cset info */
                    H5T_str_t  tmp_strpad;  /* Temp. strpad info */

                    /* Get a copy of unsigned char type as the base/parent type */
                    if (NULL == (base = (H5T_t *)H5I_object(H5T_NATIVE_UCHAR)))
                        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid base datatype");
                    if (NULL == (dt->shared->parent = H5T_copy(base, H5T_COPY_ALL)))
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "unable to copy base datatype");

                    /* change this datatype into a VL string */
                    dt->shared->type = H5T_VLEN;

                    /*
                     * Force conversions (i.e. memory to memory conversions
                     * should duplicate data, not point to the same VL strings)
                     */
                    dt->shared->force_conv = true;

                    /* Before we mess with the info in the union, extract the
                     * values we need */
                    tmp_cset   = dt->shared->u.atomic.u.s.cset;
                    tmp_strpad = dt->shared->u.atomic.u.s.pad;

                    /* This is a string, not a sequence */
                    dt->shared->u.vlen.type = H5T_VLEN_STRING;

                    /* Set character set and padding information */
                    dt->shared->u.vlen.cset = tmp_cset;
                    dt->shared->u.vlen.pad  = tmp_strpad;

                    /* Set up VL information */
                    if (H5T_set_loc(dt, NULL, H5T_LOC_MEMORY) < 0)
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "invalid datatype location");
                } /* end if */
                else {
                    prec   = 8 * size;
                    offset = 0;
                } /* end else */
                break;

            case H5T_FLOAT:
                /*
                 * The sign, mantissa, and exponent fields should be adjusted
                 * first when decreasing the size of a floating point type.
                 */
                if (dt->shared->u.atomic.u.f.sign >= prec + offset ||
                    dt->shared->u.atomic.u.f.epos + dt->shared->u.atomic.u.f.esize > prec + offset ||
                    dt->shared->u.atomic.u.f.mpos + dt->shared->u.atomic.u.f.msize > prec + offset) {
                    HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
                                "adjust sign, mantissa, and exponent fields first");
                }
                break;

            case H5T_ENUM:
            case H5T_VLEN:
            case H5T_ARRAY:
            case H5T_REFERENCE:
                assert("can't happen" && 0);
                break;

            case H5T_NO_CLASS:
            case H5T_NCLASSES:
                assert("invalid type" && 0);
                break;

            default:
                assert("not implemented yet" && 0);
                break;
        } /* end switch */

        /* Commit (if we didn't convert this type to a VL string) */
        if (dt->shared->type != H5T_VLEN) {
            dt->shared->size = size;
            if (H5T_IS_ATOMIC(dt->shared)) {
                dt->shared->u.atomic.offset = offset;
                dt->shared->u.atomic.prec   = prec;
            }
        } /* end if */

        /* Check if the new compound type is packed */
        if (dt->shared->type == H5T_COMPOUND)
            H5T__update_packed(dt);
    } /* end else */

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__set_size() */

/*-------------------------------------------------------------------------
 * Function:  H5T_get_size
 *
 * Purpose:   Determines the total size of a data type in bytes.
 *
 * Return:    Success:    Size of the data type in bytes.     The size of
 *                the data type is the size of an instance of
 *                that data type.
 *
 *            Failure:    0 (valid data types are never zero size)
 *
 *-------------------------------------------------------------------------
 */
size_t
H5T_get_size(const H5T_t *dt)
{
    /* Use FUNC_ENTER_NOAPI_NOINIT_NOERR here to avoid performance issues */
    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /* check args */
    assert(dt);
    assert(dt->shared);

    FUNC_LEAVE_NOAPI(dt->shared->size)
} /* end H5T_get_size() */

/*-------------------------------------------------------------------------
 * Function:  H5T_cmp
 *
 * Purpose:   Compares two data types.
 *
 * Return:    Success:    0 if DT1 and DT2 are equal.
 *                       <0 if DT1 is less than DT2.
 *                       >0 if DT1 is greater than DT2.
 *
 *            Failure:    0, never fails
 *
 *-------------------------------------------------------------------------
 */
int
H5T_cmp(const H5T_t *dt1, const H5T_t *dt2, bool superset)
{
    unsigned *idx1 = NULL, *idx2 = NULL;
    size_t    base_size;
    bool      swapped;
    unsigned  u;
    int       tmp;
    int       ret_value = 0;

    FUNC_ENTER_NOAPI(0)

    /* Sanity check */
    assert(dt1);
    assert(dt2);

    /* the easy case */
    if (dt1 == dt2)
        HGOTO_DONE(0);

    assert(dt1->shared);
    assert(dt2->shared);

    /* compare */
    if (dt1->shared->type < dt2->shared->type)
        HGOTO_DONE(-1);
    if (dt1->shared->type > dt2->shared->type)
        HGOTO_DONE(1);

    if (dt1->shared->size < dt2->shared->size)
        HGOTO_DONE(-1);
    if (dt1->shared->size > dt2->shared->size)
        HGOTO_DONE(1);

    if (dt1->shared->parent && !dt2->shared->parent)
        HGOTO_DONE(-1);
    if (!dt1->shared->parent && dt2->shared->parent)
        HGOTO_DONE(1);
    if (dt1->shared->parent) {
        tmp = H5T_cmp(dt1->shared->parent, dt2->shared->parent, superset);
        if (tmp < 0)
            HGOTO_DONE(-1);
        if (tmp > 0)
            HGOTO_DONE(1);
    } /* end if */

    switch (dt1->shared->type) {
        case H5T_COMPOUND:
            /*
             * Compound data types...
             */
            if (dt1->shared->u.compnd.nmembs == 0 && dt2->shared->u.compnd.nmembs == 0)
                HGOTO_DONE(0);
            if (dt1->shared->u.compnd.nmembs < dt2->shared->u.compnd.nmembs)
                HGOTO_DONE(-1);
            if (dt1->shared->u.compnd.nmembs > dt2->shared->u.compnd.nmembs)
                HGOTO_DONE(1);

            /* Build an index for each type so the names are sorted */
            if (NULL == (idx1 = (unsigned *)H5MM_malloc(dt1->shared->u.compnd.nmembs * sizeof(unsigned))) ||
                NULL == (idx2 = (unsigned *)H5MM_malloc(dt2->shared->u.compnd.nmembs * sizeof(unsigned))))
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, 0, "memory allocation failed");
            for (u = 0; u < dt1->shared->u.compnd.nmembs; u++)
                idx1[u] = idx2[u] = u;
            if (dt1->shared->u.enumer.nmembs > 1) {
                int i;

                for (i = (int)dt1->shared->u.compnd.nmembs - 1, swapped = true; swapped && i >= 0; --i) {
                    int j;

                    for (j = 0, swapped = false; j < i; j++)
                        if (strcmp(dt1->shared->u.compnd.memb[idx1[j]].name,
                                   dt1->shared->u.compnd.memb[idx1[j + 1]].name) > 0) {
                            unsigned tmp_idx = idx1[j];
                            idx1[j]          = idx1[j + 1];
                            idx1[j + 1]      = tmp_idx;
                            swapped          = true;
                        }
                }
                for (i = (int)dt2->shared->u.compnd.nmembs - 1, swapped = true; swapped && i >= 0; --i) {
                    int j;

                    for (j = 0, swapped = false; j < i; j++)
                        if (strcmp(dt2->shared->u.compnd.memb[idx2[j]].name,
                                   dt2->shared->u.compnd.memb[idx2[j + 1]].name) > 0) {
                            unsigned tmp_idx = idx2[j];
                            idx2[j]          = idx2[j + 1];
                            idx2[j + 1]      = tmp_idx;
                            swapped          = true;
                        }
                }
            } /* end if */

#ifdef H5T_DEBUG
            /* I don't quite trust the code above yet :-)  --RPM */
            if (dt1->shared->u.compnd.nmembs > 0) {
                for (u = 0; u < dt1->shared->u.compnd.nmembs - 1; u++) {
                    assert(strcmp(dt1->shared->u.compnd.memb[idx1[u]].name,
                                  dt1->shared->u.compnd.memb[idx1[u + 1]].name));
                    assert(strcmp(dt2->shared->u.compnd.memb[idx2[u]].name,
                                  dt2->shared->u.compnd.memb[idx2[u + 1]].name));
                }
            }
#endif

            /* Compare the members */
            for (u = 0; u < dt1->shared->u.compnd.nmembs; u++) {
                tmp = strcmp(dt1->shared->u.compnd.memb[idx1[u]].name,
                             dt2->shared->u.compnd.memb[idx2[u]].name);
                if (tmp < 0)
                    HGOTO_DONE(-1);
                if (tmp > 0)
                    HGOTO_DONE(1);

                if (dt1->shared->u.compnd.memb[idx1[u]].offset < dt2->shared->u.compnd.memb[idx2[u]].offset)
                    HGOTO_DONE(-1);
                if (dt1->shared->u.compnd.memb[idx1[u]].offset > dt2->shared->u.compnd.memb[idx2[u]].offset)
                    HGOTO_DONE(1);

                if (dt1->shared->u.compnd.memb[idx1[u]].size < dt2->shared->u.compnd.memb[idx2[u]].size)
                    HGOTO_DONE(-1);
                if (dt1->shared->u.compnd.memb[idx1[u]].size > dt2->shared->u.compnd.memb[idx2[u]].size)
                    HGOTO_DONE(1);

                tmp = H5T_cmp(dt1->shared->u.compnd.memb[idx1[u]].type,
                              dt2->shared->u.compnd.memb[idx2[u]].type, superset);
                if (tmp < 0)
                    HGOTO_DONE(-1);
                if (tmp > 0)
                    HGOTO_DONE(1);
            }
            break;

        case H5T_ENUM:
            /*
             * Enumeration data types...
             */
            if (dt1->shared->u.enumer.nmembs == 0 && dt2->shared->u.enumer.nmembs == 0)
                HGOTO_DONE(0);

            /* If we are doing a "superset" comparison, dt2 is allowed to have
             * more members than dt1
             */
            if (superset) {
                if (dt1->shared->u.enumer.nmembs > dt2->shared->u.enumer.nmembs)
                    HGOTO_DONE(1);
            } /* end if */
            else {
                if (dt1->shared->u.enumer.nmembs < dt2->shared->u.enumer.nmembs)
                    HGOTO_DONE(-1);
                if (dt1->shared->u.enumer.nmembs > dt2->shared->u.enumer.nmembs)
                    HGOTO_DONE(1);
            } /* end else */

            /* Build an index for each type so the names are sorted */
            if (NULL == (idx1 = (unsigned *)H5MM_malloc(dt1->shared->u.enumer.nmembs * sizeof(unsigned))) ||
                NULL == (idx2 = (unsigned *)H5MM_malloc(dt2->shared->u.enumer.nmembs * sizeof(unsigned))))
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, 0, "memory allocation failed");
            for (u = 0; u < dt1->shared->u.enumer.nmembs; u++)
                idx1[u] = u;
            if (dt1->shared->u.enumer.nmembs > 1) {
                int i;
                for (i = (int)dt1->shared->u.enumer.nmembs - 1, swapped = true; swapped && i >= 0; --i) {
                    int j;

                    for (j = 0, swapped = false; j < i; j++)
                        if (strcmp(dt1->shared->u.enumer.name[idx1[j]],
                                   dt1->shared->u.enumer.name[idx1[j + 1]]) > 0) {
                            unsigned tmp_idx = idx1[j];
                            idx1[j]          = idx1[j + 1];
                            idx1[j + 1]      = tmp_idx;
                            swapped          = true;
                        }
                }
            }
            for (u = 0; u < dt2->shared->u.enumer.nmembs; u++)
                idx2[u] = u;
            if (dt2->shared->u.enumer.nmembs > 1) {
                int i;

                for (i = (int)dt2->shared->u.enumer.nmembs - 1, swapped = true; swapped && i >= 0; --i) {
                    int j;

                    for (j = 0, swapped = false; j < i; j++)
                        if (strcmp(dt2->shared->u.enumer.name[idx2[j]],
                                   dt2->shared->u.enumer.name[idx2[j + 1]]) > 0) {
                            unsigned tmp_idx = idx2[j];
                            idx2[j]          = idx2[j + 1];
                            idx2[j + 1]      = tmp_idx;
                            swapped          = true;
                        }
                }
            }

#ifdef H5T_DEBUG
            /* I don't quite trust the code above yet :-)  --RPM */
            if (dt1->shared->u.enumer.nmembs > 0) {
                for (u = 0; u < dt1->shared->u.enumer.nmembs - 1; u++) {
                    assert(
                        strcmp(dt1->shared->u.enumer.name[idx1[u]], dt1->shared->u.enumer.name[idx1[u + 1]]));
                    assert(
                        strcmp(dt2->shared->u.enumer.name[idx2[u]], dt2->shared->u.enumer.name[idx2[u + 1]]));
                }
            }
#endif

            /* Compare the members */
            base_size = dt1->shared->parent->shared->size;
            for (u = 0; u < dt1->shared->u.enumer.nmembs; u++) {
                unsigned idx = 0;

                if (superset) {
                    unsigned lt  = 0, rt; /* Final, left & right key indices */
                    int      cmp = 1;     /* Key comparison value */

                    /* If a superset is allowed, dt2 may have more members
                     * than dt1, so binary search for matching member name in
                     * dt2
                     */
                    rt = dt2->shared->u.enumer.nmembs;

                    while (lt < rt && cmp) {
                        idx = (lt + rt) / 2;

                        /* compare */
                        if ((cmp = strcmp(dt1->shared->u.enumer.name[idx1[u]],
                                          dt2->shared->u.enumer.name[idx2[idx]])) < 0)
                            rt = idx;
                        else
                            lt = idx + 1;
                    }
                    /* Leave, if we couldn't find match */
                    if (cmp)
                        HGOTO_DONE(-1);
                } /* end if */
                else {
                    /* Check for exact member name match when not doing
                     * "superset" comparison
                     */
                    tmp = strcmp(dt1->shared->u.enumer.name[idx1[u]], dt2->shared->u.enumer.name[idx2[u]]);
                    if (tmp < 0)
                        HGOTO_DONE(-1);
                    if (tmp > 0)
                        HGOTO_DONE(1);

                    /* Set index value appropriately */
                    idx = u;
                } /* end else */

                tmp = memcmp((uint8_t *)dt1->shared->u.enumer.value + idx1[u] * base_size,
                             (uint8_t *)dt2->shared->u.enumer.value + idx2[idx] * base_size, base_size);
                if (tmp < 0)
                    HGOTO_DONE(-1);
                if (tmp > 0)
                    HGOTO_DONE(1);
            }
            break;

        case H5T_VLEN:
            assert(dt1->shared->u.vlen.type > H5T_VLEN_BADTYPE &&
                   dt1->shared->u.vlen.type < H5T_VLEN_MAXTYPE);
            assert(dt2->shared->u.vlen.type > H5T_VLEN_BADTYPE &&
                   dt2->shared->u.vlen.type < H5T_VLEN_MAXTYPE);
            assert(dt1->shared->u.vlen.loc >= H5T_LOC_BADLOC && dt1->shared->u.vlen.loc < H5T_LOC_MAXLOC);
            assert(dt2->shared->u.vlen.loc >= H5T_LOC_BADLOC && dt2->shared->u.vlen.loc < H5T_LOC_MAXLOC);

            /* Arbitrarily sort sequence VL datatypes before string VL datatypes */
            if (dt1->shared->u.vlen.type == H5T_VLEN_SEQUENCE &&
                dt2->shared->u.vlen.type == H5T_VLEN_STRING) {
                HGOTO_DONE(-1);
            }
            else if (dt1->shared->u.vlen.type == H5T_VLEN_STRING &&
                     dt2->shared->u.vlen.type == H5T_VLEN_SEQUENCE) {
                HGOTO_DONE(1);
            }
            /* Arbitrarily sort VL datatypes in memory before disk */
            if (dt1->shared->u.vlen.loc == H5T_LOC_MEMORY && dt2->shared->u.vlen.loc == H5T_LOC_DISK) {
                HGOTO_DONE(-1);
            }
            else if (dt1->shared->u.vlen.loc == H5T_LOC_DISK && dt2->shared->u.vlen.loc == H5T_LOC_MEMORY) {
                HGOTO_DONE(1);
            }
            else if (dt1->shared->u.vlen.loc == H5T_LOC_BADLOC && dt2->shared->u.vlen.loc != H5T_LOC_BADLOC) {
                HGOTO_DONE(1);
            }

            /* Don't allow VL types in different files to compare as equal */
            if (dt1->shared->u.vlen.file < dt2->shared->u.vlen.file)
                HGOTO_DONE(-1);
            if (dt1->shared->u.vlen.file > dt2->shared->u.vlen.file)
                HGOTO_DONE(1);
            break;

        case H5T_OPAQUE:
            if (dt1->shared->u.opaque.tag && dt2->shared->u.opaque.tag)
                HGOTO_DONE(strcmp(dt1->shared->u.opaque.tag, dt2->shared->u.opaque.tag));
            break;

        case H5T_ARRAY:
            if (dt1->shared->u.array.ndims < dt2->shared->u.array.ndims)
                HGOTO_DONE(-1);
            if (dt1->shared->u.array.ndims > dt2->shared->u.array.ndims)
                HGOTO_DONE(1);

            for (u = 0; u < dt1->shared->u.array.ndims; u++) {
                if (dt1->shared->u.array.dim[u] < dt2->shared->u.array.dim[u])
                    HGOTO_DONE(-1);
                if (dt1->shared->u.array.dim[u] > dt2->shared->u.array.dim[u])
                    HGOTO_DONE(1);
            }

            tmp = H5T_cmp(dt1->shared->parent, dt2->shared->parent, superset);
            if (tmp < 0)
                HGOTO_DONE(-1);
            if (tmp > 0)
                HGOTO_DONE(1);
            break;

        case H5T_NO_CLASS:
        case H5T_INTEGER:
        case H5T_FLOAT:
        case H5T_TIME:
        case H5T_STRING:
        case H5T_BITFIELD:
        case H5T_REFERENCE:
        case H5T_NCLASSES:
        default:
            /*
             * Atomic datatypes...
             */
            if (dt1->shared->u.atomic.order < dt2->shared->u.atomic.order)
                HGOTO_DONE(-1);
            if (dt1->shared->u.atomic.order > dt2->shared->u.atomic.order)
                HGOTO_DONE(1);

            if (dt1->shared->u.atomic.prec < dt2->shared->u.atomic.prec)
                HGOTO_DONE(-1);
            if (dt1->shared->u.atomic.prec > dt2->shared->u.atomic.prec)
                HGOTO_DONE(1);

            if (dt1->shared->u.atomic.offset < dt2->shared->u.atomic.offset)
                HGOTO_DONE(-1);
            if (dt1->shared->u.atomic.offset > dt2->shared->u.atomic.offset)
                HGOTO_DONE(1);

            if (dt1->shared->u.atomic.lsb_pad < dt2->shared->u.atomic.lsb_pad)
                HGOTO_DONE(-1);
            if (dt1->shared->u.atomic.lsb_pad > dt2->shared->u.atomic.lsb_pad)
                HGOTO_DONE(1);

            if (dt1->shared->u.atomic.msb_pad < dt2->shared->u.atomic.msb_pad)
                HGOTO_DONE(-1);
            if (dt1->shared->u.atomic.msb_pad > dt2->shared->u.atomic.msb_pad)
                HGOTO_DONE(1);

            switch (dt1->shared->type) {
                case H5T_INTEGER:
                    if (dt1->shared->u.atomic.u.i.sign < dt2->shared->u.atomic.u.i.sign)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.i.sign > dt2->shared->u.atomic.u.i.sign)
                        HGOTO_DONE(1);
                    break;

                case H5T_FLOAT:
                    if (dt1->shared->u.atomic.u.f.sign < dt2->shared->u.atomic.u.f.sign)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.f.sign > dt2->shared->u.atomic.u.f.sign)
                        HGOTO_DONE(1);

                    if (dt1->shared->u.atomic.u.f.epos < dt2->shared->u.atomic.u.f.epos)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.f.epos > dt2->shared->u.atomic.u.f.epos)
                        HGOTO_DONE(1);

                    if (dt1->shared->u.atomic.u.f.esize < dt2->shared->u.atomic.u.f.esize)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.f.esize > dt2->shared->u.atomic.u.f.esize)
                        HGOTO_DONE(1);

                    if (dt1->shared->u.atomic.u.f.ebias < dt2->shared->u.atomic.u.f.ebias)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.f.ebias > dt2->shared->u.atomic.u.f.ebias)
                        HGOTO_DONE(1);

                    if (dt1->shared->u.atomic.u.f.mpos < dt2->shared->u.atomic.u.f.mpos)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.f.mpos > dt2->shared->u.atomic.u.f.mpos)
                        HGOTO_DONE(1);

                    if (dt1->shared->u.atomic.u.f.msize < dt2->shared->u.atomic.u.f.msize)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.f.msize > dt2->shared->u.atomic.u.f.msize)
                        HGOTO_DONE(1);

                    if (dt1->shared->u.atomic.u.f.norm < dt2->shared->u.atomic.u.f.norm)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.f.norm > dt2->shared->u.atomic.u.f.norm)
                        HGOTO_DONE(1);

                    if (dt1->shared->u.atomic.u.f.pad < dt2->shared->u.atomic.u.f.pad)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.f.pad > dt2->shared->u.atomic.u.f.pad)
                        HGOTO_DONE(1);

                    break;

                case H5T_TIME: /* order and precision are checked above */
                    /*void */
                    break;

                case H5T_STRING:
                    if (dt1->shared->u.atomic.u.s.cset < dt2->shared->u.atomic.u.s.cset)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.s.cset > dt2->shared->u.atomic.u.s.cset)
                        HGOTO_DONE(1);

                    if (dt1->shared->u.atomic.u.s.pad < dt2->shared->u.atomic.u.s.pad)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.s.pad > dt2->shared->u.atomic.u.s.pad)
                        HGOTO_DONE(1);

                    break;

                case H5T_BITFIELD:
                    /*void */
                    break;

                case H5T_REFERENCE:
                    if (dt1->shared->u.atomic.u.r.rtype < dt2->shared->u.atomic.u.r.rtype)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.r.rtype > dt2->shared->u.atomic.u.r.rtype)
                        HGOTO_DONE(1);
                    if (dt1->shared->u.atomic.u.r.loc < dt2->shared->u.atomic.u.r.loc)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.r.loc > dt2->shared->u.atomic.u.r.loc)
                        HGOTO_DONE(1);
                    if (dt1->shared->u.atomic.u.r.file < dt2->shared->u.atomic.u.r.file)
                        HGOTO_DONE(-1);
                    if (dt1->shared->u.atomic.u.r.file > dt2->shared->u.atomic.u.r.file)
                        HGOTO_DONE(1);
                    break;

                case H5T_NO_CLASS:
                case H5T_OPAQUE:
                case H5T_COMPOUND:
                case H5T_ENUM:
                case H5T_VLEN:
                case H5T_ARRAY:
                case H5T_NCLASSES:
                default:
                    assert("not implemented yet" && 0);
                    break;
            }
            break;
    } /* end switch */

done:
    if (NULL != idx1)
        H5MM_xfree(idx1);
    if (NULL != idx2)
        H5MM_xfree(idx2);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_cmp() */

/*-------------------------------------------------------------------------
 * Function:    H5T__path_table_search
 *
 * Purpose:     Searches the global datatype conversion path table for a
 *              conversion path between two datatypes.
 *
 *              Sets `idx` to be the index of the last table entry compared
 *              (which will be the index of the matching conversion path on
 *              success). If no matching path is found, the value for `idx`
 *              will be the index into the path table where a new path
 *              entry with source and destination datatypes matching src
 *              and dst should be inserted. In this case, the caller should
 *              be sure to increment the index value by 1 if the value of
 *              the last comparison is > 0.
 *
 *              If `last_cmp` is non-NULL, the value of the last comparison
 *              (-1, 0, or 1) is returned through it.
 *
 * Return:    Success:    true (conversion path found, index in *idx)
 *            Failure:    false (no conversion path between types)
 *
 *-------------------------------------------------------------------------
 */
static bool
H5T__path_table_search(const H5T_t *src, const H5T_t *dst, int *idx, int *last_cmp)
{
    int  lt, rt, md;        /* Left, middle, and right edges */
    int  cmp;               /* Comparison result  */
    bool ret_value = false; /* Return value */

    FUNC_ENTER_PACKAGE_NOERR

    /* Sanity check */
    assert(0 != H5T_g.npaths);
    assert(src);
    assert(src->shared);
    assert(dst);
    assert(dst->shared);
    assert(idx);

    /* Find the conversion path in the table, using a binary search */
    /* NOTE: Doesn't match against entry 0, which is the no-op path */
    lt = md = 1;
    rt      = H5T_g.npaths;
    cmp     = -1;

    while (cmp && lt < rt) {
        md = (lt + rt) / 2;
        assert(H5T_g.path[md]);
        cmp = H5T_cmp(src, H5T_g.path[md]->src, false);
        if (0 == cmp)
            cmp = H5T_cmp(dst, H5T_g.path[md]->dst, false);
        if (cmp < 0)
            rt = md;
        else if (cmp > 0)
            lt = md + 1;
        else
            /* Match found */
            ret_value = true;
    }

    /* Set middle index & comparison values */
    *idx = md;
    if (last_cmp)
        *last_cmp = cmp;

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__path_table_search() */

/*-------------------------------------------------------------------------
 * Function:    H5T_path_find
 *
 * Purpose:    Library-internal wrapper to find the path which converts
 *             type SRC to type DST.
 *
 *             If SRC and DST are both null pointers then the special no-op
 *             conversion path is used.
 *
 * Return:    Success:    Pointer to the path, valid until the path
 *                        database is modified.
 *
 *            Failure:    NULL if the path does not exist and no
 *                        function can be found to apply to the new path.
 *
 *-------------------------------------------------------------------------
 */
H5T_path_t *
H5T_path_find(const H5T_t *src, const H5T_t *dst)
{
    H5T_conv_func_t conv_func;        /* Conversion function wrapper */
    H5T_path_t     *ret_value = NULL; /* Return value */

    FUNC_ENTER_NOAPI(NULL)

    /* Sanity check */
    assert(src);
    assert(src->shared);
    assert(dst);
    assert(dst->shared);

    /* Set up conversion function wrapper */
    conv_func.is_app     = false;
    conv_func.u.lib_func = NULL;

    /* Call the internal routine, with additional parameters */
    if (NULL == (ret_value = H5T__path_find_real(src, dst, NULL, &conv_func)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, NULL, "can't find datatype conversion path");

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_path_find() */

/*-------------------------------------------------------------------------
 * Function:    H5T__path_find_real
 *
 * Purpose:    Finds the path which converts type SRC to type DST, creating
 *             a new path if necessary.
 *
 *             If `conv->u.app_func`/`conv->u.lib_func` is non-NULL then it
 *             is set as the hard conversion function for that path
 *             regardless of whether the path previously existed. Changing
 *             the conversion function of a path causes statistics to be
 *             reset to zero after printing them. `name` is used only when
 *             creating a new path and is just for debugging.
 *
 *             If no "force conversion" flags are set for either the source
 *             or destination datatype and the two datatypes compare equal
 *             to each other, then the special no-op conversion path is
 *             used. This path is always stored as the first path in the
 *             path table.
 *
 * Return:    Success:    Pointer to the path, valid until the path
 *                        database is modified.
 *
 *            Failure:    NULL if the path does not exist and no
 *                        function can be found to apply to the new path.
 *
 *-------------------------------------------------------------------------
 */
static H5T_path_t *
H5T__path_find_real(const H5T_t *src, const H5T_t *dst, const char *name, H5T_conv_func_t *conv)
{
    H5T_conv_ctx_t tmp_ctx      = {0};   /* Temporary conversion context object */
    H5T_path_t    *matched_path = NULL;  /* Path existing in the table */
    H5T_path_t    *path         = NULL;  /* Pointer to current path */
    bool           noop_conv    = false; /* Whether this is a no-op conversion */
    bool           new_path     = false; /* Whether we're creating a new path */
    bool new_api_func = false; /* If the caller is an API function specifying a new conversion function */
    bool new_lib_func = false; /* If the caller is a private function specifying a new conversion function */
    int  old_npaths;           /* Previous number of paths in table */
    int  last_cmp         = 0; /* Value of last comparison during binary search */
    int  path_idx         = 0; /* Index into path table for path */
    H5T_path_t *ret_value = NULL; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Sanity check */
    assert(src);
    assert(src->shared);
    assert(dst);
    assert(dst->shared);

    /*
     * Get the datatype conversion exception callback structure.
     * Note that we have to first check if an API context has been
     * pushed, since we could have arrived here during library
     * initialization of the H5T package.
     */
    if (H5CX_pushed() && (H5CX_get_dt_conv_cb(&tmp_ctx.u.init.cb_struct) < 0))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, NULL, "unable to get conversion exception callback");

    /* Make sure the path table is initialized */
    if ((0 == H5T_g.npaths) && (H5T__init_path_table() < 0))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "unable to initialize type conversion path table");

    /* Find the conversion path. If no "force conversion" flags are
     * set and the source and destination types are equal, then use
     * the no-op conversion path. Otherwise, do a binary search over
     * the remaining entries.
     */
    noop_conv =
        src->shared->force_conv == false && dst->shared->force_conv == false && 0 == H5T_cmp(src, dst, true);
    if (noop_conv)
        matched_path = H5T_g.path[0];
    else {
        /* Search the table of conversion paths */
        if (H5T__path_table_search(src, dst, &path_idx, &last_cmp))
            matched_path = H5T_g.path[path_idx];
    }

    /* Keep a record of the number of paths in the table, in case one of the
     * initialization calls below (hard or soft) causes more entries to be
     * added to the table - QAK, 1/26/02
     */
    old_npaths = H5T_g.npaths;

    /* Set a few convenience variables */
    new_api_func = (matched_path && conv->is_app && conv->u.app_func);
    new_lib_func = (matched_path && !conv->is_app && conv->u.lib_func);

    /* If we didn't find the path, if the caller is an API function specifying
     * a new hard conversion function, or if the caller is a private function
     * specifying a new hard conversion and the path is a soft conversion, then
     * create a new path and add the new function to the path.
     */
    new_path = !matched_path || new_api_func || (new_lib_func && !matched_path->is_hard);

    if (new_path) {
        if (NULL == (path = H5FL_CALLOC(H5T_path_t)))
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed for type conversion path");
        if (name && *name) {
            strncpy(path->name, name, (size_t)H5T_NAMELEN - 1);
            path->name[H5T_NAMELEN - 1] = '\0';
        } /* end if */
        else
            snprintf(path->name, sizeof(path->name), "NONAME");
        if (NULL == (path->src = H5T_copy(src, H5T_COPY_ALL)))
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "unable to copy datatype for conversion path");
        if (NULL == (path->dst = H5T_copy(dst, H5T_COPY_ALL)))
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "unable to copy datatype for conversion path");
    } /* end if */
    else
        path = matched_path;

    /* Initialize the path if it's a new path */
    if (new_path && H5T__path_find_init_new_path(path, src, dst, conv, &tmp_ctx) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "unable to initialize new conversion path");

    /* Fail if the path still doesn't have a conversion function at this point */
    if (!path->conv.u.app_func)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "no appropriate function for conversion path");

    /* Check if paths were inserted into the table through a recursive call
     * and re-compute the correct location for this path if so. - QAK, 1/26/02
     */
    if (old_npaths != H5T_g.npaths)
        if (H5T__path_table_search(src, dst, &path_idx, &last_cmp))
            matched_path = H5T_g.path[path_idx];

    /* Replace an existing table entry or add a new entry */
    if (matched_path && new_path) {
        assert(matched_path == H5T_g.path[path_idx]);

        tmp_ctx.u.free.src_type_id = H5I_INVALID_HID;
        tmp_ctx.u.free.dst_type_id = H5I_INVALID_HID;
        if (H5T__path_free(matched_path, &tmp_ctx) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTFREE, NULL, "unable to free datatype conversion path");

        H5T_g.path[path_idx] = path;
    }
    else if (new_path) {
        /* Make space in the table for the new path if necessary */
        if ((size_t)H5T_g.npaths >= H5T_g.apaths) {
            size_t       na = MAX(H5T_DEF_CONV_TABLE_SLOTS, 2 * H5T_g.apaths);
            H5T_path_t **x;

            if (NULL == (x = H5MM_realloc(H5T_g.path, na * sizeof(H5T_path_t *))))
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
            H5T_g.apaths = na;
            H5T_g.path   = x;
        }

        /* Adjust final location in table for new path if the last comparison
         * of paths during binary search was > 0, then shift down all path
         * entries in the table starting at that location to make room for
         * the new path
         */
        assert(last_cmp != 0);
        if (last_cmp > 0)
            path_idx++;
        memmove(H5T_g.path + path_idx + 1, H5T_g.path + path_idx,
                (size_t)(H5T_g.npaths - path_idx) * sizeof(H5T_path_t *));

        H5T_g.npaths++;
        H5T_g.path[path_idx] = path;
    }

    ret_value = path;

done:
    if (!ret_value && path && new_path) {
        if (path->src && (H5T_close_real(path->src) < 0))
            HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, NULL, "unable to close datatype");
        if (path->dst && (H5T_close_real(path->dst) < 0))
            HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, NULL, "unable to close datatype");
        path = H5FL_FREE(H5T_path_t, path);
    }

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__path_find_real() */

/*-------------------------------------------------------------------------
 * Function:  H5T__init_path_table
 *
 * Purpose:   Helper function to allocate and initialize the table holding
 *            pointers to datatype conversion paths. Sets the no-op
 *            conversion path as the first entry in the table.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5T__init_path_table(void)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    /* Sanity check */
    if (0 != H5T_g.npaths)
        HGOTO_ERROR(H5E_DATATYPE, H5E_ALREADYINIT, FAIL,
                    "datatype conversion path table is already initialized");

    if (NULL == (H5T_g.path = H5MM_calloc(H5T_DEF_CONV_TABLE_SLOTS * sizeof(H5T_path_t *))))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL,
                    "memory allocation failed for type conversion path table");
    H5T_g.apaths = H5T_DEF_CONV_TABLE_SLOTS;

    /*
     * Allocate a path for the no-op conversion function
     * and set it as the first entry in the table
     */
    if (NULL == (H5T_g.path[0] = H5FL_CALLOC(H5T_path_t)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "memory allocation failed for no-op conversion path");

    /* Initialize the no-op path */
    snprintf(H5T_g.path[0]->name, sizeof(H5T_g.path[0]->name), "no-op");
    H5T_g.path[0]->conv.is_app     = false;
    H5T_g.path[0]->conv.u.lib_func = H5T__conv_noop;
    H5T_g.path[0]->cdata.command   = H5T_CONV_INIT;

    if (H5T__conv_noop(NULL, NULL, &(H5T_g.path[0]->cdata), NULL, 0, 0, 0, NULL, NULL) < 0) {
#ifdef H5T_DEBUG
        if (H5DEBUG(T))
            fprintf(H5DEBUG(T), "H5T: unable to initialize no-op conversion function (ignored)\n");
#endif
        /* Ignore any errors from the conversion function */
        if (H5E_clear_stack() < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTRESET, FAIL, "unable to clear current error stack");
    } /* end if */

    H5T_g.path[0]->is_noop = true;

    /* Set # of initial paths in the table */
    H5T_g.npaths = 1;

done:
    if (ret_value < 0) {
        if (H5T_g.path)
            H5FL_FREE(H5T_path_t, H5T_g.path[0]);
        H5MM_free(H5T_g.path);
    }

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__init_path_table() */

/*-------------------------------------------------------------------------
 * Function:  H5T__path_find_init_new_path
 *
 * Purpose:   Helper function to initialize a new conversion path that's
 *            being added to the path conversion table.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5T__path_find_init_new_path(H5T_path_t *path, const H5T_t *src, const H5T_t *dst, H5T_conv_func_t *conv,
                             H5T_conv_ctx_t *conv_ctx)
{
    H5T_t *tmp_stype = NULL;            /* temporary source datatype */
    H5T_t *tmp_dtype = NULL;            /* temporary destination datatype */
    hid_t  src_id    = H5I_INVALID_HID; /* source datatype identifier */
    hid_t  dst_id    = H5I_INVALID_HID; /* destination datatype identifier */
    herr_t status    = SUCCEED;
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    assert(path);
    assert(conv);
    assert(conv_ctx);
    assert(NULL == path->conv.u.app_func);

    /* If a hard conversion function was specified, initialize that
     * function and finish setting up the new path.
     */
    if (conv->u.app_func) {
        path->cdata.command = H5T_CONV_INIT;
        if (conv->is_app) {
            /* Copy the conversion path's source and destination datatypes and
             * register an ID for them so we can pass these to the application
             * conversion function
             */
            if (path->src && (NULL == (tmp_stype = H5T_copy(path->src, H5T_COPY_ALL))))
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "unable to copy source datatype");
            if (path->dst && (NULL == (tmp_dtype = H5T_copy(path->dst, H5T_COPY_ALL))))
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "unable to copy destination datatype");

            if (tmp_stype && ((src_id = H5I_register(H5I_DATATYPE, tmp_stype, false)) < 0))
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTREGISTER, FAIL,
                            "unable to register ID for source datatype");
            if (tmp_dtype && ((dst_id = H5I_register(H5I_DATATYPE, tmp_dtype, false)) < 0))
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTREGISTER, FAIL,
                            "unable to register ID for destination datatype");

            status = (conv->u.app_func)(src_id, dst_id, &(path->cdata), 0, 0, 0, NULL, NULL, H5CX_get_dxpl());
        }
        else
            status = (conv->u.lib_func)(path->src, path->dst, &(path->cdata), conv_ctx, 0, 0, 0, NULL, NULL);

        if (status < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to initialize conversion function");

        if (src_id >= 0) {
            if (H5I_dec_ref(src_id) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTDEC, FAIL, "can't decrement reference on temporary ID");
            src_id    = H5I_INVALID_HID;
            tmp_stype = NULL;
        }
        if (dst_id >= 0) {
            if (H5I_dec_ref(dst_id) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTDEC, FAIL, "can't decrement reference on temporary ID");
            dst_id    = H5I_INVALID_HID;
            tmp_dtype = NULL;
        }

        path->conv    = *conv;
        path->is_hard = true;
    }

    /*
     * Otherwise, scan the soft list for an applicable function
     * and add it to the path.
     */
    assert(path->conv.u.app_func || (src && dst));
    for (int i = H5T_g.nsoft - 1; i >= 0 && !path->conv.u.app_func; --i) {
        bool path_init_error = false;

        if (src->shared->type != H5T_g.soft[i].src || dst->shared->type != H5T_g.soft[i].dst)
            continue;

        path->cdata.command = H5T_CONV_INIT;
        if (H5T_g.soft[i].conv.is_app) {
            /* Copy the conversion path's source and destination datatypes and
             * register an ID for them so we can pass these to the application
             * conversion function
             */
            if (NULL == (tmp_stype = H5T_copy(path->src, H5T_COPY_ALL)))
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "unable to copy source datatype");
            if (NULL == (tmp_dtype = H5T_copy(path->dst, H5T_COPY_ALL)))
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "unable to copy destination datatype");

            if ((src_id = H5I_register(H5I_DATATYPE, tmp_stype, false)) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTREGISTER, FAIL,
                            "unable to register ID for source datatype");
            if ((dst_id = H5I_register(H5I_DATATYPE, tmp_dtype, false)) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTREGISTER, FAIL,
                            "unable to register ID for destination datatype");

            status = (H5T_g.soft[i].conv.u.app_func)(src_id, dst_id, &(path->cdata), 0, 0, 0, NULL, NULL,
                                                     H5CX_get_dxpl());
        }
        else
            status = (H5T_g.soft[i].conv.u.lib_func)(path->src, path->dst, &(path->cdata), conv_ctx, 0, 0, 0,
                                                     NULL, NULL);

        if (status < 0) {
            memset(&(path->cdata), 0, sizeof(H5T_cdata_t));
            /* ignore the error */
            if (H5E_clear_stack() < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTRESET, FAIL, "unable to clear current error stack");
            path_init_error = true;
        }

        /* Finish operation, if no error */
        if (!path_init_error) {
            strncpy(path->name, H5T_g.soft[i].name, (size_t)H5T_NAMELEN);
            path->name[H5T_NAMELEN - 1] = '\0';
            path->conv                  = H5T_g.soft[i].conv;
            path->is_hard               = false;
        }

        if (src_id >= 0) {
            if (H5I_dec_ref(src_id) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTDEC, FAIL, "can't decrement reference on temporary ID");
            src_id    = H5I_INVALID_HID;
            tmp_stype = NULL;
        }
        if (dst_id >= 0) {
            if (H5I_dec_ref(dst_id) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CANTDEC, FAIL, "can't decrement reference on temporary ID");
            dst_id    = H5I_INVALID_HID;
            tmp_dtype = NULL;
        }
    }

done:
    if (src_id >= 0) {
        if (H5I_dec_ref(src_id) < 0)
            HDONE_ERROR(H5E_DATATYPE, H5E_CANTDEC, FAIL, "can't decrement reference on temporary ID");
    }
    else if (tmp_stype) {
        if (H5T_close(tmp_stype) < 0)
            HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "can't close temporary datatype");
    }
    if (dst_id >= 0) {
        if (H5I_dec_ref(dst_id) < 0)
            HDONE_ERROR(H5E_DATATYPE, H5E_CANTDEC, FAIL, "can't decrement reference on temporary ID");
    }
    else if (tmp_dtype) {
        if (H5T_close(tmp_dtype) < 0)
            HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "can't close temporary datatype");
    }

    FUNC_LEAVE_NOAPI(ret_value)
}

/*-------------------------------------------------------------------------
 * Function:  H5T__path_free
 *
 * Purpose:   Helper function to free a datatype conversion path. This
 *            function assumes that the 'free' member of the passed in
 *            'conv_ctx' has been initialized.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5T__path_free(H5T_path_t *path, H5T_conv_ctx_t *conv_ctx)
{
    herr_t status    = SUCCEED;
    int    nprint    = 0;
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    assert(path);
    assert(conv_ctx);

    if (path->conv.u.app_func) {
        H5T__print_path_stats(path, &nprint);

        path->cdata.command = H5T_CONV_FREE;

        if (path->conv.is_app)
            status = (path->conv.u.app_func)(conv_ctx->u.free.src_type_id, conv_ctx->u.free.dst_type_id,
                                             &(path->cdata), 0, 0, 0, NULL, NULL, H5CX_get_dxpl());
        else
            status =
                (path->conv.u.lib_func)(path->src, path->dst, &(path->cdata), conv_ctx, 0, 0, 0, NULL, NULL);

        if (status < 0) {
            /* Ignore any error from shutting down the path */
            if (H5E_clear_stack() < 0)
                /* Push error, but keep going */
                HDONE_ERROR(H5E_DATATYPE, H5E_CANTRESET, FAIL, "unable to clear current error stack");

#ifdef H5T_DEBUG
            if (H5DEBUG(T)) {
                fprintf(H5DEBUG(T), "H5T: conversion function 0x%016zx free failed for %s (ignored)\n",
                        path->conv.is_app ? (size_t)path->conv.u.app_func : (size_t)path->conv.u.lib_func,
                        path->name);
            }
#endif
        }
    }

    if (path->src && (H5T_close_real(path->src) < 0))
        /* Push error, but keep going */
        HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "unable to close source datatype");
    if (path->dst && (H5T_close_real(path->dst) < 0))
        /* Push error, but keep going */
        HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "unable to close destination datatype");

    path = H5FL_FREE(H5T_path_t, path);

    FUNC_LEAVE_NOAPI(ret_value)
}

/*-------------------------------------------------------------------------
 * Function:  H5T_path_match
 *
 * Purpose:   Helper function to determine whether a datatype conversion
 *            path object matches against a given set of criteria.
 *
 * Return:    true/false (can't fail)
 *
 *-------------------------------------------------------------------------
 */
static bool
H5T_path_match(H5T_path_t *path, H5T_pers_t pers, const char *name, H5T_t *src, H5T_t *dst,
               H5VL_object_t *owned_vol_obj, H5T_conv_t func)
{
    bool ret_value = true;

    FUNC_ENTER_NOAPI_NOINIT_NOERR

    assert(path);

    if (
        /* Check that the specified conversion function persistence matches */
        ((H5T_PERS_SOFT == pers && path->is_hard) || (H5T_PERS_HARD == pers && !path->is_hard)) ||

        /* Check that the specified conversion path name matches */
        (name && *name && strcmp(name, path->name) != 0) ||

        /*
         * Check that the specified source and destination datatypes match
         * the source and destination datatypes in the conversion path
         */
        (src && H5T_cmp(src, path->src, false)) || (dst && H5T_cmp(dst, path->dst, false)) ||

        /*
         * Check that the specified VOL object pointer matches the `owned_vol_obj`
         * field for either the source datatype or destination datatype in the
         * conversion path
         */
        (owned_vol_obj && (H5T_path_match_find_type_with_volobj(path->src, owned_vol_obj) == false) &&
         (H5T_path_match_find_type_with_volobj(path->dst, owned_vol_obj) == false)) ||

        /* Check that the specified conversion function matches */
        (func && func != path->conv.u.app_func))
        ret_value = false;

    FUNC_LEAVE_NOAPI(ret_value)
} /* H5T_path_match() */

/*-------------------------------------------------------------------------
 * Function:  H5T_path_match_find_type_with_volobj
 *
 * Purpose:   Helper function to determine whether a datatype is or
 *            contains a datatype that has a VOL object pointer matching
 *            the given VOL object pointer.
 *
 * Return:    true/false (can't fail)
 *
 *-------------------------------------------------------------------------
 */
static bool
H5T_path_match_find_type_with_volobj(const H5T_t *datatype, const H5VL_object_t *owned_vol_obj)
{
    bool ret_value = false;

    FUNC_ENTER_NOAPI_NOINIT_NOERR

    assert(datatype);
    assert(owned_vol_obj);

    ret_value = (datatype->shared->owned_vol_obj == owned_vol_obj);
    if (!ret_value) {
        switch (datatype->shared->type) {
            case H5T_COMPOUND:
                for (unsigned i = 0; i < datatype->shared->u.compnd.nmembs; i++) {
                    if (ret_value)
                        break;
                    ret_value = H5T_path_match_find_type_with_volobj(datatype->shared->u.compnd.memb[i].type,
                                                                     owned_vol_obj);
                }
                break;

            case H5T_VLEN:
                /* Should be an error if no parent, but simplify logic for a true/false return value */
                if (datatype->shared->parent)
                    ret_value = H5T_path_match_find_type_with_volobj(datatype->shared->parent, owned_vol_obj);
                break;

            case H5T_ARRAY:
                /* Should be an error if no parent, but simplify logic for a true/false return value */
                if (datatype->shared->parent)
                    ret_value = H5T_path_match_find_type_with_volobj(datatype->shared->parent, owned_vol_obj);
                break;

            case H5T_INTEGER:
            case H5T_FLOAT:
            case H5T_TIME:
            case H5T_STRING:
            case H5T_BITFIELD:
            case H5T_OPAQUE:
            case H5T_REFERENCE: /* Should have been determined by above check */
            case H5T_ENUM:
            case H5T_NO_CLASS: /* Error value, but simplify logic for a true/false return value */
            case H5T_NCLASSES: /* Error value, but simplify logic for a true/false return value */
            default:
                ret_value = false;
                break;
        }
    }

    FUNC_LEAVE_NOAPI(ret_value)
}

/*-------------------------------------------------------------------------
 * Function:  H5T_path_noop
 *
 * Purpose:   Is the path the special no-op path? The no-op function can be
 *            set by the application and there might be more than one no-op
 *            path in a multi-threaded application if one thread is using
 *            the no-op path when some other thread changes its definition.
 *
 * Return:    true/false (can't fail)
 *
 *-------------------------------------------------------------------------
 */
bool
H5T_path_noop(const H5T_path_t *p)
{
    FUNC_ENTER_NOAPI_NOINIT_NOERR

    assert(p);

    FUNC_LEAVE_NOAPI(p->is_noop || (p->is_hard && 0 == H5T_cmp(p->src, p->dst, false)))
} /* end H5T_path_noop() */

/*-------------------------------------------------------------------------
 * Function:    H5T_noop_conv
 *
 * Purpose:     Check if a conversion between two dataypes will be a no-op
 *
 * Return:      true / false (can't fail)
 *
 *-------------------------------------------------------------------------
 */
bool
H5T_noop_conv(const H5T_t *src, const H5T_t *dst)
{
    bool ret_value = false; /* Return value */

    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /* Sanity check */
    assert(src);
    assert(src->shared);
    assert(dst);
    assert(dst->shared);

    /* Check the conversion path.  If source and destination types are equal
     * then its a no-op conversion, as long as neither type has a "force conversion"
     * flag.  Otherwise search over the conversion table entries.
     */
    if (src->shared->force_conv == false && dst->shared->force_conv == false &&
        0 == H5T_cmp(src, dst, true)) {
        ret_value = true;
    } /* end if */
    else {
        int idx = 0; /* Matching entry */

        /* Search the table of conversion paths */
        if (H5T__path_table_search(src, dst, &idx, NULL))
            ret_value = H5T_path_noop(H5T_g.path[idx]);
    } /* end else */

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_noop_conv() */

/*-------------------------------------------------------------------------
 * Function:  H5T_path_compound_subset
 *
 * Purpose:   Checks if the library's compound conversion function
 *            is in use. Tells whether whether the source members are
 *            a subset of destination, and the order is the same, and
 *            no conversion is needed.  For example:
 *                  struct source {            struct destination {
 *                      TYPE1 A;      -->          TYPE1 A;
 *                      TYPE2 B;      -->          TYPE2 B;
 *                      TYPE3 C;      -->          TYPE3 C;
 *                  };                             TYPE4 D;
 *                                                 TYPE5 E;
 *                                             };
 *
 * Return:    A pointer to the subset info struct in p, or NULL if the
 *            library's compound conversion function is not in use.
 *            Points directly into the H5T_path_t structure.
 *
 *-------------------------------------------------------------------------
 */
H5T_subset_info_t *
H5T_path_compound_subset(const H5T_path_t *p)
{
    H5T_subset_info_t *ret_value = NULL;

    FUNC_ENTER_NOAPI_NOINIT_NOERR

    assert(p);

    /* Only retrieve private info if the library compound conversion
     * function is in use */
    if (!p->conv.is_app && (p->conv.u.lib_func == H5T__conv_struct))
        ret_value = H5T__conv_struct_subset(&(p->cdata));

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_path_compound_subset */

/*-------------------------------------------------------------------------
 * Function:  H5T_path_bkg
 *
 * Purpose:   Get the "background" flag for the conversion path.
 *
 * Return:    Background flag (can't fail)
 *
 *-------------------------------------------------------------------------
 */
H5T_bkg_t
H5T_path_bkg(const H5T_path_t *p)
{
    FUNC_ENTER_NOAPI_NOINIT_NOERR

    assert(p);

    FUNC_LEAVE_NOAPI(p->cdata.need_bkg)
} /* end H5T_path_bkg() */

/*-------------------------------------------------------------------------
 * Function:  H5T__compiler_conv
 *
 * Purpose:   Private function for H5Tcompiler_conv.  Finds out whether the
 *            library's conversion function from type SRC to type DST
 *            is a hard conversion.
 *
 * Return:    true:           hard conversion.
 *            false:          soft conversion.
 *            FAIL:           function failed.
 *
 *-------------------------------------------------------------------------
 */
static htri_t
H5T__compiler_conv(H5T_t *src, H5T_t *dst)
{
    H5T_path_t *path;
    htri_t      ret_value = FAIL; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Find it */
    if (NULL == (path = H5T_path_find(src, dst)))
        HGOTO_ERROR(H5E_DATATYPE, H5E_NOTFOUND, FAIL, "conversion function not found");

    ret_value = (htri_t)path->is_hard;

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__compiler_conv() */

/*-------------------------------------------------------------------------
 * Function:  H5T_convert
 *
 * Purpose:   Call a conversion function to convert from source to
 *            destination data type and accumulate timing statistics.
 *
 * Return:    Success:    non-negative
 *
 *            Failure:    negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T_convert(H5T_path_t *tpath, const H5T_t *src_type, const H5T_t *dst_type, size_t nelmts, size_t buf_stride,
            size_t bkg_stride, void *buf, void *bkg)
{
    H5T_conv_ctx_t conv_ctx = {0};
#ifdef H5T_DEBUG
    H5_timer_t timer = {0}; /* Timer for conversion */
#endif
    hid_t  src_type_id = H5I_INVALID_HID;
    hid_t  dst_type_id = H5I_INVALID_HID;
    herr_t ret_value   = SUCCEED;

    FUNC_ENTER_NOAPI(FAIL)

#ifdef H5T_DEBUG
    if (H5DEBUG(T)) {
        /* Initialize and start timer */
        H5_timer_init(&timer);
        H5_timer_start(&timer);
    } /* end if */
#endif

    /* Get the datatype conversion exception callback structure from the API context */
    if (H5CX_get_dt_conv_cb(&conv_ctx.u.conv.cb_struct) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, "unable to get conversion exception callback");

    /*
     * If this is an application conversion function or an exception callback
     * function was supplied, register IDs for the datatypes so we can pass
     * those as appropriate. Also grab the DXPL if necessary so we can pass
     * that to the app conversion function.
     */
    if (tpath->conv.is_app || conv_ctx.u.conv.cb_struct.func) {
        if ((src_type_id = H5I_register(H5I_DATATYPE, src_type, false)) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTREGISTER, FAIL, "unable to register ID for source datatype");
        if ((dst_type_id = H5I_register(H5I_DATATYPE, dst_type, false)) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTREGISTER, FAIL,
                        "unable to register ID for destination datatype");

        if (tpath->conv.is_app)
            conv_ctx.u.conv.dxpl_id = H5CX_get_dxpl();
    }
    conv_ctx.u.conv.src_type_id = src_type_id;
    conv_ctx.u.conv.dst_type_id = dst_type_id;

    if (H5T_convert_with_ctx(tpath, src_type, dst_type, &conv_ctx, nelmts, buf_stride, bkg_stride, buf, bkg) <
        0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCONVERT, FAIL, "datatype conversion failed");

done:
    /* Remove IDs, but don't decrement their reference counts, as they
     * could have been registered for datatypes that weren't copied
     */
    if ((src_type_id >= 0) && (NULL == H5I_remove(src_type_id)))
        HDONE_ERROR(H5E_DATATYPE, H5E_CANTFREE, FAIL, "can't decrement temporary datatype ID");
    if ((dst_type_id >= 0) && (NULL == H5I_remove(dst_type_id)))
        HDONE_ERROR(H5E_DATATYPE, H5E_CANTFREE, FAIL, "can't decrement temporary datatype ID");

#ifdef H5T_DEBUG
    if (H5DEBUG(T)) {
        /* Stop timer */
        H5_timer_stop(&timer);

        /* Record elapsed timer info */
        H5_timer_get_times(timer, &tpath->stats.times);

        /* Increment # of calls and # of elements converted */
        tpath->stats.ncalls++;
        tpath->stats.nelmts += nelmts;
    } /* end if */
#endif

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_convert() */

/*-------------------------------------------------------------------------
 * Function:  H5T_convert_with_ctx
 *
 * Purpose:   Helper routine for H5T_convert that accepts a pointer to a
 *            H5T_conv_ctx_t conversion context structure. Useful for
 *            conversion routines involving container datatypes, such as
 *            compounds, where the conversion context structure that was
 *            setup during the initial H5T_convert call can be reused.
 *            This avoids the expensive and unnecessary overhead of
 *            recreating this structure and possibly re-registering IDs for
 *            the source and destination datatypes for every single member
 *            of the container datatype and every single element being
 *            converted that consists of that container datatype.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T_convert_with_ctx(H5T_path_t *tpath, const H5T_t *src_type, const H5T_t *dst_type,
                     const H5T_conv_ctx_t *conv_ctx, size_t nelmts, size_t buf_stride, size_t bkg_stride,
                     void *buf, void *bkg)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_NOAPI(FAIL)

    /* Call the appropriate conversion callback */
    tpath->cdata.command = H5T_CONV_CONV;
    if (tpath->conv.is_app) {
        if ((tpath->conv.u.app_func)(conv_ctx->u.conv.src_type_id, conv_ctx->u.conv.dst_type_id,
                                     &(tpath->cdata), nelmts, buf_stride, bkg_stride, buf, bkg,
                                     conv_ctx->u.conv.dxpl_id) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCONVERT, FAIL, "datatype conversion failed");
    } /* end if */
    else if ((tpath->conv.u.lib_func)(src_type, dst_type, &(tpath->cdata), conv_ctx, nelmts, buf_stride,
                                      bkg_stride, buf, bkg) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCONVERT, FAIL, "datatype conversion failed");

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_convert_with_ctx() */

/*-------------------------------------------------------------------------
 * Function:  H5T_oloc
 *
 * Purpose:   Returns a pointer to the object location for a named datatype.
 *
 * Return:    Success:    Ptr directly into named datatype
 *            Failure:    NULL
 *
 *-------------------------------------------------------------------------
 */
H5O_loc_t *
H5T_oloc(H5T_t *dt)
{
    H5O_loc_t *ret_value = NULL;

    FUNC_ENTER_NOAPI(NULL)

    assert(dt);

    switch (dt->shared->state) {
        case H5T_STATE_TRANSIENT:
        case H5T_STATE_RDONLY:
        case H5T_STATE_IMMUTABLE:
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "not a named datatype");
        case H5T_STATE_NAMED:
        case H5T_STATE_OPEN:
            assert(dt->sh_loc.type == H5O_SHARE_TYPE_COMMITTED);
            ret_value = &dt->oloc;
            break;
        default:
            HGOTO_ERROR(H5E_DATATYPE, H5E_BADTYPE, NULL, "invalid datatype state");
    } /* end switch */

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_oloc() */

/*-------------------------------------------------------------------------
 * Function:  H5T_nameof
 *
 * Purpose:   Returns a pointer to the path for a named datatype.
 *
 * Return:    Success:    Ptr directly into named datatype
 *            Failure:    NULL
 *
 *-------------------------------------------------------------------------
 */
H5G_name_t *
H5T_nameof(H5T_t *dt)
{
    H5G_name_t *ret_value = NULL;

    FUNC_ENTER_NOAPI(NULL)

    assert(dt);

    switch (dt->shared->state) {
        case H5T_STATE_TRANSIENT:
        case H5T_STATE_RDONLY:
        case H5T_STATE_IMMUTABLE:
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, NULL, "not a named datatype");
        case H5T_STATE_NAMED:
        case H5T_STATE_OPEN:
            ret_value = &(dt->path);
            break;
        default:
            HGOTO_ERROR(H5E_DATATYPE, H5E_BADTYPE, NULL, "invalid datatype state");
    } /* end switch */

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_nameof() */

/*-------------------------------------------------------------------------
 * Function:    H5T_is_immutable
 *
 * Purpose:     Check if a datatype is immutable.
 *
 * Return:      true
 *
 *              false
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5T_is_immutable(const H5T_t *dt)
{
    htri_t ret_value = false;

    FUNC_ENTER_NOAPI_NOERR

    assert(dt);

    if (dt->shared->state == H5T_STATE_IMMUTABLE)
        ret_value = true;

    FUNC_LEAVE_NOAPI(ret_value)
}

/*-------------------------------------------------------------------------
 * Function:    H5T_is_named
 *
 * Purpose:     Check if a datatype is named/committed.
 *
 * Return:      true/false/FAIL
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5T_is_named(const H5T_t *dt)
{
    htri_t ret_value = false;

    FUNC_ENTER_NOAPI_NOERR

    assert(dt);

    if (dt->vol_obj)
        ret_value = true;
    else
        ret_value = (H5T_STATE_OPEN == dt->shared->state || H5T_STATE_NAMED == dt->shared->state);

    FUNC_LEAVE_NOAPI(ret_value)
}

/*-------------------------------------------------------------------------
 * Function:    H5T_convert_committed_datatype
 *
 * Purpose:     To convert the committed datatype "dt" to a transient embedded
 *        type if the file location associated with the committed datatype is
 *        different from the parameter "f".
 *        "f" is the file location where the dataset or attribute will be created.
 *
 * Notes:       See HDFFV-9940
 *
 * Return:      Success:        non-negative
 *              Failure:        negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T_convert_committed_datatype(H5T_t *dt, H5F_t *f)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    assert(dt);
    assert(f);

    if (H5T_is_named(dt) && (dt->sh_loc.file != f)) {
        assert(dt->sh_loc.type == H5O_SHARE_TYPE_COMMITTED);

        H5O_msg_reset_share(H5O_DTYPE_ID, dt);
        if (H5O_loc_free(&dt->oloc) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTRESET, FAIL, "unable to initialize location");
        if (H5G_name_free(&dt->path) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTOPENOBJ, FAIL, "unable to reset path");

        /* If the datatype is committed through the VOL, close it */
        if (NULL != dt->vol_obj) {
            H5VL_object_t *vol_obj = dt->vol_obj;

            /* Close the datatype through the VOL*/
            if (H5VL_datatype_close(vol_obj, H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL) < 0)
                HGOTO_ERROR(H5E_DATATYPE, H5E_CLOSEERROR, FAIL, "unable to close datatype");

            /* Free the datatype and set the VOL object pointer to NULL */
            if (H5VL_free_object(vol_obj) < 0)
                HGOTO_ERROR(H5E_ATTR, H5E_CANTDEC, FAIL, "unable to free VOL object");
            dt->vol_obj = NULL;
        } /* end if */

        dt->shared->state = H5T_STATE_TRANSIENT;
    } /* end if */

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_convert_committed_datatype() */

/*--------------------------------------------------------------------------
 * Function:    H5T_get_ref_type
 *
 * Purpose:     Retrieves the type of reference for a datatype
 *              H5T_t *dt;  IN: datatype pointer for the reference datatype
 *
 * Return:      Success:        A reference type defined in H5Rpublic.h
 *              Failure:        H5R_BADTYPE
 * Notes:       Given a reference datatype object, this function returns the reference type
 *              of the datatype.
 *--------------------------------------------------------------------------
 */
H5R_type_t
H5T_get_ref_type(const H5T_t *dt)
{
    H5R_type_t ret_value = H5R_BADTYPE;

    FUNC_ENTER_NOAPI_NOERR

    assert(dt);

    if (dt->shared->type == H5T_REFERENCE)
        ret_value = dt->shared->u.atomic.u.r.rtype;

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_get_ref_type() */

/*-------------------------------------------------------------------------
 * Function:  H5T_is_sensible
 *
 * Purpose:   Determines if a data type is sensible to store on disk
 *            (i.e. not partially initialized)
 *
 * Return:    Success:    true, false
 *
 *            Failure:    Negative
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5T_is_sensible(const H5T_t *dt)
{
    htri_t ret_value = FAIL; /* Return value */

    FUNC_ENTER_NOAPI_NOERR

    assert(dt);

    switch (dt->shared->type) {
        case H5T_COMPOUND:
            /* Only allow compound datatypes with at least one member to be stored on disk */
            if (dt->shared->u.compnd.nmembs > 0)
                ret_value = true;
            else
                ret_value = false;
            break;

        case H5T_ENUM:
            /* Only allow enum datatypes with at least one member to be stored on disk */
            if (dt->shared->u.enumer.nmembs > 0)
                ret_value = true;
            else
                ret_value = false;
            break;

        case H5T_NO_CLASS:
        case H5T_INTEGER:
        case H5T_FLOAT:
        case H5T_TIME:
        case H5T_STRING:
        case H5T_BITFIELD:
        case H5T_OPAQUE:
        case H5T_REFERENCE:
        case H5T_VLEN:
        case H5T_ARRAY:
        case H5T_NCLASSES:
        default:
            /* Assume all other datatype are sensible to store on disk */
            ret_value = true;
            break;
    } /* end switch */

    FUNC_LEAVE_NOAPI(ret_value)
}

/*--------------------------------------------------------------------------
 NAME
    H5T_set_loc
 PURPOSE
    Recursively mark any datatypes as on disk/in memory
 USAGE
    htri_t H5T_set_loc(dt,f,loc)
        H5T_t *dt;              IN/OUT: Pointer to the datatype to mark
        H5F_t *f;               IN: Pointer to the file the datatype is in
        H5T_loc_t loc           IN: location of type

 RETURNS
    One of two values on success:
        true - If the location of any vlen types changed
        false - If the location of any vlen types is the same
    <0 is returned on failure
 DESCRIPTION
    Recursively descends any VL or compound datatypes to mark all VL datatypes
    as either on disk or in memory.
 --------------------------------------------------------------------------
 */
htri_t
H5T_set_loc(H5T_t *dt, H5VL_object_t *file, H5T_loc_t loc)
{
    htri_t   changed;       /* Whether H5T_set_loc changed the type (even if the size didn't change) */
    htri_t   ret_value = 0; /* Indicate that success, but no location change */
    unsigned i;             /* Local index variable */
    size_t   old_size;      /* Previous size of a field */

    FUNC_ENTER_NOAPI(FAIL)

    assert(dt);
    assert(loc >= H5T_LOC_BADLOC && loc < H5T_LOC_MAXLOC);

    /* Datatypes can't change in size if the force_conv flag is not set */
    if (dt->shared->force_conv) {
        /* Check the datatype of this element */
        switch (dt->shared->type) {
            case H5T_ARRAY: /* Recurse on VL, compound and array base element type */
                /* Recurse if it's VL, compound, enum or array */
                /* (If the force_conv flag is _not_ set, the type cannot change in size, so don't recurse) */
                if (dt->shared->parent->shared->force_conv &&
                    H5T_IS_COMPLEX(dt->shared->parent->shared->type)) {
                    /* Keep the old base element size for later */
                    old_size = dt->shared->parent->shared->size;

                    /* Mark the VL, compound or array type */
                    if ((changed = H5T_set_loc(dt->shared->parent, file, loc)) < 0)
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "Unable to set VL location");
                    if (changed > 0)
                        ret_value = changed;

                    /* Check if the field changed size */
                    if (old_size != dt->shared->parent->shared->size) {
                        /* Adjust the size of the array */
                        dt->shared->size = dt->shared->u.array.nelem * dt->shared->parent->shared->size;
                    } /* end if */
                }     /* end if */
                break;

            case H5T_COMPOUND: /* Check each field and recurse on VL, compound and array type */
            {
                ssize_t accum_change = 0; /* Amount of change in the offset of the fields */

                /* Sort the fields based on offsets */
                H5T__sort_value(dt, NULL);

                for (i = 0; i < dt->shared->u.compnd.nmembs; i++) {
                    H5T_t *memb_type; /* Member's datatype pointer */

                    /* Range check against compound member's offset */
                    if ((accum_change < 0) && ((ssize_t)dt->shared->u.compnd.memb[i].offset < accum_change))
                        HGOTO_ERROR(H5E_DATATYPE, H5E_BADVALUE, FAIL, "invalid field size in datatype");

                    /* Apply the accumulated size change to the offset of the field */
                    dt->shared->u.compnd.memb[i].offset += (size_t)accum_change;

                    /* Set the member type pointer (for convenience) */
                    memb_type = dt->shared->u.compnd.memb[i].type;

                    /* Recurse if it's VL, compound, enum or array */
                    /* (If the force_conv flag is _not_ set, the type cannot change in size, so don't recurse)
                     */
                    if (memb_type->shared->force_conv && H5T_IS_COMPLEX(memb_type->shared->type)) {
                        /* Keep the old field size for later */
                        old_size = memb_type->shared->size;

                        /* Mark the VL, compound, enum or array type */
                        if ((changed = H5T_set_loc(memb_type, file, loc)) < 0)
                            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "Unable to set VL location");
                        if (changed > 0)
                            ret_value = changed;

                        /* Check if the field changed size */
                        if (old_size != memb_type->shared->size) {

                            /* Fail if the old_size is zero */
                            if (0 == old_size)
                                HGOTO_ERROR(H5E_DATATYPE, H5E_BADVALUE, FAIL,
                                            "old_size of zero would cause division by zero");

                            /* Adjust the size of the member */
                            dt->shared->u.compnd.memb[i].size =
                                (dt->shared->u.compnd.memb[i].size * memb_type->shared->size) / old_size;

                            /* Add that change to the accumulated size change */
                            accum_change += (ssize_t)(memb_type->shared->size - old_size);
                        } /* end if */
                    }     /* end if */
                }         /* end for */

                /* Range check against datatype size */
                if ((accum_change < 0) && ((ssize_t)dt->shared->size < accum_change))
                    HGOTO_ERROR(H5E_DATATYPE, H5E_BADVALUE, FAIL, "invalid field size in datatype");

                /* Apply the accumulated size change to the datatype */
                dt->shared->size += (size_t)accum_change;
            } break;

            case H5T_VLEN: /* Recurse on the VL information if it's VL, compound or array, then free VL
                              sequence */
                /* Recurse if it's VL, compound, enum or array (ignore references here so that we can encode
                 * them as part of the same blob)*/
                /* (If the force_conv flag is _not_ set, the type cannot change in size, so don't recurse) */
                if (dt->shared->parent->shared->force_conv &&
                    H5T_IS_COMPLEX(dt->shared->parent->shared->type) &&
                    (dt->shared->parent->shared->type != H5T_REFERENCE)) {
                    if ((changed = H5T_set_loc(dt->shared->parent, file, loc)) < 0)
                        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "Unable to set VL location");
                    if (changed > 0)
                        ret_value = changed;
                } /* end if */

                /* Mark this VL sequence */
                if ((changed = H5T__vlen_set_loc(dt, file, loc)) < 0)
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "Unable to set VL location");
                if (changed > 0)
                    ret_value = changed;
                break;

            case H5T_REFERENCE:
                /* Reference types go through type conversion */
                if ((ret_value = H5T__ref_set_loc(dt, file, loc)) < 0)
                    HGOTO_ERROR(H5E_DATATYPE, H5E_CANTSET, FAIL, "Unable to set reference location");
                break;

            case H5T_NO_CLASS:
            case H5T_INTEGER:
            case H5T_FLOAT:
            case H5T_TIME:
            case H5T_STRING:
            case H5T_BITFIELD:
            case H5T_OPAQUE:
            case H5T_ENUM:
            case H5T_NCLASSES:
            default:
                break;
        } /* end switch */
    }     /* end if */

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_set_loc() */

/*-------------------------------------------------------------------------
 * Function:    H5T_is_relocatable
 *
 * Purpose:     Check if a datatype will change between disk and memory.
 *
 * Notes:       Currently, only variable-length and references change
 *              between disk & memory (see cases where things are changed in
 *              the H5T_set_loc() code above).
 *
 * Return:
 *  One of two values on success:
 *      true - If the location of any vlen types changed
 *      false - If the location of any vlen types is the same
 *  <0 is returned on failure
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5T_is_relocatable(const H5T_t *dt)
{
    htri_t ret_value = false;

    FUNC_ENTER_NOAPI_NOERR

    /* Sanity check */
    assert(dt);

    /* VL and reference datatypes are relocatable */
    if (H5T_detect_class(dt, H5T_VLEN, false) || H5T_detect_class(dt, H5T_REFERENCE, false))
        ret_value = true;

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_is_relocatable() */

/*-------------------------------------------------------------------------
 * Function:   H5T__detect_vlen_ref
 *
 * Purpose:    Check whether a datatype contains (or is) a vlen reference
 *             datatype.
 *
 * Return:      true (1) or false (0) on success
 *        (Can't fail)
 *
 *-------------------------------------------------------------------------
 */
static bool
H5T__detect_vlen_ref(const H5T_t *dt)
{
    unsigned u;                 /* Local index variable */
    bool     ret_value = false; /* Return value */

    FUNC_ENTER_PACKAGE_NOERR

    /* Sanity checks */
    assert(dt);

    /* Check if this datatype is a vlen reference */
    /* TODO currently H5T_STD_REF is always considered as a vlen type */
    if (H5T_REFERENCE == dt->shared->type && !dt->shared->u.atomic.u.r.opaque)
        HGOTO_DONE(true);

    /* Check for types that might have the correct type as a component */
    switch (dt->shared->type) {
        case H5T_COMPOUND:
            /* Iterate over all the compound datatype's fields */
            for (u = 0; u < dt->shared->u.compnd.nmembs; u++)
                /* Recurse on field's datatype */
                if (H5T__detect_vlen_ref(dt->shared->u.compnd.memb[u].type))
                    HGOTO_DONE(true);
            break;

        case H5T_ARRAY:
        case H5T_VLEN:
        case H5T_ENUM:
            HGOTO_DONE(H5T__detect_vlen_ref(dt->shared->parent));
            break;

        case H5T_NO_CLASS:
        case H5T_INTEGER:
        case H5T_FLOAT:
        case H5T_TIME:
        case H5T_STRING:
        case H5T_BITFIELD:
        case H5T_OPAQUE:
        case H5T_REFERENCE:
        case H5T_NCLASSES:
        default:
            break;
    } /* end if */

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__detect_vlen_ref() */

/*-------------------------------------------------------------------------
 * Function:    H5T_is_vl_storage
 *
 * Purpose:     Check if a datatype will be stored in a variable-length form.
 *
 * Notes:       Currently, only variable-length string & sequences and region
 *              references are stored in a variable-length form.
 *
 * Return:
 *  One of two values on success:
 *      true - If the datatype will be stored in a variable-length form
 *      false - If the datatype will NOT be stored in a variable-length form
 *  <0 is returned on failure
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5T_is_vl_storage(const H5T_t *dt)
{
    htri_t ret_value = false;

    FUNC_ENTER_NOAPI_NOERR

    /* Sanity check */
    assert(dt);

    /* VL and region reference datatypes are stored in variable-length form */
    if (H5T_detect_class(dt, H5T_VLEN, false))
        ret_value = true;
    else if (H5T_detect_class(dt, H5T_REFERENCE, false))
        ret_value = H5T__detect_vlen_ref(dt);
    else
        ret_value = false;

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_is_vl_storage() */

/*-------------------------------------------------------------------------
 * Function:    H5T__upgrade_version_cb
 *
 * Purpose:     H5T__visit callback to Upgrade the version of a datatype
 *              (if there's any benefit to doing so)
 *
 * Note:        The behavior below is tightly coupled with the "better"
 *              encodings for datatype messages in the datatype message
 *              encoding routine.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5T__upgrade_version_cb(H5T_t *dt, void *op_value)
{
    FUNC_ENTER_PACKAGE_NOERR

    /* Sanity check */
    assert(dt);
    assert(op_value);

    /* Special behavior for each type of datatype */
    switch (dt->shared->type) {
        case H5T_COMPOUND:
        case H5T_ARRAY:
        case H5T_ENUM:
            /* These types benefit from "upgrading" their version */
            if (*(unsigned *)op_value > dt->shared->version)
                dt->shared->version = *(unsigned *)op_value;
            break;

        case H5T_VLEN:
            if (dt->shared->parent->shared->version > dt->shared->version)
                dt->shared->version = dt->shared->parent->shared->version;
            break;

        case H5T_NO_CLASS:
        case H5T_INTEGER:
        case H5T_FLOAT:
        case H5T_TIME:
        case H5T_STRING:
        case H5T_BITFIELD:
        case H5T_OPAQUE:
        case H5T_REFERENCE:
        case H5T_NCLASSES:
        default:
            break;
    } /* end switch */

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* end H5T__upgrade_version_cb() */

/*-------------------------------------------------------------------------
 * Function:    H5T__upgrade_version
 *
 * Purpose:     Upgrade the version of a datatype (if there's any benefit to
 *              doing so) and recursively apply to compound members and/or
 *              parent datatypes.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T__upgrade_version(H5T_t *dt, unsigned new_version)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Sanity check */
    assert(dt);

    /* Iterate over entire datatype, upgrading the version of components, if it's useful */
    if (H5T__visit(dt, (H5T_VISIT_SIMPLE | H5T_VISIT_COMPLEX_LAST), H5T__upgrade_version_cb, &new_version) <
        0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_BADITER, FAIL, "iteration to upgrade datatype encoding version failed");

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T__upgrade_version() */

/*-------------------------------------------------------------------------
 * Function:    H5T_set_version
 *
 * Purpose:     Set the encoding for a datatype to the version indicated by
 *              the file's low bound if that is higher than the datatype's
 *              version.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T_set_version(H5F_t *f, H5T_t *dt)
{
    unsigned vers;                /* The version */
    herr_t   ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    /* Sanity check */
    assert(f);
    assert(dt);

    vers = H5O_dtype_ver_bounds[H5F_LOW_BOUND(f)];
    if (vers > dt->shared->version) {
        /* Upgrade the format version for the datatype */
        if (H5T__upgrade_version(dt, vers) < 0)
            HGOTO_ERROR(H5E_DATATYPE, H5E_CANTSET, FAIL, "can't upgrade datatype encoding");
    }

    /* Version bounds check */
    if (dt->shared->version > H5O_dtype_ver_bounds[H5F_HIGH_BOUND(f)])
        HGOTO_ERROR(H5E_DATATYPE, H5E_BADRANGE, FAIL, "Datatype version out of bounds");

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_set_version() */

/*-------------------------------------------------------------------------
 * Function:    H5T_patch_file
 *
 * Purpose:     Patch the top-level file pointers contained in dt to point
 *              to f, if dt is a committed type.  This is possible because
 *              the top-level file pointer can be closed out from under
 *              dt while dt is contained in the shared file's cache.
 *
 * Return:      SUCCEED
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T_patch_file(H5T_t *dt, H5F_t *f)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_NOAPI_NOERR

    /* Sanity check */
    assert(dt);
    assert(f);

    if (H5T_STATE_OPEN == dt->shared->state || H5T_STATE_NAMED == dt->shared->state) {
        dt->oloc.file   = f;
        dt->sh_loc.file = f;
    } /* end if */

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_patch_file() */

/*-------------------------------------------------------------------------
 * Function:    H5T_patch_vlen_file
 *
 * Purpose:     Patch the top-level file pointer contained in (dt->shared->u.vlen.file)
 *              to point to file.  This is possible because
 *              the top-level file pointer can be closed out from under
 *              dt while dt is contained in the shared file's cache.
 *
 * Return:      SUCCEED
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T_patch_vlen_file(H5T_t *dt, H5VL_object_t *file)
{
    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /* Sanity check */
    assert(dt);
    assert(dt->shared);
    assert(file);

    if ((dt->shared->type == H5T_VLEN) && dt->shared->u.vlen.file != file)
        dt->shared->u.vlen.file = file;

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* end H5T_patch_vlen_file() */

/*-------------------------------------------------------------------------
 * Function:    H5T_own_vol_obj
 *
 * Purpose:     Transfers ownership of the supplied VOL object to the
 *              datatype, the VOL object will be freed when the datatype
 *              is closed.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5T_own_vol_obj(H5T_t *dt, H5VL_object_t *vol_obj)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_NOAPI(FAIL)

    /* Sanity check */
    assert(dt);
    assert(dt->shared);
    assert(vol_obj);

    /* Currently no support for owning multiple VOL objects, free the previous
     * owned object.  Currently this is only used for holding open VOL objects
     * used in the "loc" for vlens and references, so if this is being
     * overwritten we don't need the old one anyways. */
    if (dt->shared->owned_vol_obj && H5VL_free_object(dt->shared->owned_vol_obj) < 0)
        HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "unable to close owned VOL object");

    /* Take ownership */
    dt->shared->owned_vol_obj = vol_obj;
    (void)H5VL_object_inc_rc(vol_obj);

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_own_vol_obj() */

/*-------------------------------------------------------------------------
 * Function:    H5T__get_path_table_npaths
 *
 * Purpose:     Testing function to return the number of type conversion
 *              paths currently stored in the type conversion path table
 *              cache.
 *
 * Return:      Number of type conversion paths (can't fail)
 *
 *-------------------------------------------------------------------------
 */
int
H5T__get_path_table_npaths(void)
{
    int ret_value = 0;

    FUNC_ENTER_PACKAGE_NOERR

    ret_value = H5T_g.npaths;

    FUNC_LEAVE_NOAPI(ret_value)
}

/*-------------------------------------------------------------------------
 * Function:    H5T_is_numeric_with_unusual_unused_bits
 *
 * Purpose:     Detect if a datatype is a numeric datatype (int, float, or
 *              bitfield) with an unusual # of unused bits.  This means
 *              that the precision (i.e. the # of bits used) is less than
 *              the size of the datatype, at power-of-two boundaries.
 *
 * Return:      true/false on success, can't fail
 *
 *-------------------------------------------------------------------------
 */
bool
H5T_is_numeric_with_unusual_unused_bits(const H5T_t *dt)
{
    bool ret_value = false;

    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /* Sanity check */
    assert(dt);
    assert(dt->shared);

    /* Is the correct type? */
    if (H5T_INTEGER == dt->shared->type || H5T_FLOAT == dt->shared->type ||
        H5T_BITFIELD == dt->shared->type) {

        /* Has unused bits? */
        if (dt->shared->size > 1 && dt->shared->u.atomic.prec < (dt->shared->size * 8))
            /* Unused bits are unusually large? */
            ret_value =
                (dt->shared->size * 8) > (2 * (dt->shared->u.atomic.prec + dt->shared->u.atomic.offset));
    }

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5T_is_numeric_with_unusual_unused_bits() */
