/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * 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 Setup */
/****************/

#include "H5Rmodule.h" /* This source code file is part of the H5R 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 "H5Gprivate.h"  /* Groups                                   */
#include "H5HGprivate.h" /* Global Heaps                             */
#include "H5Iprivate.h"  /* IDs                                      */
#include "H5MMprivate.h" /* Memory management                        */
#include "H5Oprivate.h"  /* Object headers                           */
#include "H5Rpkg.h"      /* References                               */
#include "H5Sprivate.h"  /* Dataspaces                               */
#include "H5Tprivate.h"  /* Datatypes                                */

#include "H5VLnative_private.h" /* Native VOL connector                     */

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

#define H5R_MAX_STRING_LEN (1 << 16) /* Max encoded string length    */

/* Encode macro */
#define H5R_ENCODE(func, val, buf, buf_size, actual, m)                                                      \
    do {                                                                                                     \
        size_t __nalloc = buf_size;                                                                          \
        if (func(val, buf, &__nalloc) < 0)                                                                   \
            HGOTO_ERROR(H5E_REFERENCE, H5E_CANTENCODE, FAIL, m)                                              \
        if (buf && buf_size >= __nalloc) {                                                                   \
            buf += __nalloc;                                                                                 \
            buf_size -= __nalloc;                                                                            \
        }                                                                                                    \
        actual += __nalloc;                                                                                  \
    } while (0)

#define H5R_ENCODE_VAR(func, var, size, buf, buf_size, actual, m)                                            \
    do {                                                                                                     \
        size_t __nalloc = buf_size;                                                                          \
        if (func(var, size, buf, &__nalloc) < 0)                                                             \
            HGOTO_ERROR(H5E_REFERENCE, H5E_CANTENCODE, FAIL, m)                                              \
        if (buf && buf_size >= __nalloc) {                                                                   \
            p += __nalloc;                                                                                   \
            buf_size -= __nalloc;                                                                            \
        }                                                                                                    \
        actual += __nalloc;                                                                                  \
    } while (0)

/* Decode macro */
#define H5R_DECODE(func, val, buf, buf_size, actual, m)                                                      \
    do {                                                                                                     \
        size_t __nbytes = buf_size;                                                                          \
        if (func(buf, &__nbytes, val) < 0)                                                                   \
            HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDECODE, FAIL, m)                                              \
        buf += __nbytes;                                                                                     \
        buf_size -= __nbytes;                                                                                \
        actual += __nbytes;                                                                                  \
    } while (0)

#define H5R_DECODE_VAR(func, var, size, buf, buf_size, actual, m)                                            \
    do {                                                                                                     \
        size_t __nbytes = buf_size;                                                                          \
        if (func(buf, &__nbytes, var, size) < 0)                                                             \
            HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDECODE, FAIL, m)                                              \
        p += __nbytes;                                                                                       \
        buf_size -= __nbytes;                                                                                \
        actual += __nbytes;                                                                                  \
    } while (0)

/* Debug */
#ifdef H5R_DEBUG
#define H5R_LOG_DEBUG(...)                                                                                   \
    do {                                                                                                     \
        HDfprintf(stdout, " # %s(): ", __func__);                                                            \
        HDfprintf(stdout, __VA_ARGS__);                                                                      \
        HDfprintf(stdout, "\n");                                                                             \
        HDfflush(stdout);                                                                                    \
    } while (0)
static const char *
H5R__print_token(const H5O_token_t token)
{
    static char string[64];

    /* Print the raw token. */
    HDsnprintf(string, 64, "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
               (unsigned char)token.__data[15], (unsigned char)token.__data[14],
               (unsigned char)token.__data[13], (unsigned char)token.__data[12],
               (unsigned char)token.__data[11], (unsigned char)token.__data[10],
               (unsigned char)token.__data[9], (unsigned char)token.__data[8], (unsigned char)token.__data[7],
               (unsigned char)token.__data[6], (unsigned char)token.__data[5], (unsigned char)token.__data[4],
               (unsigned char)token.__data[3], (unsigned char)token.__data[2], (unsigned char)token.__data[1],
               (unsigned char)token.__data[0]);

    return string;
}
#else
#define H5R_LOG_DEBUG(...)                                                                                   \
    do {                                                                                                     \
    } while (0)
#endif

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

/********************/
/* Local Prototypes */
/********************/

static herr_t H5R__encode_obj_token(const H5O_token_t *obj_token, size_t token_size, unsigned char *buf,
                                    size_t *nalloc);
static herr_t H5R__decode_obj_token(const unsigned char *buf, size_t *nbytes, H5O_token_t *obj_token,
                                    uint8_t *token_size);
static herr_t H5R__encode_region(H5S_t *space, unsigned char *buf, size_t *nalloc);
static herr_t H5R__decode_region(const unsigned char *buf, size_t *nbytes, H5S_t **space_ptr);
static herr_t H5R__encode_string(const char *string, unsigned char *buf, size_t *nalloc);
static herr_t H5R__decode_string(const unsigned char *buf, size_t *nbytes, char **string_ptr);

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

/* Package initialization variable */
hbool_t H5_PKG_INIT_VAR = FALSE;

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

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

/* Flag indicating "top" of interface has been initialized */
static hbool_t H5R_top_package_initialize_s = FALSE;

