/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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://support.hdfgroup.org/ftp/HDF5/releases.  *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*
 * FILE:    H5I.c - Internal storage routines for handling "IDs"
 *
 * REMARKS: IDs which allow objects (void * currently) to be bundled
 *          into "types" for more general storage.
 *
 * DESIGN:  The types are stored in an array of pointers to store each
 *          type in an element. Each "type" node contains a link to a
 *          hash table to manage the IDs in each type.  Allowed types are
 *          values within the range 1 to H5I_MAX_NUM_TYPES and are given out
 *          at run-time.  Types used by the library are stored in global
 *          variables defined in H5Ipublic.h.
 */

#include "H5Imodule.h" /* This source code file is part of the H5I module */
#define H5T_FRIEND     /* Suppress error about including H5Tpkg */

#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 "H5Fprivate.h"  /* File access                              */
#include "H5FLprivate.h" /* Free Lists                               */
#include "H5Gprivate.h"  /* Groups                                   */
#include "H5Ipkg.h"      /* IDs                                      */
#include "H5MMprivate.h" /* Memory management                        */
#include "H5Oprivate.h"  /* Object headers                           */
#include "H5SLprivate.h" /* Skip Lists                               */
#include "H5Tpkg.h"      /* Datatypes                                */
#include "H5VLprivate.h" /* Virtual Object Layer                     */

/* Local Macros */

/* Combine a Type number and an atom index into an atom */
#define H5I_MAKE(g, i) ((((hid_t)(g)&TYPE_MASK) << ID_BITS) | ((hid_t)(i)&ID_MASK))

/* Local typedefs */

/* Atom information structure used */
typedef struct H5I_id_info_t {
    hid_t       id;        /* ID for this info                */
    unsigned    count;     /* ref. count for this atom            */
    unsigned    app_count; /* ref. count of application visible atoms  */
    const void *obj_ptr;   /* pointer associated with the atom        */
} H5I_id_info_t;

/* ID type structure used */
typedef struct {
    const H5I_class_t *cls;        /* Pointer to ID class                      */
    unsigned           init_count; /* # of times this type has been initialized*/
    uint64_t           id_count;   /* Current number of IDs held            */
    uint64_t           nextid;     /* ID to use for the next atom            */
    H5I_id_info_t *    last_info;  /* Info for most recent ID looked up        */
    H5SL_t *           ids;        /* Pointer to skip list that stores IDs     */
} H5I_id_type_t;

typedef struct {
    H5I_search_func_t app_cb;  /* Application's callback routine */
    void *            app_key; /* Application's "key" (user data) */
    void *            ret_obj; /* Object to return */
} H5I_search_ud_t;

typedef struct {
    H5I_iterate_func_t op;      /* Application's callback routine */
    void *             op_data; /* Application's user data */
} H5I_iterate_pub_ud_t;

/* User data for iterator callback for retrieving an ID corresponding to an object pointer */
typedef struct {
    const void *object;   /* object pointer to search for */
    H5I_type_t  obj_type; /* type of object we are searching for */
    hid_t       ret_id;   /* ID returned */
} H5I_get_id_ud_t;

/* User data for iterator callback for ID iteration */
typedef struct {
    H5I_search_func_t user_func;  /* 'User' function to invoke */
    void *            user_udata; /* User data to pass to 'user' function */
    hbool_t           app_ref;    /* Whether this is an appl. ref. call */
    H5I_type_t        obj_type;   /* Type of object we are iterating over */
} H5I_iterate_ud_t;

/* User data for H5I__clear_type_cb */
typedef struct {
    H5I_id_type_t *type_ptr; /* Pointer to the type being cleard */
    hbool_t        force;    /* Whether to always remove the id */
    hbool_t        app_ref;  /* Whether this is an appl. ref. call */
} H5I_clear_type_ud_t;

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

/*-------------------- Locally scoped variables -----------------------------*/

/* Array of pointers to atomic types */
static H5I_id_type_t *H5I_id_type_list_g[H5I_MAX_NUM_TYPES];

/* Variable to keep track of the number of types allocated.  Its value is the */
/* next type ID to be handed out, so it is always one greater than the number */
/* of types. */
/* Starts at 1 instead of 0 because it makes trace output look nicer.  If more */
/* types (or IDs within a type) are needed, adjust TYPE_BITS in H5Ipkg.h       */
/* and/or increase size of hid_t */
static int H5I_next_type = (int)H5I_NTYPES;

/* Declare a free list to manage the H5I_id_info_t struct */
H5FL_DEFINE_STATIC(H5I_id_info_t);

/* Declare a free list to manage the H5I_id_type_t struct */
H5FL_DEFINE_STATIC(H5I_id_type_t);

/* Declare a free list to manage the H5I_class_t struct */
H5FL_DEFINE_STATIC(H5I_class_t);

H5FL_EXTERN(H5VL_object_t);

/*--------------------- Local function prototypes ---------------------------*/
static void *         H5I__unwrap(void *obj_ptr, H5I_type_t type);
static htri_t         H5I__clear_type_cb(void *_id, void *key, void *udata);
static int            H5I__destroy_type(H5I_type_t type);
static void *         H5I__remove_verify(hid_t id, H5I_type_t id_type);
static void *         H5I__remove_common(H5I_id_type_t *type_ptr, hid_t id);
static int            H5I__inc_type_ref(H5I_type_t type);
static int            H5I__get_type_ref(H5I_type_t type);
static int            H5I__search_cb(void *obj, hid_t id, void *_udata);
static H5I_id_info_t *H5I__find_id(hid_t id);
static int            H5I__iterate_pub_cb(void *obj, hid_t id, void *udata);
static int            H5I__find_id_cb(void *_item, void *_key, void *_udata);
static int            H5I__id_dump_cb(void *_item, void *_key, void *_udata);

/*-------------------------------------------------------------------------
 * Function:    H5I_term_package
 *
 * Purpose:     Terminate the H5I interface: release all memory, reset all
 *              global variables to initial values. This only happens if all
 *              types have been destroyed from other interfaces.
 *
 * Return:      Success:    Positive if any action was taken that might
 *                          affect some other interface; zero otherwise.
 *
 *              Failure:    Negative
 *
 *-------------------------------------------------------------------------
 */
