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

/*
 * Purpose:     The native VOL connector where access is to a single HDF5 file
 *              using HDF5 VFDs.
 */

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

#define H5VL_FRIEND /* Suppress error about including H5VLpkg   */

/***********/
/* Headers */
/***********/

#include "H5private.h"  /* Generic Functions                        */
#include "H5Aprivate.h" /* Attributes                               */
#include "H5Dprivate.h" /* Datasets                                 */
#include "H5Eprivate.h" /* Error handling                           */
#include "H5Fprivate.h" /* Files                                    */
#include "H5Gprivate.h" /* Groups                                   */
#include "H5Iprivate.h" /* IDs                                      */
#include "H5Oprivate.h" /* Object headers                           */
#include "H5Pprivate.h" /* Property lists                           */
#include "H5Tprivate.h" /* Datatypes                                */
#include "H5VLpkg.h"    /* Virtual Object Layer                     */

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

/* The VOL connector identification number */
static hid_t H5VL_NATIVE_ID_g = H5I_INVALID_HID;

/* Prototypes */
static herr_t H5VL__native_term(void);

/* Native VOL connector class struct */
static const H5VL_class_t H5VL_native_cls_g = {
    H5VL_VERSION,      /* VOL class struct version */
    H5VL_NATIVE_VALUE, /* value        */
    H5VL_NATIVE_NAME,  /* name         */
    0,                 /* capability flags */
    NULL,              /* initialize   */
    H5VL__native_term, /* terminate    */
    {
        /* info_cls */
        (size_t)0, /* info size    */
        NULL,      /* info copy    */
        NULL,      /* info compare */
        NULL,      /* info free    */
        NULL,      /* info to str  */
        NULL       /* str to info  */
    },
    {
        /* wrap_cls */
        NULL, /* get_object   */
        NULL, /* get_wrap_ctx */
        NULL, /* wrap_object  */
        NULL, /* unwrap_object */
        NULL  /* free_wrap_ctx */
    },
    {
        /* attribute_cls */
        H5VL__native_attr_create,   /* create       */
        H5VL__native_attr_open,     /* open         */
        H5VL__native_attr_read,     /* read         */
        H5VL__native_attr_write,    /* write        */
        H5VL__native_attr_get,      /* get          */
        H5VL__native_attr_specific, /* specific     */
        H5VL__native_attr_optional, /* optional     */
        H5VL__native_attr_close     /* close        */
    },
    {
        /* dataset_cls */
        H5VL__native_dataset_create,   /* create       */
        H5VL__native_dataset_open,     /* open         */
        H5VL__native_dataset_read,     /* read         */
        H5VL__native_dataset_write,    /* write        */
        H5VL__native_dataset_get,      /* get          */
        H5VL__native_dataset_specific, /* specific     */
        H5VL__native_dataset_optional, /* optional     */
        H5VL__native_dataset_close     /* close        */
    },
    {
        /* datatype_cls */
        H5VL__native_datatype_commit,   /* commit       */
        H5VL__native_datatype_open,     /* open         */
        H5VL__native_datatype_get,      /* get          */
        H5VL__native_datatype_specific, /* specific     */
        NULL,                           /* optional     */
        H5VL__native_datatype_close     /* close        */
    },
    {
        /* file_cls */
        H5VL__native_file_create,   /* create       */
        H5VL__native_file_open,     /* open         */
        H5VL__native_file_get,      /* get          */
        H5VL__native_file_specific, /* specific     */
        H5VL__native_file_optional, /* optional     */
        H5VL__native_file_close     /* close        */
    },
    {
        /* group_cls */
        H5VL__native_group_create,   /* create       */
        H5VL__native_group_open,     /* open         */
        H5VL__native_group_get,      /* get          */
        H5VL__native_group_specific, /* specific     */
        H5VL__native_group_optional, /* optional     */
        H5VL__native_group_close     /* close        */
    },
    {
        /* link_cls */
        H5VL__native_link_create,   /* create       */
        H5VL__native_link_copy,     /* copy         */
        H5VL__native_link_move,     /* move         */
        H5VL__native_link_get,      /* get          */
        H5VL__native_link_specific, /* specific     */
        NULL                        /* optional     */
    },
    {
        /* object_cls */
        H5VL__native_object_open,     /* open         */
        H5VL__native_object_copy,     /* copy         */
        H5VL__native_object_get,      /* get          */
        H5VL__native_object_specific, /* specific     */
        H5VL__native_object_optional  /* optional     */
    },
    {
        /* introspect_cls */
        H5VL__native_introspect_get_conn_cls, /* get_conn_cls */
        H5VL__native_introspect_opt_query,    /* opt_query    */
    },
    {
        /* request_cls */
        NULL, /* wait         */
        NULL, /* notify       */
        NULL, /* cancel       */
        NULL, /* specific     */
        NULL, /* optional     */
        NULL  /* free         */
    },
    {
        /* blob_cls */
        H5VL__native_blob_put,      /* put */
        H5VL__native_blob_get,      /* get */
        H5VL__native_blob_specific, /* specific */
        NULL                        /* optional */
    },
    {
        /* token_cls */
        H5VL__native_token_cmp,    /* cmp            */
        H5VL__native_token_to_str, /* to_str         */
        H5VL__native_str_to_token  /* from_str       */
    },
    NULL /* optional     */
};