/*--------------------------------------------------------------------------
NAME
   H5R__init_package -- Initialize interface-specific information
USAGE
    herr_t H5R__init_package()

RETURNS
    Non-negative on success/Negative on failure
DESCRIPTION
    Initializes any interface-specific data or routines.

--------------------------------------------------------------------------*/
herr_t
H5R__init_package(void)
{
    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /* Mark "top" of interface as initialized */
    H5R_top_package_initialize_s = TRUE;

    /* Sanity check, if assert fails, H5R_REF_BUF_SIZE must be increased */
    HDcompile_assert(sizeof(H5R_ref_priv_t) <= H5R_REF_BUF_SIZE);

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

/*--------------------------------------------------------------------------
 NAME
    H5R_top_term_package
 PURPOSE
    Terminate various H5R objects
 USAGE
    void H5R_top_term_package()
 RETURNS
    void
 DESCRIPTION
    Release IDs for the atom group, deferring full interface shutdown
    until later (in H5R_term_package).
 GLOBAL VARIABLES
 COMMENTS, BUGS, ASSUMPTIONS
     Can't report errors...
 EXAMPLES
 REVISION LOG
--------------------------------------------------------------------------*/
int
H5R_top_term_package(void)
{
    int n = 0;

    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /* Mark closed if initialized */
    if (H5R_top_package_initialize_s)
        if (0 == n)
            H5R_top_package_initialize_s = FALSE;

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

/*--------------------------------------------------------------------------
 NAME
    H5R_term_package
 PURPOSE
    Terminate various H5R objects
 USAGE
    void H5R_term_package()
 RETURNS
    void
 DESCRIPTION
    Release the atom group and any other resources allocated.
 GLOBAL VARIABLES
 COMMENTS, BUGS, ASSUMPTIONS
     Can't report errors...

     Finishes shutting down the interface, after H5R_top_term_package()
     is called
 EXAMPLES
 REVISION LOG
--------------------------------------------------------------------------*/
int
H5R_term_package(void)
{
    int n = 0;

    FUNC_ENTER_NOAPI_NOINIT_NOERR

    if (H5_PKG_INIT_VAR) {
        /* Sanity checks */
        HDassert(FALSE == H5R_top_package_initialize_s);

        /* Mark closed */
        if (0 == n)
            H5_PKG_INIT_VAR = FALSE;
    }

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

/*-------------------------------------------------------------------------
 * Function:    H5R__create_object
 *
 * Purpose:     Creates an object reference
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__create_object(const H5O_token_t *obj_token, size_t token_size, H5R_ref_priv_t *ref)
{
    size_t encode_size;
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    HDassert(ref);

    /* Create new reference */
    ref->info.obj.filename = NULL;
    ref->loc_id            = H5I_INVALID_HID;
    ref->type              = (uint8_t)H5R_OBJECT2;
    if (H5R__set_obj_token(ref, obj_token, token_size) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTSET, FAIL, "unable to set object token")

    /* Cache encoding size (assume no external reference) */
    if (H5R__encode(NULL, ref, NULL, &encode_size, 0) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTENCODE, FAIL, "unable to determine encoding size")
    ref->encode_size = (uint32_t)encode_size;

    H5R_LOG_DEBUG("Created object reference, %d, filename=%s, obj_addr=%s, encode size=%u",
                  (int)sizeof(H5R_ref_priv_t), ref->info.obj.filename, H5R__print_token(ref->info.obj.token),
                  ref->encode_size);

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

/*-------------------------------------------------------------------------
 * Function:    H5R__create_region
 *
 * Purpose:     Creates a region reference
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__create_region(const H5O_token_t *obj_token, size_t token_size, H5S_t *space, H5R_ref_priv_t *ref)
{
    size_t encode_size;
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    HDassert(space);
    HDassert(ref);

    /* Create new reference */
    ref->info.obj.filename = NULL;
    if (NULL == (ref->info.reg.space = H5S_copy(space, FALSE, TRUE)))
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTCOPY, FAIL, "unable to copy dataspace")

    ref->loc_id = H5I_INVALID_HID;
    ref->type   = (uint8_t)H5R_DATASET_REGION2;
    if (H5R__set_obj_token(ref, obj_token, token_size) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTSET, FAIL, "unable to set object token")

    /* Cache encoding size (assume no external reference) */
    if (H5R__encode(NULL, ref, NULL, &encode_size, 0) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTENCODE, FAIL, "unable to determine encoding size")
    ref->encode_size = (uint32_t)encode_size;

    H5R_LOG_DEBUG("Created region reference, %d, filename=%s, obj_addr=%s, encode size=%u",
                  (int)sizeof(H5R_ref_priv_t), ref->info.obj.filename, H5R__print_token(ref->info.obj.token),
                  ref->encode_size);

done:
    if (ret_value < 0)
        if (ref->info.reg.space) {
            H5S_close(ref->info.reg.space);
            ref->info.reg.space = NULL;
        } /* end if */

    FUNC_LEAVE_NOAPI(ret_value)
} /* H5R__create_region */

/*-------------------------------------------------------------------------
 * Function:    H5R__create_attr
 *
 * Purpose:     Creates an attribute reference
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__create_attr(const H5O_token_t *obj_token, size_t token_size, const char *attr_name, H5R_ref_priv_t *ref)
{
    size_t encode_size;
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    HDassert(attr_name);
    HDassert(ref);

    /* Make sure that attribute name is not longer than supported encode size */
    if (HDstrlen(attr_name) > H5R_MAX_STRING_LEN)
        HGOTO_ERROR(H5E_REFERENCE, H5E_ARGS, FAIL, "attribute name too long (%d > %d)",
                    (int)HDstrlen(attr_name), H5R_MAX_STRING_LEN)

    /* Create new reference */
    ref->info.obj.filename = NULL;
    if (NULL == (ref->info.attr.name = HDstrdup(attr_name)))
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTCOPY, FAIL, "Cannot copy attribute name")

    ref->loc_id = H5I_INVALID_HID;
    ref->type   = (uint8_t)H5R_ATTR;
    if (H5R__set_obj_token(ref, obj_token, token_size) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTSET, FAIL, "unable to set object token")

    /* Cache encoding size (assume no external reference) */
    if (H5R__encode(NULL, ref, NULL, &encode_size, 0) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTENCODE, FAIL, "unable to determine encoding size")
    ref->encode_size = (uint32_t)encode_size;

    H5R_LOG_DEBUG("Created attribute reference, %d, filename=%s, obj_addr=%s, attr name=%s, encode size=%u",
                  (int)sizeof(H5R_ref_priv_t), ref->info.obj.filename, H5R__print_token(ref->info.obj.token),
                  ref->info.attr.name, ref->encode_size);