int
H5I_term_package(void)
{
    int n = 0;

    FUNC_ENTER_NOAPI_NOINIT_NOERR

    if (H5_PKG_INIT_VAR) {
        H5I_id_type_t *type_ptr; /* Pointer to ID type */
        int            type;     /* Type of ID */

        /* How many types are still being used? */
        for (type = 0; type < H5I_next_type; type++)
            if ((type_ptr = H5I_id_type_list_g[type]) && type_ptr->ids)
                n++;

        /* If no types are used then clean up */
        if (0 == n) {
            for (type = 0; type < H5I_next_type; type++) {
                type_ptr = H5I_id_type_list_g[type];
                if (type_ptr) {
                    HDassert(NULL == type_ptr->ids);
                    type_ptr                 = H5FL_FREE(H5I_id_type_t, type_ptr);
                    H5I_id_type_list_g[type] = NULL;
                    n++;
                } /* end if */
            }     /* end for */

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

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

/*-------------------------------------------------------------------------
 * Function:    H5Iregister_type
 *
 * Purpose:     Public interface to H5I_register_type.  Creates a new type
 *              of ID's to give out.  A specific number (RESERVED) of type
 *              entries may be reserved to enable "constant" values to be handed
 *              out which are valid IDs in the type, but which do not map to any
 *              data structures and are not allocated dynamically later. HASH_SIZE is
 *              the minimum hash table size to use for the type. FREE_FUNC is
 *              called with an object pointer when the object is removed from
 *              the type.
 *
 * Return:      Success:    Type ID of the new type
 *              Failure:    H5I_BADID
 *
 *-------------------------------------------------------------------------
 */
H5I_type_t
H5Iregister_type(size_t H5_ATTR_DEBUG_API_USED hash_size, unsigned reserved, H5I_free_t free_func)
{
    H5I_class_t *cls = NULL;            /* New ID class */
    H5I_type_t   new_type;              /* New ID type value */
    H5I_type_t   ret_value = H5I_BADID; /* Return value */

    FUNC_ENTER_API(H5I_BADID)
    H5TRACE3("It", "zIux", hash_size, reserved, free_func);

    /* Generate a new H5I_type_t value */

    /* Increment the number of types */
    if (H5I_next_type < H5I_MAX_NUM_TYPES) {
        new_type = (H5I_type_t)H5I_next_type;
        H5I_next_type++;
    } /* end if */
    else {
        hbool_t done; /* Indicate that search was successful */
        int     i;    /* Local index variable */

        /* Look for a free type to give out */
        done = FALSE;
        for (i = H5I_NTYPES; i < H5I_MAX_NUM_TYPES && done == FALSE; i++) {
            if (NULL == H5I_id_type_list_g[i]) {
                /* Found a free type ID */
                new_type = (H5I_type_t)i;
                done     = TRUE;
            } /* end if */
        }     /* end for */

        /* Verify that we found a type to give out */
        if (done == FALSE)
            HGOTO_ERROR(H5E_ATOM, H5E_NOSPACE, H5I_BADID, "Maximum number of ID types exceeded")
    } /* end else */

    /* Allocate new ID class */
    if (NULL == (cls = H5FL_CALLOC(H5I_class_t)))
        HGOTO_ERROR(H5E_ATOM, H5E_CANTALLOC, H5I_BADID, "ID class allocation failed")

    /* Initialize class fields */
    cls->type_id   = new_type;
    cls->flags     = H5I_CLASS_IS_APPLICATION;
    cls->reserved  = reserved;
    cls->free_func = free_func;

    /* Register the new ID class */
    if (H5I_register_type(cls) < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_CANTINIT, H5I_BADID, "can't initialize ID class")

    /* Set return value */
    ret_value = new_type;

done:
    /* Clean up on error */
    if (ret_value < 0)
        if (cls)
            cls = H5FL_FREE(H5I_class_t, cls);

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

/*-------------------------------------------------------------------------
 * Function:    H5I_register_type
 *
 * Purpose:     Creates a new type of ID's to give out.
 *              The class is initialized or its reference count is incremented
 *              (if it is already initialized).
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5I_register_type(const H5I_class_t *cls)
{
    H5I_id_type_t *type_ptr  = NULL;    /* Ptr to the atomic type*/
    herr_t         ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    /* Sanity check */
    HDassert(cls);
    HDassert(cls->type_id > 0 && (int)cls->type_id < H5I_MAX_NUM_TYPES);

    /* Initialize the type */
    if (NULL == H5I_id_type_list_g[cls->type_id]) {
        /* Allocate the type information for new type */
        if (NULL == (type_ptr = (H5I_id_type_t *)H5FL_CALLOC(H5I_id_type_t)))
            HGOTO_ERROR(H5E_ATOM, H5E_CANTALLOC, FAIL, "ID type allocation failed")
        H5I_id_type_list_g[cls->type_id] = type_ptr;
    } /* end if */
    else {
        /* Get the pointer to the existing type */
        type_ptr = H5I_id_type_list_g[cls->type_id];
    } /* end else */

    /* Initialize the ID type structure for new types */
    if (type_ptr->init_count == 0) {
        type_ptr->cls       = cls;
        type_ptr->id_count  = 0;
        type_ptr->nextid    = cls->reserved;
        type_ptr->last_info = NULL;
        if (NULL == (type_ptr->ids = H5SL_create(H5SL_TYPE_HID, NULL)))
            HGOTO_ERROR(H5E_ATOM, H5E_CANTCREATE, FAIL, "skip list creation failed")
    } /* end if */

    /* Increment the count of the times this type has been initialized */
    type_ptr->init_count++;

done:
    if (ret_value < 0) { /* Clean up on error */
        if (type_ptr) {
            if (type_ptr->ids)
                H5SL_close(type_ptr->ids);
            (void)H5FL_FREE(H5I_id_type_t, type_ptr);
        } /* end if */
    }     /* end if */

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

/*-------------------------------------------------------------------------
 * Function:    H5Itype_exists
 *
 * Purpose:     Query function to inform the user if a given type is
 *              currently registered with the library.
 *
 * Return:      TRUE/FALSE/FAIL
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5Itype_exists(H5I_type_t type)
{
    htri_t ret_value = TRUE; /* Return value */

    FUNC_ENTER_API(FAIL)
    H5TRACE1("t", "It", type);

    /* Validate parameter */
    if (H5I_IS_LIB_TYPE(type))
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, FAIL, "cannot call public function on library type")
    if (type <= H5I_BADID || (int)type >= H5I_next_type)
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "invalid type number")

    if (NULL == H5I_id_type_list_g[type])
        ret_value = FALSE;

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

/*-------------------------------------------------------------------------
 * Function:    H5Inmembers
 *
 * Purpose:     Returns the number of members in a type.  Public interface to
 *              H5I_nmembers.  The public interface throws an error if the
 *              supplied type does not exist.  This is different than the
 *              private interface, which will just return 0.
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:    James Laird
 *        Nathaniel Furrer
 *              Friday, April 23, 2004
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Inmembers(H5I_type_t type, hsize_t *num_members)
{
    int ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)
    H5TRACE2("e", "It*h", type, num_members);

    if (H5I_IS_LIB_TYPE(type))
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, FAIL, "cannot call public function on library type")

    /* Validate parameters.  This needs to be done here, instead of letting
     * the private interface handle it, because the public interface throws
     * an error when the supplied type does not exist.
     */
    if (type <= H5I_BADID || (int)type >= H5I_next_type)
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "invalid type number")
    if (NULL == H5I_id_type_list_g[type])
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "supplied type does not exist")

    if (num_members) {
        int64_t members;

        if ((members = H5I_nmembers(type)) < 0)
            HGOTO_ERROR(H5E_ATOM, H5E_CANTCOUNT, FAIL, "can't compute number of members")

        H5_CHECKED_ASSIGN(*num_members, hsize_t, members, int64_t);
    } /* end if */

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

/*-------------------------------------------------------------------------
 * Function:    H5I_nmembers
 *
 * Purpose:     Returns the number of members in a type.
 *
 * Return:      Success:    Number of members; zero if the type is empty
 *                          or has been deleted.
 *
 *              Failure:    Negative
 *
 * Programmer:    Robb Matzke
 *              Wednesday, March 24, 1999
 *
 *-------------------------------------------------------------------------
 */
int64_t
H5I_nmembers(H5I_type_t type)
{
    H5I_id_type_t *type_ptr;      /* Pointer to the ID type */
    int64_t        ret_value = 0; /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    /* Validate parameter */
    if (type <= H5I_BADID || (int)type >= H5I_next_type)
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "invalid type number")
    if (NULL == (type_ptr = H5I_id_type_list_g[type]) || type_ptr->init_count <= 0)
        HGOTO_DONE(0);

    /* Set return value */
    H5_CHECKED_ASSIGN(ret_value, int64_t, type_ptr->id_count, uint64_t);

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

/*-------------------------------------------------------------------------
 * Function:    H5I__unwrap
 *
 * Purpose:     Unwraps the object pointer for the 'item' that corresponds
 *              to an ID.
 *
 * Return:      Pointer to the unwrapped pointer (can't fail)
 *
 * Programmer:  Quincey Koziol
 *              Friday, October 19, 2018
 *
 *-------------------------------------------------------------------------
 */
static void *
H5I__unwrap(void *obj_ptr, H5I_type_t type)
{
    void *ret_value = NULL; /* Return value */

    FUNC_ENTER_STATIC_NOERR

    /* Sanity checks */
    HDassert(obj_ptr);

    /* The stored object pointer might be an H5VL_object_t, in which
     * case we'll need to get the wrapped object struct (H5F_t *, etc.).
     */
    if (H5I_FILE == type || H5I_GROUP == type || H5I_DATASET == type || H5I_ATTR == type) {
        const H5VL_object_t *vol_obj;

        vol_obj   = (const H5VL_object_t *)obj_ptr;
        ret_value = H5VL_object_data(vol_obj);
    } /* end if */
    else if (H5I_DATATYPE == type) {
        H5T_t *dt = (H5T_t *)obj_ptr;

        ret_value = (void *)H5T_get_actual_type(dt);
    } /* end if */
    else
        ret_value = obj_ptr;

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

/*-------------------------------------------------------------------------
 * Function:    H5Iclear_type
 *
 * Purpose:     Removes all objects from the type, calling the free
 *              function for each object regardless of the reference count.
 *              Public interface to H5I_clear_type.
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:    James Laird
 *        Nathaniel Furrer
 *              Friday, April 23, 2004
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Iclear_type(H5I_type_t type, hbool_t force)
{
    herr_t ret_value = FAIL; /* Return value */

    FUNC_ENTER_API(FAIL)
    H5TRACE2("e", "Itb", type, force);

    if (H5I_IS_LIB_TYPE(type))
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, FAIL, "cannot call public function on library type")

    ret_value = H5I_clear_type(type, force, TRUE);

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

/*-------------------------------------------------------------------------
 * Function:    H5I_clear_type
 *
 * Purpose:     Removes all objects from the type, calling the free
 *              function for each object regardless of the reference count.
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:    Robb Matzke
 *              Wednesday, March 24, 1999
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5I_clear_type(H5I_type_t type, hbool_t force, hbool_t app_ref)
{
    H5I_clear_type_ud_t udata;               /* udata struct for callback */
    int                 ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    /* Validate parameters */
    if (type <= H5I_BADID || (int)type >= H5I_next_type)
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "invalid type number")

    udata.type_ptr = H5I_id_type_list_g[type];
    if (udata.type_ptr == NULL || udata.type_ptr->init_count <= 0)
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, FAIL, "invalid type")

    /* Finish constructing udata */
    udata.force   = force;
    udata.app_ref = app_ref;

    /* Attempt to free all ids in the type */
    if (H5SL_try_free_safe(udata.type_ptr->ids, H5I__clear_type_cb, &udata) < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_CANTDELETE, FAIL, "can't free ids in type")

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

/*-------------------------------------------------------------------------
 * Function:    H5I__clear_type_cb
 *
 * Purpose:     Attempts to free the specified ID, calling the free
 *              function for the object.
 *
 * Return:      TRUE/FALSE/FAIL
 *
 * Programmer:  Neil Fortner
 *              Friday, July 10, 2015
 *
 *-------------------------------------------------------------------------
 */