/*-------------------------------------------------------------------------
 * Function:    H5VL_native_register
 *
 * Purpose:     Register the native VOL connector and retrieve an ID for it.
 *
 * Return:      Success:    The ID for the native connector
 *              Failure:    H5I_INVALID_HID
 *
 *-------------------------------------------------------------------------
 */
hid_t
H5VL_native_register(void)
{
    hid_t ret_value = H5I_INVALID_HID; /* Return value */

    FUNC_ENTER_NOAPI(H5I_INVALID_HID)

    /* Register the native VOL connector, if it isn't already */
    if (H5I_INVALID_HID == H5VL_NATIVE_ID_g)
        if ((H5VL_NATIVE_ID_g =
                 H5VL__register_connector(&H5VL_native_cls_g, TRUE, H5P_VOL_INITIALIZE_DEFAULT)) < 0)
            HGOTO_ERROR(H5E_VOL, H5E_CANTINSERT, H5I_INVALID_HID, "can't create ID for native VOL connector")

    /* Set return value */
    ret_value = H5VL_NATIVE_ID_g;

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

/*---------------------------------------------------------------------------
 * Function:    H5VL__native_term
 *
 * Purpose:     Shut down the native VOL
 *
 * Returns:     SUCCEED (Can't fail)
 *
 *---------------------------------------------------------------------------
 */
static herr_t
H5VL__native_term(void)
{
    FUNC_ENTER_STATIC_NOERR

    /* Reset VOL ID */
    H5VL_NATIVE_ID_g = H5I_INVALID_HID;

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

/*---------------------------------------------------------------------------
 * Function:    H5VL__native_introspect_get_conn_cls
 *
 * Purpose:     Query the connector class.
 *
 * Note:        This routine is in this file so that it can return the address
 *              of the staticly declared class struct.
 *
 * Returns:     SUCCEED (Can't fail)
 *
 *---------------------------------------------------------------------------
 */
herr_t
H5VL__native_introspect_get_conn_cls(void H5_ATTR_UNUSED *obj, H5VL_get_conn_lvl_t H5_ATTR_UNUSED lvl,
                                     const H5VL_class_t **conn_cls)
{
    FUNC_ENTER_PACKAGE_NOERR

    /* Sanity check */
    HDassert(conn_cls);

    /* Retrieve the native VOL connector class */
    *conn_cls = &H5VL_native_cls_g;

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

/*-------------------------------------------------------------------------
 * Function:    H5VL_native_get_file_addr_len
 *
 * Purpose:     Convenience function to get a file's address length from a
 *              location ID. Useful when you have to encode/decode addresses
 *              to/from tokens.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5VL_native_get_file_addr_len(hid_t loc_id, size_t *addr_len)
{
    H5I_type_t vol_obj_type = H5I_BADID; /* Object type of loc_id */
    void *     vol_obj      = NULL;      /* VOL Object of loc_id */
    herr_t     ret_value    = SUCCEED;   /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    /* check arguments */
    HDassert(addr_len);

    /* Get object type */
    if ((vol_obj_type = H5I_get_type(loc_id)) < 0)
        HGOTO_ERROR(H5E_VOL, H5E_BADTYPE, FAIL, "invalid location identifier")

    /* Retrieve underlying VOL object */
    if (NULL == (vol_obj = H5VL_object(loc_id)))
        HGOTO_ERROR(H5E_VOL, H5E_BADTYPE, FAIL, "invalid location identifier")

    /* Retrieve file address length */
    if (H5VL__native_get_file_addr_len(vol_obj, vol_obj_type, addr_len) < 0)
        HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "can't get file address length")

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

/*-------------------------------------------------------------------------
 * Function:    H5VL__native_get_file_addr_len
 *
 * Purpose:     Convenience function to get a file's address length from a
 *              VOL object. Useful when you have to encode/decode addresses
 *              to/from tokens.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5VL__native_get_file_addr_len(void *obj, H5I_type_t obj_type, size_t *addr_len)
{
    H5F_t *file      = NULL; /* File stuct pointer */
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_NOAPI(FAIL)

    /* check arguments */
    HDassert(obj);
    HDassert(addr_len);

    /* Retrieve file from the VOL object */
    if (H5VL_native_get_file_struct(obj, obj_type, &file) < 0)
        HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "couldn't get file from VOL object")

    /* Get the length of an address in this file */
    *addr_len = H5F_SIZEOF_ADDR(file);

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

/*-------------------------------------------------------------------------
 * Function:    H5VLnative_addr_to_token
 *
 * Purpose:     Converts a native VOL haddr_t address to an abstract VOL token.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5VLnative_addr_to_token(hid_t loc_id, haddr_t addr, H5O_token_t *token)
{
    H5I_type_t vol_obj_type = H5I_BADID; /* Object type of loc_id */
    void *     vol_obj      = NULL;      /* VOL Object of loc_id */
    herr_t     ret_value    = SUCCEED;   /* Return value         */

    FUNC_ENTER_API(FAIL)
    H5TRACE3("e", "ia*k", loc_id, addr, token);

    /* Check args */
    if (NULL == token)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "token pointer can't be NULL")

    /* Get object type */
    if ((vol_obj_type = H5I_get_type(loc_id)) < 0)
        HGOTO_ERROR(H5E_VOL, H5E_BADTYPE, FAIL, "invalid location identifier")

    /* Retrieve underlying VOL object */
    if (NULL == (vol_obj = H5VL_object(loc_id)))
        HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "can't get underlying VOL object")