done:
    if (ret_value < 0) {
        H5MM_xfree(ref->info.attr.name);
        ref->info.attr.name = NULL;
    } /* end if */

    FUNC_LEAVE_NOAPI(ret_value)
} /* H5R__create_attr */

/*-------------------------------------------------------------------------
 * Function:    H5R__destroy
 *
 * Purpose:     Destroy a reference
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__destroy(H5R_ref_priv_t *ref)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    HDassert(ref != NULL);

    H5R_LOG_DEBUG("Destroying reference, filename=%s, obj_addr=%s, encode size=%u", ref->info.obj.filename,
                  H5R__print_token(ref->info.obj.token), ref->encode_size);

    H5MM_xfree(ref->info.obj.filename);
    ref->info.obj.filename = NULL;

    switch (ref->type) {
        case H5R_OBJECT2:
            break;

        case H5R_DATASET_REGION2:
            if (H5S_close(ref->info.reg.space) < 0)
                HGOTO_ERROR(H5E_REFERENCE, H5E_CANTFREE, FAIL, "Cannot close dataspace")
            ref->info.reg.space = NULL;
            break;

        case H5R_ATTR:
            H5MM_xfree(ref->info.attr.name);
            ref->info.attr.name = NULL;
            break;

        case H5R_OBJECT1:
        case H5R_DATASET_REGION1:
            break;
        case H5R_BADTYPE:
        case H5R_MAXTYPE:
            HDassert("invalid reference type" && 0);
            HGOTO_ERROR(H5E_REFERENCE, H5E_UNSUPPORTED, FAIL, "internal error (invalid reference type)")

        default:
            HDassert("unknown reference type" && 0);
            HGOTO_ERROR(H5E_REFERENCE, H5E_UNSUPPORTED, FAIL, "internal error (unknown reference type)")
    } /* end switch */

    /* Decrement refcount of attached loc_id */
    if (ref->type && (ref->loc_id != H5I_INVALID_HID)) {
        if (ref->app_ref) {
            if (H5I_dec_app_ref(ref->loc_id) < 0)
                HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDEC, FAIL, "decrementing location ID failed")
        }
        else {
            if (H5I_dec_ref(ref->loc_id) < 0)
                HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDEC, FAIL, "decrementing location ID failed")
        }
    }

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

/*-------------------------------------------------------------------------
 * Function:    H5R__set_loc_id
 *
 * Purpose:     Attach location ID to reference and increment location refcount.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__set_loc_id(H5R_ref_priv_t *ref, hid_t id, hbool_t inc_ref, hbool_t app_ref)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    HDassert(ref != NULL);
    HDassert(id != H5I_INVALID_HID);

    /* If a location ID was previously assigned, decrement refcount and
     * assign new one */
    if ((ref->loc_id != H5I_INVALID_HID)) {
        if (ref->app_ref) {
            if (H5I_dec_app_ref(ref->loc_id) < 0)
                HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDEC, FAIL, "decrementing location ID failed")
        }
        else {
            if (H5I_dec_ref(ref->loc_id) < 0)
                HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDEC, FAIL, "decrementing location ID failed")
        }
    }
    ref->loc_id = id;

    /* Prevent location ID from being freed until reference is destroyed,
     * set app_ref if necessary as references are exposed to users and are
     * expected to be destroyed, this allows the loc_id to be cleanly released
     * on shutdown if users fail to call H5Rdestroy(). */
    if (inc_ref && H5I_inc_ref(ref->loc_id, app_ref) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTINC, FAIL, "incrementing location ID failed")
    ref->app_ref = app_ref;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__get_loc_id
 *
 * Purpose:     Retrieve location ID attached to existing reference.
 *
 * Return:      Valid ID on success / H5I_INVALID_HID on failure
 *
 *-------------------------------------------------------------------------
 */
hid_t
H5R__get_loc_id(const H5R_ref_priv_t *ref)
{
    hid_t ret_value = H5I_INVALID_HID; /* Return value */

    FUNC_ENTER_PACKAGE_NOERR

    HDassert(ref != NULL);

    ret_value = ref->loc_id;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__reopen_file
 *
 * Purpose:     Re-open referenced file using file access property list.
 *
 * Return:      Valid ID on success / H5I_INVALID_HID on failure
 *
 *-------------------------------------------------------------------------
 */
hid_t
H5R__reopen_file(H5R_ref_priv_t *ref, hid_t fapl_id)
{
    H5P_genplist_t *      plist;           /* Property list for FAPL */
    void *                new_file = NULL; /* File object opened */
    H5VL_connector_prop_t connector_prop;  /* Property for VOL connector ID & info     */
    H5VL_object_t *       vol_obj = NULL;  /* VOL object for file */
    hbool_t               supported;       /* Whether 'post open' operation is supported by VOL connector */
    hid_t                 ret_value = H5I_INVALID_HID;

    FUNC_ENTER_PACKAGE

    /* TODO add search path */

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&fapl_id, H5P_CLS_FACC, H5I_INVALID_HID, TRUE) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTSET, H5I_INVALID_HID, "can't set access property list info")

    /* Get the VOL info from the fapl */
    if (NULL == (plist = (H5P_genplist_t *)H5I_object(fapl_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, H5I_INVALID_HID, "not a file access property list")
    if (H5P_peek(plist, H5F_ACS_VOL_CONN_NAME, &connector_prop) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTGET, H5I_INVALID_HID, "can't get VOL connector info")

    /* Stash a copy of the "top-level" connector property, before any pass-through
     *  connectors modify or unwrap it.
     */
    if (H5CX_set_vol_connector_prop(&connector_prop) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTSET, H5I_INVALID_HID,
                    "can't set VOL connector info in API context")

    /* Open the file */
    /* (Must open file read-write to allow for object modifications) */
    if (NULL == (new_file = H5VL_file_open(&connector_prop, H5R_REF_FILENAME(ref), H5F_ACC_RDWR, fapl_id,
                                           H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL)))
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTOPENFILE, H5I_INVALID_HID, "unable to open file")

    /* Get an ID for the file */
    if ((ret_value = H5VL_register_using_vol_id(H5I_FILE, new_file, connector_prop.connector_id, TRUE)) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTREGISTER, H5I_INVALID_HID, "unable to atomize file handle")

    /* Get the file object */
    if (NULL == (vol_obj = H5VL_vol_object(ret_value)))
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTGET, H5I_INVALID_HID, "invalid object identifier")

    /* Make the 'post open' callback */
    supported = FALSE;
    if (H5VL_introspect_opt_query(vol_obj, H5VL_SUBCLS_FILE, H5VL_NATIVE_FILE_POST_OPEN, &supported) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTGET, H5I_INVALID_HID, "can't check for 'post open' operation")
    if (supported)
        if (H5VL_file_optional(vol_obj, H5VL_NATIVE_FILE_POST_OPEN, H5P_DATASET_XFER_DEFAULT,
                               H5_REQUEST_NULL) < 0)
            HGOTO_ERROR(H5E_REFERENCE, H5E_CANTINIT, H5I_INVALID_HID,
                        "unable to make file 'post open' callback")

    /* Attach loc_id to reference */
    if (H5R__set_loc_id((H5R_ref_priv_t *)ref, ret_value, FALSE, TRUE) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTSET, H5I_INVALID_HID, "unable to attach location id to reference")

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