static htri_t
H5I__clear_type_cb(void *_id, void H5_ATTR_UNUSED *key, void *_udata)
{
    H5I_id_info_t *      id        = (H5I_id_info_t *)_id;          /* Current ID being worked with */
    H5I_clear_type_ud_t *udata     = (H5I_clear_type_ud_t *)_udata; /* udata struct */
    htri_t               ret_value = FALSE;                         /* Return value */

    FUNC_ENTER_STATIC_NOERR

    /* Sanity checks */
    HDassert(id);
    HDassert(udata);
    HDassert(udata->type_ptr);

    /* Do nothing to the object if the reference count is larger than
     * one and forcing is off.
     */
    if (udata->force || (id->count - (!udata->app_ref * id->app_count)) <= 1) {
        /* Check for a 'free' function and call it, if it exists */
        if (udata->type_ptr->cls->free_func &&
            (udata->type_ptr->cls->free_func)((void *)id->obj_ptr) < 0) { /* (Casting away const OK -QAK) */
            if (udata->force) {
#ifdef H5I_DEBUG
                if (H5DEBUG(I)) {
                    HDfprintf(H5DEBUG(I),
                              "H5I: free type=%d obj=0x%08lx "
                              "failure ignored\n",
                              (int)udata->type_ptr->cls->type_id, (unsigned long)(id->obj_ptr));
                } /* end if */
#endif            /*H5I_DEBUG*/

                /* Indicate node should be removed from list */
                ret_value = TRUE;
            } /* end if */
        }     /* end if */
        else {
            /* Indicate node should be removed from list */
            ret_value = TRUE;
        } /* end else */

        /* Remove ID if requested */
        if (ret_value) {
            /* Free ID info */
            id = H5FL_FREE(H5I_id_info_t, id);

            /* Decrement the number of IDs in the type */
            udata->type_ptr->id_count--;
        } /* end if */
    }     /* end if */

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

/*-------------------------------------------------------------------------
 * Function:    H5Idestroy_type
 *
 * Purpose:     Destroys a type along with all atoms in that type
 *              regardless of their reference counts. Destroying IDs
 *              involves calling the free-func for each ID's object and
 *              then adding the ID struct to the ID free list.  Public
 *              interface to H5I__destroy_type.
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:    Nathaniel Furrer
 *        James Laird
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Idestroy_type(H5I_type_t type)
{
    herr_t ret_value = FAIL; /* Return value */

    FUNC_ENTER_API(FAIL)
    H5TRACE1("e", "It", type);

    if (H5I_IS_LIB_TYPE(type))
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, FAIL, "cannot call public function on library type")

    ret_value = H5I__destroy_type(type);

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

/*-------------------------------------------------------------------------
 * Function:    H5I__destroy_type
 *
 * Purpose:     Destroys a type along with all atoms in that type
 *              regardless of their reference counts. Destroying IDs
 *              involves calling the free-func for each ID's object and
 *              then adding the ID struct to the ID free list.
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:    Nathaniel Furrer
 *        James Laird
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5I__destroy_type(H5I_type_t type)
{
    H5I_id_type_t *type_ptr;            /* ptr to the atomic type */
    herr_t         ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_STATIC

    /* Validate parameter */
    if (type <= H5I_BADID || (int)type >= H5I_next_type)
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "invalid type number")

    type_ptr = H5I_id_type_list_g[type];
    if (type_ptr == NULL || type_ptr->init_count <= 0)
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, FAIL, "invalid type")

    /* Close/clear/destroy all IDs for this type */
    H5E_BEGIN_TRY { H5I_clear_type(type, TRUE, FALSE); }
    H5E_END_TRY /*don't care about errors*/

        /* Check if we should release the ID class */
        if (type_ptr->cls->flags & H5I_CLASS_IS_APPLICATION)
            type_ptr->cls = H5FL_FREE(H5I_class_t, (void *)type_ptr->cls);

    if (H5SL_close(type_ptr->ids) < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_CANTCLOSEOBJ, FAIL, "can't close skip list")
    type_ptr->ids = NULL;

    type_ptr                 = H5FL_FREE(H5I_id_type_t, type_ptr);
    H5I_id_type_list_g[type] = NULL;

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

/*-------------------------------------------------------------------------
 * Function:    H5Iregister
 *
 * Purpose:     Public interface to H5I_register.
 *
 * Return:      Success:    New object ID
 *              Failure:    H5I_INVALID_HID
 *
 *-------------------------------------------------------------------------
 */
hid_t
H5Iregister(H5I_type_t type, const void *object)
{
    hid_t ret_value = H5I_INVALID_HID; /* Return value */

    FUNC_ENTER_API(H5I_INVALID_HID)
    H5TRACE2("i", "It*x", type, object);

    if (H5I_IS_LIB_TYPE(type))
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, FAIL, "cannot call public function on library type")

    ret_value = H5I_register(type, object, TRUE);

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

/*-------------------------------------------------------------------------
 * Function:    H5I_register
 *
 * Purpose:     Registers an OBJECT in a TYPE and returns an ID for it.
 *              This routine does _not_ check for unique-ness of the objects,
 *              if you register an object twice, you will get two different
 *              IDs for it.  This routine does make certain that each ID in a
 *              type is unique.  IDs are created by getting a unique number
 *              for the type the ID is in and incorporating the type into
 *              the ID which is returned to the user.
 *
 * Return:      Success:    New object ID
 *              Failure:    H5I_INVALID_HID
 *
 *-------------------------------------------------------------------------
 */
hid_t
H5I_register(H5I_type_t type, const void *object, hbool_t app_ref)
{
    H5I_id_type_t *type_ptr  = NULL;            /* ptr to the type                  */
    H5I_id_info_t *id_ptr    = NULL;            /* ptr to the new ID information    */
    hid_t          new_id    = -1;              /* new ID                           */
    hid_t          ret_value = H5I_INVALID_HID; /* return value             */

    FUNC_ENTER_NOAPI(FAIL)

    /* Check arguments */
    if (type <= H5I_BADID || (int)type >= H5I_next_type)
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, H5I_INVALID_HID, "invalid type number")
    type_ptr = H5I_id_type_list_g[type];
    if ((NULL == type_ptr) || (type_ptr->init_count <= 0))
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, H5I_INVALID_HID, "invalid type")
    if (NULL == (id_ptr = H5FL_MALLOC(H5I_id_info_t)))
        HGOTO_ERROR(H5E_ATOM, H5E_NOSPACE, H5I_INVALID_HID, "memory allocation failed")

    /* Create the struct & its ID */
    new_id            = H5I_MAKE(type, type_ptr->nextid);
    id_ptr->id        = new_id;
    id_ptr->count     = 1; /* initial reference count */
    id_ptr->app_count = !!app_ref;
    id_ptr->obj_ptr   = object;

    /* Insert into the type */
    if (H5SL_insert(type_ptr->ids, id_ptr, &id_ptr->id) < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_CANTINSERT, H5I_INVALID_HID, "can't insert ID node into skip list")
    type_ptr->id_count++;
    type_ptr->nextid++;

    /* Sanity check for the 'nextid' getting too large and wrapping around */
    HDassert(type_ptr->nextid <= ID_MASK);

    /* Set the most recent ID to this object */
    type_ptr->last_info = id_ptr;

    /* Set return value */
    ret_value = new_id;

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

/*-------------------------------------------------------------------------
 * Function:    H5I_register_using_existing_id
 *
 * Purpose:     Registers an OBJECT in a TYPE with the supplied ID for it.
 *              This routine will check to ensure the supplied ID is not already
 *              in use, and ensure that it is a valid ID for the given type,
 *              but will NOT check to ensure the OBJECT is not already
 *              registered (thus, it is possible to register one object under
 *              multiple IDs).
 *
 * NOTE:        Intended for use in refresh calls, where we have to close
 *              and re-open the underlying data, then hook the object back
 *              up to the original ID.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5I_register_using_existing_id(H5I_type_t type, void *object, hbool_t app_ref, hid_t existing_id)
{
    H5I_id_type_t *type_ptr;            /* ptr to the type                  */
    H5I_id_info_t *id_ptr;              /* ptr to the new ID information    */
    herr_t         ret_value = SUCCEED; /* return value                     */

    FUNC_ENTER_NOAPI(FAIL)

    /* Check arguments */
    HDassert(object);

    /* Make sure ID is not already in use */
    if (NULL != (id_ptr = H5I__find_id(existing_id)))
        HGOTO_ERROR(H5E_ATOM, H5E_BADRANGE, FAIL, "ID already in use")

    /* Make sure type number is valid */
    if (type <= H5I_BADID || (int)type >= H5I_next_type)
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "invalid type number")

    /* Get type pointer from list of types */
    type_ptr = H5I_id_type_list_g[type];

    if (NULL == type_ptr || type_ptr->init_count <= 0)
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, FAIL, "invalid type")

    /* Make sure requested ID belongs to object's type */
    if (H5I_TYPE(existing_id) != type)
        HGOTO_ERROR(H5E_ATOM, H5E_BADRANGE, FAIL, "invalid type for provided ID")

    /* Allocate new structure to house this ID */
    if (NULL == (id_ptr = H5FL_MALLOC(H5I_id_info_t)))
        HGOTO_ERROR(H5E_ATOM, H5E_NOSPACE, FAIL, "memory allocation failed")

    /* Create the struct & insert requested ID */
    id_ptr->id        = existing_id;
    id_ptr->count     = 1; /* initial reference count*/
    id_ptr->app_count = !!app_ref;
    id_ptr->obj_ptr   = object;

    /* Insert into the type */
    if (H5SL_insert(type_ptr->ids, id_ptr, &id_ptr->id) < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_CANTINSERT, FAIL, "can't insert ID node into skip list")
    type_ptr->id_count++;

    /* Set the most recent ID to this object */
    type_ptr->last_info = id_ptr;

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

/*-------------------------------------------------------------------------
 * Function:    H5I_subst
 *
 * Purpose:     Substitute a new object pointer for the specified ID.
 *
 * Return:      Success:    Non-NULL previous object pointer associated
 *                          with the specified ID.
 *              Failure:    NULL
 *
 * Programmer:    Quincey Koziol
 *        Saturday, February 27, 2010
 *
 *-------------------------------------------------------------------------
 */