#ifndef NDEBUG
    {
        H5VL_object_t *vol_obj_container;
        hbool_t        is_native_vol_obj;

        /* Get the location object */
        if (NULL == (vol_obj_container = (H5VL_object_t *)H5I_object(loc_id)))
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier")

        /* Make sure that the VOL object is a native connector object */
        if (H5VL_object_is_native(vol_obj_container, &is_native_vol_obj) < 0)
            HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL,
                        "can't determine if VOL object is native connector object")

        HDassert(is_native_vol_obj && "not a native VOL connector object");
    }
#endif

    /* Convert the haddr_t to an object token */
    if (H5VL_native_addr_to_token(vol_obj, vol_obj_type, addr, token) < 0)
        HGOTO_ERROR(H5E_VOL, H5E_CANTSERIALIZE, FAIL, "couldn't serialize haddr_t into object token")

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

/*-------------------------------------------------------------------------
 * Function:    H5VL_native_addr_to_token
 *
 * Purpose:     Converts a native VOL haddr_t address to an abstract VOL token.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5VL_native_addr_to_token(void *obj, H5I_type_t obj_type, haddr_t addr, H5O_token_t *token)
{
    uint8_t *p;
    size_t   addr_len  = 0; /* Size of haddr_t      */
    herr_t   ret_value = SUCCEED;

    FUNC_ENTER_NOAPI(FAIL)

    /* Check args */
    HDassert(obj);
    HDassert(token);

    /* Get the length of an haddr_t in the file */
    if (H5VL__native_get_file_addr_len(obj, obj_type, &addr_len) < 0)
        HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "couldn't get length of haddr_t from VOL object")

    /* Ensure that token is initialized */
    HDmemset(token, 0, sizeof(H5O_token_t));

    /* Encode token */
    p = (uint8_t *)token;
    H5F_addr_encode_len(addr_len, &p, addr);

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