/*-------------------------------------------------------------------------
 * Function:    H5R__get_type
 *
 * Purpose:     Given a reference to some object, return the type of that
 *              reference.
 *
 * Return:      Type of the reference
 *
 *-------------------------------------------------------------------------
 */
H5R_type_t
H5R__get_type(const H5R_ref_priv_t *ref)
{
    H5R_type_t ret_value = H5R_BADTYPE;

    FUNC_ENTER_PACKAGE_NOERR

    HDassert(ref != NULL);
    ret_value = (H5R_type_t)ref->type;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__equal
 *
 * Purpose:     Compare two references
 *
 * Return:      TRUE if equal, FALSE if unequal, FAIL if error
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5R__equal(const H5R_ref_priv_t *ref1, const H5R_ref_priv_t *ref2)
{
    htri_t ret_value = TRUE;

    FUNC_ENTER_PACKAGE

    HDassert(ref1 != NULL);
    HDassert(ref2 != NULL);

    /* Compare reference types */
    if (ref1->type != ref2->type)
        HGOTO_DONE(FALSE);

    /* Compare object addresses */
    if (ref1->token_size != ref2->token_size)
        HGOTO_DONE(FALSE);
    if (0 != HDmemcmp(&ref1->info.obj.token, &ref2->info.obj.token, ref1->token_size))
        HGOTO_DONE(FALSE);

    /* Compare filenames */
    if ((ref1->info.obj.filename && (NULL == ref2->info.obj.filename)) ||
        ((NULL == ref1->info.obj.filename) && ref2->info.obj.filename))
        HGOTO_DONE(FALSE);
    if (ref1->info.obj.filename && ref1->info.obj.filename &&
        (0 != HDstrcmp(ref1->info.obj.filename, ref2->info.obj.filename)))
        HGOTO_DONE(FALSE);

    switch (ref1->type) {
        case H5R_OBJECT2:
            break;
        case H5R_DATASET_REGION2:
            if ((ret_value = H5S_extent_equal(ref1->info.reg.space, ref2->info.reg.space)) < 0)
                HGOTO_ERROR(H5E_REFERENCE, H5E_CANTCOMPARE, FAIL, "cannot compare dataspace extents")
            break;
        case H5R_ATTR:
            HDassert(ref1->info.attr.name && ref2->info.attr.name);
            if (0 != HDstrcmp(ref1->info.attr.name, ref2->info.attr.name))
                HGOTO_DONE(FALSE);
            break;
        case H5R_OBJECT1:
        case H5R_DATASET_REGION1:
        case H5R_BADTYPE:
        case H5R_MAXTYPE:
            HDassert("invalid reference type" && 0);
            HGOTO_ERROR(H5E_REFERENCE, H5E_UNSUPPORTED, FAIL, "internal error (invalid reference type)")
        default:
            HDassert("unknown reference type" && 0);
            HGOTO_ERROR(H5E_REFERENCE, H5E_UNSUPPORTED, FAIL, "internal error (unknown reference type)")
    } /* end switch */

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

/*-------------------------------------------------------------------------
 * Function:    H5R__copy
 *
 * Purpose:     Copy a reference
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__copy(const H5R_ref_priv_t *src_ref, H5R_ref_priv_t *dst_ref)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    HDassert((src_ref != NULL) && (dst_ref != NULL));

    H5MM_memcpy(&dst_ref->info.obj.token, &src_ref->info.obj.token, sizeof(H5O_token_t));
    dst_ref->encode_size = src_ref->encode_size;
    dst_ref->type        = src_ref->type;
    dst_ref->token_size  = src_ref->token_size;

    switch (src_ref->type) {
        case H5R_OBJECT2:
            break;
        case H5R_DATASET_REGION2:
            if (NULL == (dst_ref->info.reg.space = H5S_copy(src_ref->info.reg.space, FALSE, TRUE)))
                HGOTO_ERROR(H5E_REFERENCE, H5E_CANTCOPY, FAIL, "unable to copy dataspace")
            break;
        case H5R_ATTR:
            if (NULL == (dst_ref->info.attr.name = HDstrdup(src_ref->info.attr.name)))
                HGOTO_ERROR(H5E_REFERENCE, H5E_CANTCOPY, FAIL, "Cannot copy attribute name")
            break;
        case H5R_OBJECT1:
        case H5R_DATASET_REGION1:
            HDassert("invalid reference type" && 0);
            HGOTO_ERROR(H5E_REFERENCE, H5E_UNSUPPORTED, FAIL, "internal error (invalid reference type)")
        case H5R_BADTYPE:
        case H5R_MAXTYPE:
        default:
            HDassert("unknown reference type" && 0);
            HGOTO_ERROR(H5E_REFERENCE, H5E_UNSUPPORTED, FAIL, "internal error (unknown reference type)")
    } /* end switch */

    /* We only need to keep a copy of the filename if we don't have the loc_id */
    if (src_ref->loc_id == H5I_INVALID_HID) {
        HDassert(src_ref->info.obj.filename);

        if (NULL == (dst_ref->info.obj.filename = HDstrdup(src_ref->info.obj.filename)))
            HGOTO_ERROR(H5E_REFERENCE, H5E_CANTCOPY, FAIL, "Cannot copy filename")
        dst_ref->loc_id = H5I_INVALID_HID;
    }
    else {
        dst_ref->info.obj.filename = NULL;

        /* Set location ID and hold reference to it */
        dst_ref->loc_id = src_ref->loc_id;
        if (H5I_inc_ref(dst_ref->loc_id, TRUE) < 0)
            HGOTO_ERROR(H5E_REFERENCE, H5E_CANTINC, FAIL, "incrementing location ID failed")
        dst_ref->app_ref = TRUE;
    }

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

/*-------------------------------------------------------------------------
 * Function:    H5R__get_obj_token
 *
 * Purpose:     Given a reference to some object, get the encoded token.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__get_obj_token(const H5R_ref_priv_t *ref, H5O_token_t *obj_token, size_t *token_size)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    HDassert(ref != NULL);
    HDassert(ref->token_size <= H5O_MAX_TOKEN_SIZE);

    if (obj_token) {
        if (0 == ref->token_size)
            HGOTO_ERROR(H5E_REFERENCE, H5E_CANTCOPY, FAIL, "NULL token size")
        H5MM_memcpy(obj_token, &ref->info.obj.token, sizeof(H5O_token_t));
    }
    if (token_size)
        *token_size = ref->token_size;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__set_obj_token
 *
 * Purpose:     Given a reference to some object, set the encoded token.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__set_obj_token(H5R_ref_priv_t *ref, const H5O_token_t *obj_token, size_t token_size)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE_NOERR

    HDassert(ref != NULL);
    HDassert(obj_token);
    HDassert(token_size);
    HDassert(token_size <= H5O_MAX_TOKEN_SIZE);

    H5MM_memcpy(&ref->info.obj.token, obj_token, token_size);
    HDassert(token_size <= 255);
    ref->token_size = (uint8_t)token_size;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__get_region
 *
 * Purpose:     Given a reference to some object, creates a copy of the
 *              dataset pointed to's dataspace and defines a selection in
 *              the copy which is the region pointed to.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__get_region(const H5R_ref_priv_t *ref, H5S_t *space)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    HDassert(ref != NULL);
    HDassert(ref->type == H5R_DATASET_REGION2);
    HDassert(space);

    /* Copy reference selection to destination */
    if (H5S_select_copy(space, ref->info.reg.space, FALSE) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTCOPY, FAIL, "unable to copy selection")

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

/*-------------------------------------------------------------------------
 * Function:    H5R__get_file_name
 *
 * Purpose:     Given a reference to some object, determine a file name of
 *              the object located into.
 *
 * Return:      Non-negative length of the path on success / -1 on failure
 *
 *-------------------------------------------------------------------------
 */
ssize_t
H5R__get_file_name(const H5R_ref_priv_t *ref, char *buf, size_t size)
{
    size_t  copy_len;
    ssize_t ret_value = -1; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Check args */
    HDassert(ref != NULL);

    /* Return if that reference has no filename set */
    if (!ref->info.obj.filename)
        HGOTO_ERROR(H5E_REFERENCE, H5E_ARGS, (-1), "no filename available for that reference")

    /* Get the file name length */
    copy_len = HDstrlen(ref->info.obj.filename);
    HDassert(copy_len <= H5R_MAX_STRING_LEN);

    /* Copy the file name */
    if (buf) {
        copy_len = MIN(copy_len, size - 1);
        H5MM_memcpy(buf, ref->info.obj.filename, copy_len);
        buf[copy_len] = '\0';
    }
    ret_value = (ssize_t)(copy_len + 1);

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

/*-------------------------------------------------------------------------
 * Function:    H5R__get_attr_name
 *
 * Purpose:     Given a reference to some attribute, determine its name.
 *
 * Return:      Non-negative length of the path on success / -1 on failure
 *
 *-------------------------------------------------------------------------
 */
ssize_t
H5R__get_attr_name(const H5R_ref_priv_t *ref, char *buf, size_t size)
{
    ssize_t ret_value = -1; /* Return value */
    size_t  attr_name_len;  /* Length of the attribute name */

    FUNC_ENTER_PACKAGE_NOERR

    /* Check args */
    HDassert(ref != NULL);
    HDassert(ref->type == H5R_ATTR);

    /* Get the attribute name length */
    attr_name_len = HDstrlen(ref->info.attr.name);
    HDassert(attr_name_len <= H5R_MAX_STRING_LEN);

    /* Get the attribute name */
    if (buf) {
        size_t copy_len = MIN(attr_name_len, size - 1);
        H5MM_memcpy(buf, ref->info.attr.name, copy_len);
        buf[copy_len] = '\0';
    }

    ret_value = (ssize_t)(attr_name_len + 1);

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

/*-------------------------------------------------------------------------
 * Function:    H5R__encode
 *
 * Purpose:     Private function for H5Rencode
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__encode(const char *filename, const H5R_ref_priv_t *ref, unsigned char *buf, size_t *nalloc,
            unsigned flags)
{
    uint8_t *p        = (uint8_t *)buf;
    size_t   buf_size = 0, encode_size = 0;
    herr_t   ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    HDassert(ref);
    HDassert(nalloc);

    /**
     * Encoding format:
     * | Reference type (8 bits) | Flags (8 bits) | Token (token size)
     *    |                         |
     *    |                         |----> H5R_IS_EXTERNAL: File info
     *    |
     *    |----> H5R_DATASET_REGION2: Serialized selection
     *    |
     *    |----> H5R_ATTR: Attribute name len + name
     *
     */

    /* Don't encode if buffer size isn't big enough or buffer is empty */
    if (buf && *nalloc >= H5R_ENCODE_HEADER_SIZE) {
        /* Encode the type of the reference */
        *p++ = (uint8_t)ref->type;

        /* Encode the flags */
        *p++ = (uint8_t)flags;

        buf_size = *nalloc - H5R_ENCODE_HEADER_SIZE;
    } /* end if */
    encode_size += H5R_ENCODE_HEADER_SIZE;

    /* Encode object token */
    H5R_ENCODE_VAR(H5R__encode_obj_token, &ref->info.obj.token, ref->token_size, p, buf_size, encode_size,
                   "Cannot encode object address");

    /**
     * TODO Encode VOL info
     * When we have a better way of storing blobs, we should add
     * support for referencing files in external VOLs.
     * There are currently multiple limitations:
     *   - avoid duplicating VOL info on each reference
     *   - must query terminal VOL connector to avoid passthrough confusion
     */
    if (flags & H5R_IS_EXTERNAL)
        /* Encode file name */
        H5R_ENCODE(H5R__encode_string, filename, p, buf_size, encode_size, "Cannot encode filename");

    switch (ref->type) {
        case H5R_OBJECT2:
            break;

        case H5R_DATASET_REGION2:
            /* Encode dataspace */
            H5R_ENCODE(H5R__encode_region, ref->info.reg.space, p, buf_size, encode_size,
                       "Cannot encode region");
            break;

        case H5R_ATTR:
            /* Encode attribute name */
            H5R_ENCODE(H5R__encode_string, ref->info.attr.name, p, buf_size, encode_size,
                       "Cannot encode attribute name");
            break;

        case H5R_OBJECT1:
        case H5R_DATASET_REGION1:
        case H5R_BADTYPE:
        case H5R_MAXTYPE:
            HDassert("invalid reference type" && 0);
            HGOTO_ERROR(H5E_REFERENCE, H5E_UNSUPPORTED, FAIL, "internal error (invalid reference type)")

        default:
            HDassert("unknown reference type" && 0);
            HGOTO_ERROR(H5E_REFERENCE, H5E_UNSUPPORTED, FAIL, "internal error (unknown reference type)")
    } /* end switch */

    H5R_LOG_DEBUG("Encoded reference, filename=%s, obj_addr=%s, encode size=%u", ref->info.obj.filename,
                  H5R__print_token(ref->info.obj.token), encode_size);

    *nalloc = encode_size;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__decode
 *
 * Purpose:     Private function for H5Rdecode
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__decode(const unsigned char *buf, size_t *nbytes, H5R_ref_priv_t *ref)
{
    const uint8_t *p        = (const uint8_t *)buf;
    size_t         buf_size = 0, decode_size = 0;
    uint8_t        flags;
    herr_t         ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    /* Sanity checks */
    HDassert(buf);
    HDassert(nbytes);
    HDassert(ref);

    /* Don't decode if buffer size isn't big enough */
    buf_size = *nbytes;
    if (buf_size < H5R_ENCODE_HEADER_SIZE)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDECODE, FAIL, "Buffer size is too small")

    /* Set new reference */
    ref->type = (int8_t)*p++;
    if (ref->type <= H5R_BADTYPE || ref->type >= H5R_MAXTYPE)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid reference type")

    /* Set flags */
    flags = *p++;

    buf_size -= H5R_ENCODE_HEADER_SIZE;
    decode_size += H5R_ENCODE_HEADER_SIZE;

    /* Decode object token */
    H5R_DECODE_VAR(H5R__decode_obj_token, &ref->info.obj.token, &ref->token_size, p, buf_size, decode_size,
                   "Cannot decode object address");

    /* We do not need to store the filename if the reference is internal */
    if (flags & H5R_IS_EXTERNAL) {
        /* Decode file name */
        H5R_DECODE(H5R__decode_string, &ref->info.obj.filename, p, buf_size, decode_size,
                   "Cannot decode filename");
    }
    else
        ref->info.obj.filename = NULL;

    switch (ref->type) {
        case H5R_OBJECT2:
            break;
        case H5R_DATASET_REGION2:
            /* Decode dataspace */
            H5R_DECODE(H5R__decode_region, &ref->info.reg.space, p, buf_size, decode_size,
                       "Cannot decode region");
            break;
        case H5R_ATTR:
            /* Decode attribute name */
            H5R_DECODE(H5R__decode_string, &ref->info.attr.name, p, buf_size, decode_size,
                       "Cannot decode attribute name");
            break;
        case H5R_OBJECT1:
        case H5R_DATASET_REGION1:
        case H5R_BADTYPE:
        case H5R_MAXTYPE:
            HDassert("invalid reference type" && 0);
            HGOTO_ERROR(H5E_REFERENCE, H5E_UNSUPPORTED, FAIL, "internal error (invalid reference type)")
        default:
            HDassert("unknown reference type" && 0);
            HGOTO_ERROR(H5E_REFERENCE, H5E_UNSUPPORTED, FAIL, "internal error (unknown reference type)")
    } /* end switch */

    /* Set loc ID to invalid */
    ref->loc_id = H5I_INVALID_HID;

    /* Set encoding size */
    ref->encode_size = (uint32_t)decode_size;

    H5R_LOG_DEBUG("Decoded reference, filename=%s, obj_addr=%s, encode size=%u", ref->info.obj.filename,
                  H5R__print_token(ref->info.obj.token), ref->encode_size);

    /* Set output info */
    *nbytes = decode_size;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__encode_obj_token
 *
 * Purpose:     Encode an object address.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5R__encode_obj_token(const H5O_token_t *obj_token, size_t token_size, unsigned char *buf, size_t *nalloc)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_STATIC_NOERR

    HDassert(nalloc);

    /* Don't encode if buffer size isn't big enough or buffer is empty */
    if (buf && *nalloc >= token_size) {
        uint8_t *p = (uint8_t *)buf;

        /* Encode token size */
        *p++ = (uint8_t)(token_size & 0xff);

        /* Encode token */
        H5MM_memcpy(p, obj_token, token_size);
    }
    *nalloc = token_size + H5_SIZEOF_UINT8_T;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__decode_obj_token
 *
 * Purpose:     Decode an object address.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5R__decode_obj_token(const unsigned char *buf, size_t *nbytes, H5O_token_t *obj_token, uint8_t *token_size)
{
    const uint8_t *p         = (const uint8_t *)buf;
    herr_t         ret_value = SUCCEED;

    FUNC_ENTER_STATIC

    HDassert(buf);
    HDassert(nbytes);
    HDassert(obj_token);
    HDassert(token_size);

    /* Don't decode if buffer size isn't big enough */
    if (*nbytes < H5_SIZEOF_UINT8_T)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDECODE, FAIL, "Buffer size is too small")

    /* Get token size */
    *token_size = *p++;
    if (*token_size > sizeof(H5O_token_t))
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDECODE, FAIL, "Invalid token size (%u)", *token_size)

    /* Make sure that token is initialized */
    HDmemset(obj_token, 0, sizeof(H5O_token_t));

    /* Decode token */
    H5MM_memcpy(obj_token, p, *token_size);

    *nbytes = (size_t)(*token_size + H5_SIZEOF_UINT8_T);

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

/*-------------------------------------------------------------------------
 * Function:    H5R__encode_region
 *
 * Purpose:     Encode a selection.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5R__encode_region(H5S_t *space, unsigned char *buf, size_t *nalloc)
{
    uint8_t *p         = NULL; /* Pointer to data to store */
    hssize_t buf_size  = 0;
    herr_t   ret_value = SUCCEED;

    FUNC_ENTER_STATIC

    HDassert(space);
    HDassert(nalloc);

    /* Get the amount of space required to serialize the selection */
    if ((buf_size = H5S_SELECT_SERIAL_SIZE(space)) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTENCODE, FAIL,
                    "Cannot determine amount of space needed for serializing selection")

    /* Don't encode if buffer size isn't big enough or buffer is empty */
    if (buf && *nalloc >= ((size_t)buf_size + 2 * H5_SIZEOF_UINT32_T)) {
        int rank;
        p = (uint8_t *)buf;

        /* Encode the size for safety check */
        UINT32ENCODE(p, (uint32_t)buf_size);

        /* Encode the extent rank */
        if ((rank = H5S_get_simple_extent_ndims(space)) < 0)
            HGOTO_ERROR(H5E_REFERENCE, H5E_CANTGET, FAIL, "can't get extent rank for selection")
        UINT32ENCODE(p, (uint32_t)rank);

        /* Serialize the selection */
        if (H5S_SELECT_SERIALIZE(space, (unsigned char **)&p) < 0)
            HGOTO_ERROR(H5E_REFERENCE, H5E_CANTENCODE, FAIL, "can't serialize selection")
    } /* end if */
    *nalloc = (size_t)buf_size + 2 * H5_SIZEOF_UINT32_T;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__decode_region
 *
 * Purpose:     Decode a selection.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5R__decode_region(const unsigned char *buf, size_t *nbytes, H5S_t **space_ptr)
{
    const uint8_t *p        = (const uint8_t *)buf;
    size_t         buf_size = 0;
    unsigned       rank;
    H5S_t *        space;
    herr_t         ret_value = SUCCEED;

    FUNC_ENTER_STATIC

    HDassert(buf);
    HDassert(nbytes);
    HDassert(space_ptr);

    /* Don't decode if buffer size isn't big enough */
    if (*nbytes < (2 * H5_SIZEOF_UINT32_T))
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDECODE, FAIL, "Buffer size is too small")

    /* Decode the selection size */
    UINT32DECODE(p, buf_size);
    buf_size += H5_SIZEOF_UINT32_T;

    /* Decode the extent rank */
    UINT32DECODE(p, rank);
    buf_size += H5_SIZEOF_UINT32_T;

    /* Don't decode if buffer size isn't big enough */
    if (*nbytes < buf_size)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDECODE, FAIL, "Buffer size is too small")

    /* Deserialize the selection (dataspaces need the extent rank information) */
    if (NULL == (space = H5S_create(H5S_SIMPLE)))
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDECODE, FAIL, "Buffer size is too small")
    if (H5S_set_extent_simple(space, rank, NULL, NULL) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTSET, FAIL, "can't set extent rank for selection")
    if (H5S_SELECT_DESERIALIZE(&space, &p) < 0)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDECODE, FAIL, "can't deserialize selection")

    *nbytes    = buf_size;
    *space_ptr = space;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__encode_string
 *
 * Purpose:     Encode a string.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5R__encode_string(const char *string, unsigned char *buf, size_t *nalloc)
{
    size_t string_len, buf_size;
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_STATIC

    HDassert(string);
    HDassert(nalloc);

    /* Get the amount of space required to serialize the string */
    string_len = HDstrlen(string);
    if (string_len > H5R_MAX_STRING_LEN)
        HGOTO_ERROR(H5E_REFERENCE, H5E_ARGS, FAIL, "string too long")

    /* Compute buffer size, allow for the attribute name length and object address */
    buf_size = string_len + sizeof(uint16_t);

    if (buf && *nalloc >= buf_size) {
        uint8_t *p = (uint8_t *)buf;
        /* Serialize information for string length into the buffer */
        UINT16ENCODE(p, string_len);
        /* Copy the string into the buffer */
        H5MM_memcpy(p, string, string_len);
    }
    *nalloc = buf_size;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__decode_string
 *
 * Purpose:     Decode a string.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5R__decode_string(const unsigned char *buf, size_t *nbytes, char **string_ptr)
{
    const uint8_t *p = (const uint8_t *)buf;
    size_t         string_len;
    char *         string    = NULL;
    herr_t         ret_value = SUCCEED;

    FUNC_ENTER_STATIC

    HDassert(buf);
    HDassert(nbytes);
    HDassert(string_ptr);

    /* Don't decode if buffer size isn't big enough */
    if (*nbytes < sizeof(uint16_t))
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDECODE, FAIL, "Buffer size is too small")

    /* Get the string length */
    UINT16DECODE(p, string_len);
    HDassert(string_len <= H5R_MAX_STRING_LEN);

    /* Allocate the string */
    if (NULL == (string = (char *)H5MM_malloc(string_len + 1)))
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTALLOC, FAIL, "Cannot allocate string")

    /* Copy the string */
    H5MM_memcpy(string, p, string_len);
    string[string_len] = '\0';

    *string_ptr = string;
    *nbytes     = sizeof(uint16_t) + string_len;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__encode_heap
 *
 * Purpose:     Encode data and insert into heap (native only).
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__encode_heap(H5F_t *f, unsigned char *buf, size_t *nalloc, const unsigned char *data, size_t data_size)
{
    size_t buf_size;
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    HDassert(f);
    HDassert(nalloc);

    buf_size = H5HG_HEAP_ID_SIZE(f);
    if (buf && *nalloc >= buf_size) {
        H5HG_t   hobjid;
        uint8_t *p = (uint8_t *)buf;

        /* Write the reference information to disk (allocates space also) */
        if (H5HG_insert(f, data_size, data, &hobjid) < 0)
            HGOTO_ERROR(H5E_REFERENCE, H5E_WRITEERROR, FAIL, "Unable to write reference information")

        /* Encode the heap information */
        H5F_addr_encode(f, &p, hobjid.addr);
        UINT32ENCODE(p, hobjid.idx);
    } /* end if */
    *nalloc = buf_size;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__decode_heap
 *
 * Purpose:     Decode data inserted into heap (native only).
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__decode_heap(H5F_t *f, const unsigned char *buf, size_t *nbytes, unsigned char **data_ptr,
                 size_t *data_size)
{
    const uint8_t *p = (const uint8_t *)buf;
    H5HG_t         hobjid;
    size_t         buf_size;
    herr_t         ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    HDassert(f);
    HDassert(buf);
    HDassert(nbytes);
    HDassert(data_ptr);

    buf_size = H5HG_HEAP_ID_SIZE(f);
    /* Don't decode if buffer size isn't big enough */
    if (*nbytes < buf_size)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDECODE, FAIL, "Buffer size is too small")

    /* Get the heap information */
    H5F_addr_decode(f, &p, &(hobjid.addr));
    if (!H5F_addr_defined(hobjid.addr) || hobjid.addr == 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Undefined reference pointer")
    UINT32DECODE(p, hobjid.idx);

    /* Read the information from disk */
    if (NULL == (*data_ptr = (unsigned char *)H5HG_read(f, &hobjid, (void *)*data_ptr, data_size)))
        HGOTO_ERROR(H5E_REFERENCE, H5E_READERROR, FAIL, "Unable to read reference data")

    *nbytes = buf_size;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__encode_token_obj_compat
 *
 * Purpose:     Encode an object token. (native only)
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__encode_token_obj_compat(const H5O_token_t *obj_token, size_t token_size, unsigned char *buf,
                             size_t *nalloc)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE_NOERR

    HDassert(obj_token);
    HDassert(token_size);
    HDassert(nalloc);

    /* Don't encode if buffer size isn't big enough or buffer is empty */
    if (buf && *nalloc >= token_size)
        H5MM_memcpy(buf, obj_token, token_size);

    *nalloc = token_size;

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

/*-------------------------------------------------------------------------
 * Function:    H5R__decode_token_obj_compat
 *
 * Purpose:     Decode an object token. (native only)
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__decode_token_obj_compat(const unsigned char *buf, size_t *nbytes, H5O_token_t *obj_token,
                             size_t token_size)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    HDassert(buf);
    HDassert(nbytes);
    HDassert(obj_token);
    HDassert(token_size);

    /* Don't decode if buffer size isn't big enough */
    if (*nbytes < token_size)
        HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDECODE, FAIL, "Buffer size is too small")

    H5MM_memcpy(obj_token, buf, token_size);

    *nbytes = token_size;

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* H5R__decode_token_obj_compat() */

/*-------------------------------------------------------------------------
 * Function:    H5R__decode_token_region_compat
 *
 * Purpose:     Decode dataset selection from data inserted into heap
 *              (native only).
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5R__decode_token_region_compat(H5F_t *f, const unsigned char *buf, size_t *nbytes, H5O_token_t *obj_token,
                                size_t token_size, H5S_t **space_ptr)
{
    unsigned char *data  = NULL;
    H5O_token_t    token = {0};
    size_t         data_size;
    const uint8_t *p;
    H5S_t *        space     = NULL;
    herr_t         ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    HDassert(f);
    HDassert(buf);
    HDassert(nbytes);
    HDassert(token_size);

    /* Read from heap */
    if (H5R__decode_heap(f, buf, nbytes, &data, &data_size) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier")

    /* Get object address */
    p = (const uint8_t *)data;
    H5MM_memcpy(&token, p, token_size);
    p += token_size;

    if (space_ptr) {
        H5O_loc_t oloc; /* Object location */

        /* Initialize the object location */
        H5O_loc_reset(&oloc);
        oloc.file = f;

        if (H5VL_native_token_to_addr(f, H5I_FILE, token, &oloc.addr) < 0)
            HGOTO_ERROR(H5E_REFERENCE, H5E_CANTUNSERIALIZE, FAIL,
                        "can't deserialize object token into address")

        /* Open and copy the dataset's dataspace */
        if (NULL == (space = H5S_read(&oloc)))
            HGOTO_ERROR(H5E_REFERENCE, H5E_NOTFOUND, FAIL, "not found")

        /* Unserialize the selection */
        if (H5S_SELECT_DESERIALIZE(&space, &p) < 0)
            HGOTO_ERROR(H5E_REFERENCE, H5E_CANTDECODE, FAIL, "can't deserialize selection")

        *space_ptr = space;
    }
    if (obj_token)
        H5MM_memcpy(obj_token, &token, sizeof(H5O_token_t));

done:
    H5MM_free(data);

    if (ret_value < 0) {
        if (space && H5S_close(space) < 0)
            HDONE_ERROR(H5E_DATASET, H5E_CLOSEERROR, FAIL, "unable to release dataspace")
    }

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