void *
H5I_subst(hid_t id, const void *new_object)
{
    H5I_id_info_t *id_ptr;           /* Pointer to the atom */
    void *         ret_value = NULL; /* Return value */

    FUNC_ENTER_NOAPI(NULL)

    /* General lookup of the ID */
    if (NULL == (id_ptr = H5I__find_id(id)))
        HGOTO_ERROR(H5E_ATOM, H5E_NOTFOUND, NULL, "can't get ID ref count")

    /* Get the old object pointer to return */
    ret_value = (void *)id_ptr->obj_ptr; /* (Casting away const OK -QAK) */

    /* Set the new object pointer for the ID */
    id_ptr->obj_ptr = new_object;

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

/*-------------------------------------------------------------------------
 * Function:    H5I_object
 *
 * Purpose:     Find an object pointer for the specified ID.
 *
 * Return:      Success:    Non-NULL object pointer associated with the
 *                          specified ID
 *
 *              Failure:    NULL
 *
 *-------------------------------------------------------------------------
 */
void *
H5I_object(hid_t id)
{
    H5I_id_info_t *id_ptr;           /* Pointer to the new atom  */
    void *         ret_value = NULL; /* Return value             */

    FUNC_ENTER_NOAPI_NOERR

    /* General lookup of the ID */
    if (NULL != (id_ptr = H5I__find_id(id))) {
        /* Get the object pointer to return */
        ret_value = (void *)id_ptr->obj_ptr; /* (Casting away const OK -QAK) */
    }

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

/*-------------------------------------------------------------------------
 * Function:    H5Iobject_verify
 *
 * Purpose:     Find an object pointer for the specified ID, verifying that
 *              its in a particular type.  Public interface to
 *              H5I_object_verify.
 *
 * Return:      Success:    Non-NULL object pointer associated with the
 *                          specified ID.
 *              Failure:    NULL
 *
 *-------------------------------------------------------------------------
 */
void *
H5Iobject_verify(hid_t id, H5I_type_t id_type)
{
    void *ret_value = NULL; /* Return value */

    FUNC_ENTER_API(NULL)
    H5TRACE2("*x", "iIt", id, id_type);

    /* Validate parameters */
    if (H5I_IS_LIB_TYPE(id_type))
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, NULL, "cannot call public function on library type")
    if (id_type < 1 || (int)id_type >= H5I_next_type)
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, NULL, "identifier has invalid type")

    ret_value = H5I_object_verify(id, id_type);

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

/*-------------------------------------------------------------------------
 * Function:    H5I_object_verify
 *
 * Purpose:     Find an object pointer for the specified ID, verifying that
 *              its in a particular type.
 *
 * Return:      Success:    Non-NULL object pointer associated with the
 *                          specified ID.
 *              Failure:    NULL
 *
 * Programmer:    Quincey Koziol
 *        Wednesday, July 31, 2002
 *
 *-------------------------------------------------------------------------
 */
void *
H5I_object_verify(hid_t id, H5I_type_t id_type)
{
    H5I_id_info_t *id_ptr    = NULL; /* Pointer to the new atom  */
    void *         ret_value = NULL; /* Return value             */

    FUNC_ENTER_NOAPI_NOERR

    HDassert(id_type >= 1 && (int)id_type < H5I_next_type);

    /* Verify that the type of the ID is correct & lookup the ID */
    if (id_type == H5I_TYPE(id) && NULL != (id_ptr = H5I__find_id(id))) {
        /* Get the object pointer to return */
        ret_value = (void *)id_ptr->obj_ptr; /* (Casting away const OK -QAK) */
    }

    FUNC_LEAVE_NOAPI(ret_value)
} /* H5I_object_verify() */

/*-------------------------------------------------------------------------
 * Function:    H5I_get_type
 *
 * Purpose:     Given an object ID return the type to which it
 *              belongs.  The ID need not be the ID of an object which
 *              currently exists because the type number is encoded
 *              in the object ID.
 *
 * Return:      Success:    A positive integer (corresponding to an H5I_type_t
 *                          enum value for library ID types, but not for user
 *                          ID types).
 *              Failure:    H5I_BADID
 *
 * Programmer:    Robb Matzke
 *        Friday, February 19, 1999
 *
 *-------------------------------------------------------------------------
 */
H5I_type_t
H5I_get_type(hid_t id)
{
    H5I_type_t ret_value = H5I_BADID; /* Return value */

    FUNC_ENTER_NOAPI_NOERR

    if (id > 0)
        ret_value = H5I_TYPE(id);

    HDassert(ret_value >= H5I_BADID && (int)ret_value < H5I_next_type);

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

/*-------------------------------------------------------------------------
 * Function:    H5Iget_type
 *
 * Purpose:     The public version of H5I_get_type(), obtains a type number
 *              when given an ID.  The ID need not be the ID of an
 *              object which currently exists because the type number is
 *              encoded as part of the ID.
 *
 * Return:      Success:    A positive integer (corresponding to an H5I_type_t
 *                          enum value for library ID types, but not for user
 *                          ID types).
 *              Failure:    H5I_BADID
 *
 *-------------------------------------------------------------------------
 */
H5I_type_t
H5Iget_type(hid_t id)
{
    H5I_type_t ret_value = H5I_BADID; /* Return value */

    FUNC_ENTER_API(H5I_BADID)
    H5TRACE1("It", "i", id);

    ret_value = H5I_get_type(id);

    if (ret_value <= H5I_BADID || (int)ret_value >= H5I_next_type || NULL == H5I_object(id))
        HGOTO_DONE(H5I_BADID);

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

/*-------------------------------------------------------------------------
 * Function:    H5I_is_file_object
 *
 * Purpose:     Convenience function to determine if an ID represents
 *              a file object.
 *
 *              In H5O calls, you can't use object_verify to ensure
 *              the ID was of the correct class since there's no
 *              H5I_OBJECT ID class.
 *
 * Return:      Success:    TRUE/FALSE
 *              Failure:    FAIL
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5I_is_file_object(hid_t id)
{
    H5I_type_t id_type   = H5I_get_type(id);
    htri_t     ret_value = FAIL;

    FUNC_ENTER_NOAPI(FAIL);

    /* Fail if the ID type is out of range */
    if (id_type < 1 || id_type >= H5I_NTYPES)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "ID type out of range");

    /* Return TRUE if the ID is a file object (dataset, group, map, or committed
     * datatype), FALSE otherwise.
     */
    if (H5I_DATASET == id_type || H5I_GROUP == id_type || H5I_MAP == id_type)
        ret_value = TRUE;
    else if (H5I_DATATYPE == id_type) {

        H5T_t *dt = NULL;

        if (NULL == (dt = (H5T_t *)H5I_object(id)))
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "unable to get underlying datatype struct");

        ret_value = H5T_is_named(dt);
    }
    else
        ret_value = FALSE;

done:
    FUNC_LEAVE_NOAPI(ret_value);
} /* H5I_is_file_object() */

/*-------------------------------------------------------------------------
 * Function:    H5Iremove_verify
 *
 * Purpose:     Removes the specified ID from its type, first checking that the
 *              type of the ID and the type type are the same.  Public interface to
 *              H5I__remove_verify.
 *
 * Return:      Success:    A pointer to the object that was removed, the
 *                          same pointer which would have been found by
 *                          calling H5I_object().
 *              Failure:    NULL
 *
 * Programmer:    James Laird
 *        Nathaniel Furrer
 *
 *-------------------------------------------------------------------------
 */
void *
H5Iremove_verify(hid_t id, H5I_type_t id_type)
{
    void *ret_value = NULL; /* Return value */

    FUNC_ENTER_API(NULL)
    H5TRACE2("*x", "iIt", id, id_type);

    if (H5I_IS_LIB_TYPE(id_type))
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, NULL, "cannot call public function on library type")

    /* Remove the id */
    ret_value = H5I__remove_verify(id, id_type);

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

/*-------------------------------------------------------------------------
 * Function:    H5I__remove_verify
 *
 * Purpose:     Removes the specified ID from its type, first checking that
 *              the ID's type is the same as the ID type supplied as an argument
 *
 * Return:      Success:    A pointer to the object that was removed, the
 *                          same pointer which would have been found by
 *                          calling H5I_object().
 *              Failure:    NULL
 *
 * Programmer:    James Laird
 *        Nat Furrer
 *
 *-------------------------------------------------------------------------
 */