/*-------------------------------------------------------------------------
 * Function:    H5VLnative_token_to_addr
 *
 * Purpose:     Converts an abstract VOL token to a native VOL haddr_t address.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5VLnative_token_to_addr(hid_t loc_id, H5O_token_t token, haddr_t *addr)
{
    H5I_type_t vol_obj_type = H5I_BADID; /* Object type of loc_id */
    void *     vol_obj      = NULL;      /* VOL Object of loc_id */
    herr_t     ret_value    = SUCCEED;   /* Return value         */

    FUNC_ENTER_API(FAIL)
    H5TRACE3("e", "ik*a", loc_id, token, addr);

    /* Check args */
    if (NULL == addr)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "addr pointer can't be NULL")

    /* Get object type */
    if ((vol_obj_type = H5I_get_type(loc_id)) < 0)
        HGOTO_ERROR(H5E_VOL, H5E_BADTYPE, FAIL, "invalid location identifier")

    /* Retrieve underlying VOL object */
    if (NULL == (vol_obj = H5VL_object(loc_id)))
        HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "can't get underlying VOL object")

#ifndef NDEBUG
    {
        H5VL_object_t *vol_obj_container;
        hbool_t        is_native_vol_obj;

        /* Get the location object */
        if (NULL == (vol_obj_container = (H5VL_object_t *)H5I_object(loc_id)))
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier")

        /* Make sure that the VOL object is a native connector object */
        if (H5VL_object_is_native(vol_obj_container, &is_native_vol_obj) < 0)
            HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL,
                        "can't determine if VOL object is native connector object")

        HDassert(is_native_vol_obj && "not a native VOL connector object");
    }
#endif

    /* Convert the object token to an haddr_t */
    if (H5VL_native_token_to_addr(vol_obj, vol_obj_type, token, addr) < 0)
        HGOTO_ERROR(H5E_VOL, H5E_CANTUNSERIALIZE, FAIL, "couldn't deserialize object token into haddr_t")

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

/*-------------------------------------------------------------------------
 * Function:    H5VL_native_token_to_addr
 *
 * Purpose:     Converts an abstract VOL token to a native VOL haddr_t address.
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5VL_native_token_to_addr(void *obj, H5I_type_t obj_type, H5O_token_t token, haddr_t *addr)
{
    const uint8_t *p;
    size_t         addr_len  = 0; /* Size of haddr_t      */
    herr_t         ret_value = SUCCEED;

    FUNC_ENTER_NOAPI(FAIL)

    /* Check args */
    HDassert(obj);
    HDassert(addr);

    /* Get the length of an haddr_t in the file */
    if (H5VL__native_get_file_addr_len(obj, obj_type, &addr_len) < 0)
        HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "couldn't get length of haddr_t from VOL object")

    /* Decode token */
    p = (const uint8_t *)&token;
    H5F_addr_decode_len(addr_len, &p, addr);

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

/*---------------------------------------------------------------------------
 * Function:    H5VL_native_get_file_struct
 *
 * Purpose:     Utility routine to get file struct for an object
 *
 * Returns:     SUCCEED/FAIL
 *
 *---------------------------------------------------------------------------
 */
herr_t
H5VL_native_get_file_struct(void *obj, H5I_type_t type, H5F_t **file)
{
    H5O_loc_t *oloc      = NULL;    /* Object location for ID   */
    herr_t     ret_value = SUCCEED; /* Return value             */

    FUNC_ENTER_NOAPI(FAIL);

    *file = NULL;

    switch (type) {
        case H5I_FILE:
            *file = (H5F_t *)obj;
            break;

        case H5I_GROUP:
            oloc = H5G_oloc((H5G_t *)obj);
            break;

        case H5I_DATATYPE:
            oloc = H5T_oloc((H5T_t *)obj);
            break;

        case H5I_DATASET:
            oloc = H5D_oloc((H5D_t *)obj);
            break;

        case H5I_ATTR:
            oloc = H5A_oloc((H5A_t *)obj);
            break;

        case H5I_MAP:
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "maps not supported in native VOL connector")

        case H5I_UNINIT:
        case H5I_BADID:
        case H5I_DATASPACE:
        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:
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file or file object")
    } /* end switch */

    /* Set return value for objects (not files) */
    if (oloc)
        *file = oloc->file;

    /* Couldn't find a file struct */
    if (!*file)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "object is not associated with a file")

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