static void *
H5I__remove_verify(hid_t id, H5I_type_t id_type)
{
    void *ret_value = NULL; /*return value            */

    FUNC_ENTER_STATIC_NOERR

    /* Argument checking will be performed by H5I_remove() */

    /* Verify that the type of the ID is correct */
    if (id_type == H5I_TYPE(id))
        ret_value = H5I_remove(id);

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

/*-------------------------------------------------------------------------
 * Function:    H5I__remove_common
 *
 * Purpose:     Common code to remove a specified ID from its type.
 *
 * Return:      Success:    A pointer to the object that was removed, the
 *                          same pointer which would have been found by
 *                          calling H5I_object().
 *              Failure:    NULL
 *
 * Programmer:  Quincey Koziol
 *              October 3, 2013
 *
 *-------------------------------------------------------------------------
 */
static void *
H5I__remove_common(H5I_id_type_t *type_ptr, hid_t id)
{
    H5I_id_info_t *curr_id;          /* Pointer to the current atom */
    void *         ret_value = NULL; /* Return value */

    FUNC_ENTER_STATIC

    /* Sanity check */
    HDassert(type_ptr);

    /* Get the ID node for the ID */
    if (NULL == (curr_id = (H5I_id_info_t *)H5SL_remove(type_ptr->ids, &id)))
        HGOTO_ERROR(H5E_ATOM, H5E_CANTDELETE, NULL, "can't remove ID node from skip list")

    /* Check if this ID was the last one accessed */
    if (type_ptr->last_info == curr_id)
        type_ptr->last_info = NULL;

    ret_value = (void *)curr_id->obj_ptr; /* (Casting away const OK -QAK) */
    curr_id   = H5FL_FREE(H5I_id_info_t, curr_id);

    /* Decrement the number of IDs in the type */
    (type_ptr->id_count)--;

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

/*-------------------------------------------------------------------------
 * Function:    H5I_remove
 *
 * Purpose:     Removes the specified ID from its type.
 *
 * Return:      Success:    A pointer to the object that was removed, the
 *                          same pointer which would have been found by
 *                          calling H5I_object().
 *              Failure:    NULL
 *
 * Programmer:    Unknown
 *
 *-------------------------------------------------------------------------
 */
void *
H5I_remove(hid_t id)
{
    H5I_id_type_t *type_ptr;         /* Pointer to the atomic type */
    H5I_type_t     type;             /* Atom's atomic type */
    void *         ret_value = NULL; /* Return value */

    FUNC_ENTER_NOAPI(NULL)

    /* Check arguments */
    type = H5I_TYPE(id);
    if (type <= H5I_BADID || (int)type >= H5I_next_type)
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, NULL, "invalid type number")
    type_ptr = H5I_id_type_list_g[type];
    if (type_ptr == NULL || type_ptr->init_count <= 0)
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, NULL, "invalid type")

    /* Remove the node from the type */
    if (NULL == (ret_value = H5I__remove_common(type_ptr, id)))
        HGOTO_ERROR(H5E_ATOM, H5E_CANTDELETE, NULL, "can't remove ID node")

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

/*-------------------------------------------------------------------------
 * Function:    H5Idec_ref
 *
 * Purpose:     Decrements the number of references outstanding for an ID.
 *              If the reference count for an ID reaches zero, the object
 *              will be closed.
 *
 * Return:      Success:    New reference count
 *              Failure:    -1
 *
 * Programmer:  Quincey Koziol
 *              Dec  7, 2003
 *
 *-------------------------------------------------------------------------
 */
int
H5Idec_ref(hid_t id)
{
    int ret_value = 0; /* Return value */

    FUNC_ENTER_API((-1))
    H5TRACE1("Is", "i", id);

    /* Check arguments */
    if (id < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, (-1), "invalid ID")

    /* Do actual decrement operation */
    if ((ret_value = H5I_dec_app_ref(id)) < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_CANTDEC, (-1), "can't decrement ID ref count")

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

/*-------------------------------------------------------------------------
 * Function:    H5I_dec_ref
 *
 * Purpose:     Decrements the number of references outstanding for an ID.
 *              This will fail if the type is not a reference counted type.
 *              The ID type's 'free' function will be called for the ID
 *              if the reference count for the ID reaches 0 and a free
 *              function has been defined at type creation time.
 *
 * Return:      Success:    New reference count
 *
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
int
H5I_dec_ref(hid_t id)
{
    H5I_id_info_t *id_ptr;        /* Pointer to the new ID */
    int            ret_value = 0; /* Return value */

    FUNC_ENTER_NOAPI((-1))

    /* Sanity check */
    HDassert(id >= 0);

    /* General lookup of the ID */
    if (NULL == (id_ptr = H5I__find_id(id)))
        HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, (-1), "can't locate ID")

    /* If this is the last reference to the object then invoke the type's
     * free method on the object. If the free method is undefined or
     * successful then remove the object from the type; otherwise leave
     * the object in the type without decrementing the reference
     * count. If the reference count is more than one then decrement the
     * reference count without calling the free method.
     *
     * Beware: the free method may call other H5I functions.
     *
     * If an object is closing, we can remove the ID even though the free
     * method might fail.  This can happen when a mandatory filter fails to
     * write when a dataset is closed and the chunk cache is flushed to the
     * file.  We have to close the dataset anyway. (SLU - 2010/9/7)
     */
    if (1 == id_ptr->count) {
        H5I_id_type_t *type_ptr; /*ptr to the type    */

        /* Get the ID's type */
        type_ptr = H5I_id_type_list_g[H5I_TYPE(id)];

        /* (Casting away const OK -QAK) */
        if (!type_ptr->cls->free_func || (type_ptr->cls->free_func)((void *)id_ptr->obj_ptr) >= 0) {
            /* Remove the node from the type */
            if (NULL == H5I__remove_common(type_ptr, id))
                HGOTO_ERROR(H5E_ATOM, H5E_CANTDELETE, (-1), "can't remove ID node")
            ret_value = 0;
        } /* end if */
        else
            ret_value = -1;
    } /* end if */
    else {
        --(id_ptr->count);
        ret_value = (int)id_ptr->count;
    } /* end else */

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

/*-------------------------------------------------------------------------
 * Function:    H5I_dec_app_ref
 *
 * Purpose:     H5I_dec_ref wrapper for case of modifying the application ref.
 *              count for an ID as well as normal reference count.
 *
 * Return:      Success:    New app. reference count
 *              Failure:    -1
 *
 * Programmer:  Quincey Koziol
 *              Sept 16, 2010
 *
 *-------------------------------------------------------------------------
 */
int
H5I_dec_app_ref(hid_t id)
{
    H5I_id_info_t *id_ptr;        /* Pointer to the new ID */
    int            ret_value = 0; /* Return value */

    FUNC_ENTER_NOAPI((-1))

    /* Sanity check */
    HDassert(id >= 0);

    /* Call regular decrement reference count routine */
    if ((ret_value = H5I_dec_ref(id)) < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_CANTDEC, (-1), "can't decrement ID ref count")

    /* Check if the ID still exists */
    if (ret_value > 0) {
        /* General lookup of the ID */
        if (NULL == (id_ptr = H5I__find_id(id)))
            HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, (-1), "can't locate ID")

        /* Adjust app_ref */
        --(id_ptr->app_count);
        HDassert(id_ptr->count >= id_ptr->app_count);

        /* Set return value */
        ret_value = (int)id_ptr->app_count;
    } /* end if */

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

/*-------------------------------------------------------------------------
 * Function:    H5I_dec_app_ref_always_close
 *
 * Purpose:     H5I_dec_app_ref wrapper for case of always closing the ID,
 *              even when the free routine fails
 *
 * Return:      Success:    New app. reference count
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
int
H5I_dec_app_ref_always_close(hid_t id)
{
    int ret_value = 0; /* Return value */

    FUNC_ENTER_NOAPI((-1))

    /* Sanity check */
    HDassert(id >= 0);

    /* Call application decrement reference count routine */
    ret_value = H5I_dec_app_ref(id);

    /* Check for failure */
    if (ret_value < 0) {
        /*
         * If an object is closing, we can remove the ID even though the free
         * method might fail.  This can happen when a mandatory filter fails to
         * write when a dataset is closed and the chunk cache is flushed to the
         * file.  We have to close the dataset anyway. (SLU - 2010/9/7)
         */
        H5I_remove(id);

        HGOTO_ERROR(H5E_ATOM, H5E_CANTDEC, (-1), "can't decrement ID ref count")
    }

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

/*-------------------------------------------------------------------------
 * Function:    H5Iinc_ref
 *
 * Purpose:     Increments the number of references outstanding for an ID.
 *
 * Return:      Success:    New reference count
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
int
H5Iinc_ref(hid_t id)
{
    int ret_value; /* Return value */

    FUNC_ENTER_API((-1))
    H5TRACE1("Is", "i", id);

    /* Check arguments */
    if (id < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, (-1), "invalid ID")

    /* Do actual increment operation */
    if ((ret_value = H5I_inc_ref(id, TRUE)) < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_CANTINC, (-1), "can't increment ID ref count")

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

/*-------------------------------------------------------------------------
 * Function:    H5I_inc_ref
 *
 * Purpose:     Increment the reference count for an object.
 *
 * Return:      Success:    The new reference count
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
int
H5I_inc_ref(hid_t id, hbool_t app_ref)
{
    H5I_id_info_t *id_ptr;        /* Pointer to the ID */
    int            ret_value = 0; /* Return value */

    FUNC_ENTER_NOAPI((-1))

    /* Sanity check */
    HDassert(id >= 0);

    /* General lookup of the ID */
    if (NULL == (id_ptr = H5I__find_id(id)))
        HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, (-1), "can't locate ID")

    /* Adjust reference counts */
    ++(id_ptr->count);
    if (app_ref)
        ++(id_ptr->app_count);

    /* Set return value */
    ret_value = (int)(app_ref ? id_ptr->app_count : id_ptr->count);

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

/*-------------------------------------------------------------------------
 * Function:    H5Iget_ref
 *
 * Purpose:     Retrieves the number of references outstanding for an ID.
 *
 * Return:      Success:    Reference count
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
int
H5Iget_ref(hid_t id)
{
    int ret_value; /* Return value */

    FUNC_ENTER_API((-1))
    H5TRACE1("Is", "i", id);

    /* Check arguments */
    if (id < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, (-1), "invalid ID")

    /* Do actual retrieve operation */
    if ((ret_value = H5I_get_ref(id, TRUE)) < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_CANTGET, (-1), "can't get ID ref count")

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

/*-------------------------------------------------------------------------
 * Function:    H5I_get_ref
 *
 * Purpose:     Retrieve the reference count for an object.
 *
 * Return:      Success:    The reference count
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
int
H5I_get_ref(hid_t id, hbool_t app_ref)
{
    H5I_id_info_t *id_ptr;        /* Pointer to the ID */
    int            ret_value = 0; /* Return value */

    FUNC_ENTER_NOAPI((-1))

    /* Sanity check */
    HDassert(id >= 0);

    /* General lookup of the ID */
    if (NULL == (id_ptr = H5I__find_id(id)))
        HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, (-1), "can't locate ID")

    /* Set return value */
    ret_value = (int)(app_ref ? id_ptr->app_count : id_ptr->count);

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

/*-------------------------------------------------------------------------
 * Function:    H5Iinc_type_ref
 *
 * Purpose:     Increments the number of references outstanding for an ID type.
 *
 * Return:      Success:    New reference count
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
int
H5Iinc_type_ref(H5I_type_t type)
{
    int ret_value; /* Return value */

    FUNC_ENTER_API((-1))
    H5TRACE1("Is", "It", type);

    /* Check arguments */
    if (type <= 0 || (int)type >= H5I_next_type)
        HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, (-1), "invalid ID type")
    if (H5I_IS_LIB_TYPE(type))
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, (-1), "cannot call public function on library type")

    /* Do actual increment operation */
    if ((ret_value = H5I__inc_type_ref(type)) < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_CANTINC, (-1), "can't increment ID type ref count")

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

/*-------------------------------------------------------------------------
 * Function:    H5I__inc_type_ref
 *
 * Purpose:     Increment the reference count for an ID type.
 *
 * Return:      Success:    The new reference count
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
static int
H5I__inc_type_ref(H5I_type_t type)
{
    H5I_id_type_t *type_ptr;       /* Pointer to the type */
    int            ret_value = -1; /* Return value */

    FUNC_ENTER_STATIC

    /* Sanity check */
    HDassert(type > 0 && (int)type < H5I_next_type);

    /* Check arguments */
    type_ptr = H5I_id_type_list_g[type];
    if (NULL == type_ptr)
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, (-1), "invalid type")

    /* Set return value */
    ret_value = (int)(++(type_ptr->init_count));

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

/*-------------------------------------------------------------------------
 * Function:    H5Idec_type_ref
 *
 * Purpose:     Decrements the reference count on an entire type of IDs.
 *              If the type reference count becomes zero then the type is
 *              destroyed along with all atoms in that type regardless of
 *              their reference counts. Destroying IDs involves calling
 *              the free-func for each ID's object and then adding the ID
 *              struct to the ID free list.  Public interface to
 *              H5I_dec_type_ref.
 *              Returns the number of references to the type on success; a
 *              return value of 0 means that the type will have to be
 *              re-initialized before it can be used again (and should probably
 *              be set to H5I_UNINIT).
 *
 * NOTE:        Using an error type to also represent a count is semantially
 *              incorrect. We should consider fixing this in a future major
 *              release (DER).
 *
 * Return:      Success:    Number of references to type
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Idec_type_ref(H5I_type_t type)
{
    herr_t ret_value = 0; /* Return value */

    FUNC_ENTER_API((-1))
    H5TRACE1("e", "It", type);

    if (H5I_IS_LIB_TYPE(type))
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, (-1), "cannot call public function on library type")

    ret_value = H5I_dec_type_ref(type);

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

/*-------------------------------------------------------------------------
 * Function:    H5I_dec_type_ref
 *
 * Purpose:     Decrements the reference count on an entire type of IDs.
 *              If the type reference count becomes zero then the type is
 *              destroyed along with all atoms in that type regardless of
 *              their reference counts. Destroying IDs involves calling
 *              the free-func for each ID's object and then adding the ID
 *              struct to the ID free list.
 *              Returns the number of references to the type on success; a
 *              return value of 0 means that the type will have to be
 *              re-initialized before it can be used again (and should probably
 *              be set to H5I_UNINIT).
 *
 * Return:      Success:    Number of references to type
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
int
H5I_dec_type_ref(H5I_type_t type)
{
    H5I_id_type_t *type_ptr;      /* Pointer to the ID type */
    herr_t         ret_value = 0; /* Return value */

    FUNC_ENTER_NOAPI((-1))

    if (type <= H5I_BADID || (int)type >= H5I_next_type)
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, (-1), "invalid type number")

    type_ptr = H5I_id_type_list_g[type];
    if (type_ptr == NULL || type_ptr->init_count <= 0)
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, (-1), "invalid type")

    /* Decrement the number of users of the atomic type.  If this is the
     * last user of the type then release all atoms from the type and
     * free all memory it used.  The free function is invoked for each atom
     * being freed.
     */
    if (1 == type_ptr->init_count) {
        H5I__destroy_type(type);
        ret_value = 0;
    } /* end if */
    else {
        --(type_ptr->init_count);
        ret_value = (herr_t)type_ptr->init_count;
    } /* end else */

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

/*-------------------------------------------------------------------------
 * Function:    H5Iget_type_ref
 *
 * Purpose:     Retrieves the number of references outstanding for a type.
 *
 * Return:      Success:    Reference count
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
int
H5Iget_type_ref(H5I_type_t type)
{
    int ret_value; /* Return value */

    FUNC_ENTER_API((-1))
    H5TRACE1("Is", "It", type);

    /* Check arguments */
    if (type <= 0 || (int)type >= H5I_next_type)
        HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, (-1), "invalid ID type")
    if (H5I_IS_LIB_TYPE(type))
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, (-1), "cannot call public function on library type")

    /* Do actual retrieve operation */
    if ((ret_value = H5I__get_type_ref(type)) < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_CANTGET, (-1), "can't get ID type ref count")

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

/*-------------------------------------------------------------------------
 * Function:    H5I__get_type_ref
 *
 * Purpose:     Retrieve the reference count for an ID type.
 *
 * Return:      Success:    The reference count
 *
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
static int
H5I__get_type_ref(H5I_type_t type)
{
    H5I_id_type_t *type_ptr;       /* Pointer to the type  */
    int            ret_value = -1; /* Return value         */

    FUNC_ENTER_STATIC

    /* Sanity check */
    HDassert(type >= 0);

    /* Check arguments */
    type_ptr = H5I_id_type_list_g[type];
    if (!type_ptr)
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, FAIL, "invalid type")

    /* Set return value */
    ret_value = (int)type_ptr->init_count;

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

/*-------------------------------------------------------------------------
 * Function:    H5Iis_valid
 *
 * Purpose:     Check if the given id is valid.  An id is valid if it is in
 *              use and has an application reference count of at least 1.
 *
 * Return:      TRUE/FALSE/FAIL
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5Iis_valid(hid_t id)
{
    H5I_id_info_t *id_ptr;           /* ptr to the ID */
    htri_t         ret_value = TRUE; /* Return value */

    FUNC_ENTER_API(FAIL)
    H5TRACE1("t", "i", id);

    /* Find the ID */
    if (NULL == (id_ptr = H5I__find_id(id)))
        ret_value = FALSE;
    else if (!id_ptr->app_count) /* Check if the found id is an internal id */
        ret_value = FALSE;

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

/*-------------------------------------------------------------------------
 * Function:    H5I__search_cb
 *
 * Purpose:     Callback routine for H5Isearch, when it calls H5I_iterate.
 *              Calls "user" callback search function, and then sets return
 *              value, based on the result of that callback.
 *
 * Return:      Success:    H5_ITER_CONT (0) or H5_ITER_STOP (1)
 *              Failure:    H5_ITER_ERROR (-1)
 *
 *-------------------------------------------------------------------------
 */
static int
H5I__search_cb(void *obj, hid_t id, void *_udata)
{
    H5I_search_ud_t *udata = (H5I_search_ud_t *)_udata; /* User data for callback */
    herr_t           cb_ret_val;                        /* User callback return value */
    int              ret_value = H5_ITER_ERROR;         /* Callback return value */

    FUNC_ENTER_STATIC_NOERR

    cb_ret_val = (*udata->app_cb)(obj, id, udata->app_key);

    /* Set the return value based on the callback's return value */
    if (cb_ret_val > 0) {
        ret_value      = H5_ITER_STOP; /* terminate iteration early */
        udata->ret_obj = obj;          /* also set out parameter */
    }
    else if (cb_ret_val < 0)
        ret_value = H5_ITER_ERROR; /* indicate failure (which terminates iteration) */

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

/*-------------------------------------------------------------------------
 * Function:    H5Isearch
 *
 * Purpose:     Apply function FUNC to each member of type TYPE and return a
 *              pointer to the first object for which FUNC returns non-zero.
 *              The FUNC should take a pointer to the object and the KEY as
 *              arguments and return non-zero to terminate the search (zero
 *              to continue).  Public interface to H5I_search.
 *
 * Limitation:  Currently there is no way to start searching from where a
 *              previous search left off.
 *
 * Return:      Success:    The first object in the type for which FUNC
 *                          returns non-zero. NULL if FUNC returned zero
 *                          for every object in the type.
 *
 *              Failure:    NULL
 *
 *-------------------------------------------------------------------------
 */
void *
H5Isearch(H5I_type_t type, H5I_search_func_t func, void *key)
{
    H5I_search_ud_t udata;     /* Context for iteration */
    void *          ret_value; /* Return value */

    FUNC_ENTER_API(NULL)
    H5TRACE3("*x", "Itx*x", type, func, key);

    /* Check arguments */
    if (H5I_IS_LIB_TYPE(type))
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, NULL, "cannot call public function on library type")

    /* Set up udata struct */
    udata.app_cb  = func;
    udata.app_key = key;
    udata.ret_obj = NULL;

    /* Note that H5I_iterate returns an error code.  We ignore it
     * here, as we can't do anything with it without revising the API.
     */
    (void)H5I_iterate(type, H5I__search_cb, &udata, TRUE);

    /* Set return value */
    ret_value = udata.ret_obj;

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

/*-------------------------------------------------------------------------
 * Function:    H5I__iterate_pub_cb
 *
 * Purpose:     Callback routine for H5Iiterate, when it calls
 *              H5I_iterate.  Calls "user" callback search function, and
 *              then sets return value, based on the result of that
 *              callback.
 *
 * Return:      Success:    H5_ITER_CONT (0) or H5_ITER_STOP (1)
 *              Failure:    H5_ITER_ERROR (-1)
 *
 * Programmer:  Neil Fortner
 *              Friday, October 11, 2013
 *
 *-------------------------------------------------------------------------
 */
static int
H5I__iterate_pub_cb(void H5_ATTR_UNUSED *obj, hid_t id, void *_udata)
{
    H5I_iterate_pub_ud_t *udata = (H5I_iterate_pub_ud_t *)_udata; /* User data for callback */
    herr_t                cb_ret_val;                             /* User callback return value */
    int                   ret_value = H5_ITER_ERROR;              /* Callback return value */

    FUNC_ENTER_STATIC_NOERR

    /* Invoke the callback */
    cb_ret_val = (*udata->op)(id, udata->op_data);

    /* Set the return value based on the callback's return value */
    if (cb_ret_val > 0)
        ret_value = H5_ITER_STOP; /* terminate iteration early */
    else if (cb_ret_val < 0)
        ret_value = H5_ITER_ERROR; /* indicate failure (which terminates iteration) */
    else
        ret_value = H5_ITER_CONT; /* continue iteration */

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

/*-------------------------------------------------------------------------
 * Function:    H5Iiterate
 *
 * Purpose:     Call the callback funciton op for each member of the id
 *              type type.  op takes as parameters the id and a
 *              passthrough of op_data, and returns an herr_t.  A positive
 *              return from op will cause the iteration to stop and
 *              H5Iiterate will return the value returned by op.  A
 *              negative return from op will cause the iteration to stop
 *              and H5Iiterate will return failure.  A zero return from op
 *              will allow iteration to continue, as long as there are
 *              other ids remaining in type.
 *
 * Limitation:  Currently there is no way to start searching from where a
 *              previous search left off.
 *
 * Return:      The last value returned by op
 *
 * Programmer:  Neil Fortner
 *              Friday, October 11, 2013
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Iiterate(H5I_type_t type, H5I_iterate_func_t op, void *op_data)
{
    H5I_iterate_pub_ud_t int_udata; /* Internal user data */
    herr_t               ret_value; /* Return value */

    FUNC_ENTER_API(FAIL)
    H5TRACE3("e", "Itx*x", type, op, op_data);

    /* Set up udata struct */
    int_udata.op      = op;
    int_udata.op_data = op_data;

    /* Note that H5I_iterate returns an error code.  We ignore it
     * here, as we can't do anything with it without revising the API.
     */
    if ((ret_value = H5I_iterate(type, H5I__iterate_pub_cb, &int_udata, TRUE)) < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_BADITER, FAIL, "can't iterate over ids")

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

/*-------------------------------------------------------------------------
 * Function:    H5I__iterate_cb
 *
 * Purpose:     Callback routine for H5I_iterate, invokes "user" callback
 *              function, and then sets return value, based on the result of
 *              that callback.
 *
 * Return:      Success:    H5_ITER_CONT (0) or H5_ITER_STOP (1)
 *              Failure:    H5_ITER_ERROR (-1)
 *
 *-------------------------------------------------------------------------
 */
static int
H5I__iterate_cb(void *_item, void H5_ATTR_UNUSED *_key, void *_udata)
{
    H5I_id_info_t *   item      = (H5I_id_info_t *)_item;     /* Pointer to the ID node */
    H5I_iterate_ud_t *udata     = (H5I_iterate_ud_t *)_udata; /* User data for callback */
    int               ret_value = H5_ITER_CONT;               /* Callback return value */

    FUNC_ENTER_STATIC_NOERR

    /* Only invoke the callback function if this ID is visible externally and
     * its reference count is positive.
     */
    if ((!udata->app_ref) || (item->app_count > 0)) {
        H5I_type_t type = udata->obj_type;
        void *     obj_ptr;
        herr_t     cb_ret_val;

        /* The stored object pointer might be an H5VL_object_t, in which
         * case we'll need to get the wrapped object struct (H5F_t *, etc.).
         */
        obj_ptr = H5I__unwrap((void *)item->obj_ptr, type);

        /* Invoke callback function */
        cb_ret_val =
            (*udata->user_func)((void *)obj_ptr, item->id, udata->user_udata); /* (Casting away const OK) */

        /* Set the return value based on the callback's return value */
        if (cb_ret_val > 0)
            ret_value = H5_ITER_STOP; /* terminate iteration early */
        else if (cb_ret_val < 0)
            ret_value = H5_ITER_ERROR; /* indicate failure (which terminates iteration) */
    }                                  /* end if */

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

/*-------------------------------------------------------------------------
 * Function:    H5I_iterate
 *
 * Purpose:     Apply function FUNC to each member of type TYPE (with
 *              non-zero application reference count if app_ref is TRUE).
 *              Stop if FUNC returns a non zero value (i.e. anything
 *              other than H5_ITER_CONT).
 *
 *              If FUNC returns a positive value (i.e. H5_ITER_STOP),
 *              return SUCCEED.
 *
 *              If FUNC returns a negative value (i.e. H5_ITER_ERROR),
 *              return FAIL.
 *
 *              The FUNC should take a pointer to the object and the
 *              udata as arguments and return non-zero to terminate
 *              siteration, and zero to continue.
 *
 * Limitation:  Currently there is no way to start the iteration from
 *              where a previous iteration left off.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5I_iterate(H5I_type_t type, H5I_search_func_t func, void *udata, hbool_t app_ref)
{
    H5I_id_type_t *type_ptr;            /* Pointer to the type  */
    herr_t         ret_value = SUCCEED; /* Return value         */

    FUNC_ENTER_NOAPI(FAIL)

    /* Check arguments */
    if (type <= H5I_BADID || (int)type >= H5I_next_type)
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "invalid type number")
    type_ptr = H5I_id_type_list_g[type];

    /* Only iterate through ID list if it is initialized and there are IDs in type */
    if (type_ptr && type_ptr->init_count > 0 && type_ptr->id_count > 0) {
        H5I_iterate_ud_t iter_udata;  /* User data for iteration callback */
        herr_t           iter_status; /* Iteration status */

        /* Set up iterator user data */
        iter_udata.user_func  = func;
        iter_udata.user_udata = udata;
        iter_udata.app_ref    = app_ref;
        iter_udata.obj_type   = type;

        /* Iterate over IDs */
        if ((iter_status = H5SL_iterate(type_ptr->ids, H5I__iterate_cb, &iter_udata)) < 0)
            HGOTO_ERROR(H5E_ATOM, H5E_BADITER, FAIL, "iteration failed")
    } /* end if */

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

/*-------------------------------------------------------------------------
 * Function:    H5I__find_id
 *
 * Purpose:     Given an object ID find the info struct that describes the
 *              object.
 *
 * Return:      Success:    A pointer to the object's info struct.
 *
 *              Failure:    NULL
 *
 *-------------------------------------------------------------------------
 */
static H5I_id_info_t *
H5I__find_id(hid_t id)
{
    H5I_type_t     type;             /*ID's type        */
    H5I_id_type_t *type_ptr;         /*ptr to the type    */
    H5I_id_info_t *ret_value = NULL; /* Return value */

    FUNC_ENTER_STATIC_NOERR

    /* Check arguments */
    type = H5I_TYPE(id);
    if (type <= H5I_BADID || (int)type >= H5I_next_type)
        HGOTO_DONE(NULL)
    type_ptr = H5I_id_type_list_g[type];
    if (!type_ptr || type_ptr->init_count <= 0)
        HGOTO_DONE(NULL)

    /* Check for same ID as we have looked up last time */
    if (type_ptr->last_info && type_ptr->last_info->id == id)
        ret_value = type_ptr->last_info;
    else {
        /* Locate the ID node for the ID */
        ret_value = (H5I_id_info_t *)H5SL_search(type_ptr->ids, &id);

        /* Remember this ID */
        type_ptr->last_info = ret_value;
    } /* end else */

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

/*-------------------------------------------------------------------------
 * Function:    H5Iget_name
 *
 * Purpose:     Gets a name of an object from its ID.
 *
 * Return:      Success:    The length of the name
 *
 *              Failure:    -1
 *
 * NOTE:        Not safe for arbitrary VOL connectors as it relies on
 *              private H5G calls.
 *
 * Comments: Public function
 *  If 'name' is non-NULL then write up to 'size' bytes into that
 *  buffer and always return the length of the entry name.
 *  Otherwise 'size' is ignored and the function does not store the name,
 *  just returning the number of characters required to store the name.
 *  If an error occurs then the buffer pointed to by 'name' (NULL or non-NULL)
 *  is unchanged and the function returns a negative value.
 *  If a zero is returned for the name's length, then there is no name
 *  associated with the ID.
 *
 *-------------------------------------------------------------------------
 */
ssize_t
H5Iget_name(hid_t id, char *name /*out*/, size_t size)
{
    H5VL_object_t *   vol_obj; /* Object of loc_id */
    H5VL_loc_params_t loc_params;
    ssize_t           ret_value; /* Return value */

    FUNC_ENTER_API((-1))
    H5TRACE3("Zs", "ixz", id, name, size);

    /* Get the object pointer */
    if (NULL == (vol_obj = H5VL_vol_object(id)))
        HGOTO_ERROR(H5E_ATOM, H5E_BADTYPE, (-1), "invalid identifier")

    /* Set location parameters */
    loc_params.type     = H5VL_OBJECT_BY_SELF;
    loc_params.obj_type = H5I_get_type(id);

    /* Retrieve object's name */
    if (H5VL_object_get(vol_obj, &loc_params, H5VL_OBJECT_GET_NAME, H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL,
                        &ret_value, name, size) < 0)
        HGOTO_ERROR(H5E_ATOM, H5E_CANTGET, (-1), "can't retrieve object name")

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

/*-------------------------------------------------------------------------
 * Function:    H5Iget_file_id
 *
 * Purpose:     Obtains the file ID given an object ID.  The user has to
 *              close this ID.
 *
 * Return:      Success:    The file ID associated with the object
 *
 *              Failure:    H5I_INVALID_HID
 *
 *-------------------------------------------------------------------------
 */
hid_t
H5Iget_file_id(hid_t obj_id)
{
    H5I_type_t type;                        /* ID type */
    hid_t      ret_value = H5I_INVALID_HID; /* Return value */

    FUNC_ENTER_API(FAIL)
    H5TRACE1("i", "i", obj_id);

    /* Get object type */
    type = H5I_TYPE(obj_id);

    /* Call internal function */
    if (H5I_FILE == type || H5I_DATATYPE == type || H5I_GROUP == type || H5I_DATASET == type ||
        H5I_ATTR == type) {
        H5VL_object_t *vol_obj; /* Object of obj_id */

        /* Get the VOL object */
        if (NULL == (vol_obj = H5VL_vol_object(obj_id)))
            HGOTO_ERROR(H5E_ATOM, H5E_BADTYPE, H5I_INVALID_HID, "invalid location identifier")

        /* Get the file ID */
        if ((ret_value = H5F_get_file_id(vol_obj, type, TRUE)) < 0)
            HGOTO_ERROR(H5E_ATOM, H5E_CANTGET, H5I_INVALID_HID, "can't retrieve file ID")
    } /* end if */
    else
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, H5I_INVALID_HID, "not an ID of a file object")

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

/*-------------------------------------------------------------------------
 * Function:    H5I__find_id_cb
 *
 * Purpose:     Callback for searching for an ID with a specific pointer
 *
 * Return:      Success:    H5_ITER_CONT (0) or H5_ITER_STOP (1)
 *              Failure:    H5_ITER_ERROR (-1)
 *
 *-------------------------------------------------------------------------
 */
static int
H5I__find_id_cb(void *_item, void H5_ATTR_UNUSED *_key, void *_udata)
{
    H5I_id_info_t *  item      = (H5I_id_info_t *)_item;    /* Pointer to the ID node */
    H5I_get_id_ud_t *udata     = (H5I_get_id_ud_t *)_udata; /* Pointer to user data */
    H5I_type_t       type      = udata->obj_type;
    const void *     obj_ptr   = NULL;
    int              ret_value = H5_ITER_CONT; /* Return value */

    FUNC_ENTER_STATIC_NOERR

    /* Sanity check */
    HDassert(item);
    HDassert(udata);

    /* Get a pointer to the VOL connector's data */
    obj_ptr = H5I__unwrap(item->obj_ptr, type);

    /* Check for a match */
    if (obj_ptr == udata->object) {
        udata->ret_id = item->id;
        ret_value     = H5_ITER_STOP;
    } /* end if */

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

/*-------------------------------------------------------------------------
 * Function:    H5I_find_id
 *
 * Purpose:     Return the ID of an object by searching through the ID list
 *              for the type.
 *
 * Return:      SUCCEED/FAIL
 *              (id will be set to H5I_INVALID_HID on errors or not found)
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5I_find_id(const void *object, H5I_type_t type, hid_t *id)
{
    H5I_id_type_t *type_ptr;            /* Pointer to the type  */
    herr_t         ret_value = SUCCEED; /* Return value         */

    FUNC_ENTER_NOAPI(FAIL)

    HDassert(id);

    *id = H5I_INVALID_HID;

    type_ptr = H5I_id_type_list_g[type];
    if (!type_ptr || type_ptr->init_count <= 0)
        HGOTO_ERROR(H5E_ATOM, H5E_BADGROUP, FAIL, "invalid type")

    /* Only iterate through ID list if it is initialized and there are IDs in type */
    if (type_ptr->init_count > 0 && type_ptr->id_count > 0) {
        H5I_get_id_ud_t udata;       /* User data */
        herr_t          iter_status; /* Iteration status */

        /* Set up iterator user data */
        udata.object   = object;
        udata.obj_type = type;
        udata.ret_id   = H5I_INVALID_HID;

        /* Iterate over IDs for the ID type */
        if ((iter_status = H5SL_iterate(type_ptr->ids, H5I__find_id_cb, &udata)) < 0)
            HGOTO_ERROR(H5E_ATOM, H5E_BADITER, FAIL, "iteration failed")

        *id = udata.ret_id;
    } /* end if */

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

/*-------------------------------------------------------------------------
 * Function:    H5I__id_dump_cb
 *
 * Purpose:     Dump the contents of an ID to stderr for debugging.
 *
 * Return:      H5_ITER_CONT (always)
 *
 *-------------------------------------------------------------------------
 */
static int
H5I__id_dump_cb(void *_item, void H5_ATTR_UNUSED *_key, void *_udata)
{
    H5I_id_info_t *item    = (H5I_id_info_t *)_item; /* Pointer to the ID node */
    H5I_type_t     type    = *(H5I_type_t *)_udata;  /* User data */
    H5G_name_t *   path    = NULL;                   /* Path to file object */
    const void *   obj_ptr = NULL;                   /* Pointer to VOL connector object */

    FUNC_ENTER_STATIC_NOERR

    HDfprintf(stderr, "         id = %" PRIdHID "\n", item->id);
    HDfprintf(stderr, "         count = %u\n", item->count);
    HDfprintf(stderr, "         obj   = 0x%8p\n", item->obj_ptr);

    /* Get the group location, so we get get the name */
    switch (type) {
        case H5I_GROUP: {
            const H5VL_object_t *vol_obj = (const H5VL_object_t *)item->obj_ptr;

            obj_ptr = H5VL_object_data(vol_obj);
            if (H5_VOL_NATIVE == vol_obj->connector->cls->value)
                path = H5G_nameof((const H5G_t *)obj_ptr);
            break;
        }

        case H5I_DATASET: {
            const H5VL_object_t *vol_obj = (const H5VL_object_t *)item->obj_ptr;

            obj_ptr = H5VL_object_data(vol_obj);
            if (H5_VOL_NATIVE == vol_obj->connector->cls->value)
                path = H5D_nameof((const H5D_t *)obj_ptr);
            break;
        }

        case H5I_DATATYPE: {
            const H5T_t *dt = (const H5T_t *)item->obj_ptr;

            obj_ptr = (void *)H5T_get_actual_type((H5T_t *)dt); /* Casting away const OK - QAK */
            path    = H5T_nameof((const H5T_t *)obj_ptr);
            break;
        }

        /* TODO: Maps will have to be added when they are supported in the
         *       native VOL connector.
         */
        case H5I_MAP:

        case H5I_UNINIT:
        case H5I_BADID:
        case H5I_FILE:
        case H5I_DATASPACE:
        case H5I_ATTR:
        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_NTYPES:
        default:
            break; /* Other types of IDs are not stored in files */
    }

    if (path) {
        if (path->user_path_r)
            HDfprintf(stderr, "                user_path = %s\n", H5RS_get_str(path->user_path_r));
        if (path->full_path_r)
            HDfprintf(stderr, "                full_path = %s\n", H5RS_get_str(path->full_path_r));
    }

    FUNC_LEAVE_NOAPI(H5_ITER_CONT)
} /* end H5I__id_dump_cb() */

/*-------------------------------------------------------------------------
 * Function:    H5I_dump_ids_for_type
 *
 * Purpose:     Dump the contents of a type to stderr for debugging.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5I_dump_ids_for_type(H5I_type_t type)
{
    H5I_id_type_t *type_ptr = NULL;

    FUNC_ENTER_NOAPI_NOERR

    HDfprintf(stderr, "Dumping ID type %d\n", (int)type);
    type_ptr = H5I_id_type_list_g[type];

    if (type_ptr) {

        /* Header */
        HDfprintf(stderr, "     init_count = %u\n", type_ptr->init_count);
        HDfprintf(stderr, "     reserved   = %u\n", type_ptr->cls->reserved);
        HDfprintf(stderr, "     id_count   = %llu\n", (unsigned long long)type_ptr->id_count);
        HDfprintf(stderr, "     nextid        = %llu\n", (unsigned long long)type_ptr->nextid);

        /* List */
        if (type_ptr->id_count > 0) {
            HDfprintf(stderr, "     List:\n");
            H5SL_iterate(type_ptr->ids, H5I__id_dump_cb, &type);
        }
    }
    else
        HDfprintf(stderr, "Global type info/tracking pointer for that type is NULL\n");

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