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

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

#define H5O_FRIEND		/*suppress error about including H5Opkg	  */
#include "H5SMmodule.h"         /* This source code file is part of the H5SM module */


/***********/
/* Headers */
/***********/
#include "H5private.h"		/* Generic Functions			*/
#include "H5Eprivate.h"		/* Error handling		  	*/
#include "H5Opkg.h"             /* Object Headers                       */
#include "H5SMpkg.h"            /* Shared object header messages        */


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


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

/* Udata struct for calls to H5SM_compare_cb and H5SM_compare_iter_op*/
typedef struct H5SM_compare_udata_t {
    const H5SM_mesg_key_t *key; /* Key; compare this against stored message */
    H5O_msg_crt_idx_t idx;      /* Index of the message in the OH, if applicable */
    herr_t ret;                 /* Return value; set this to result of memcmp */
} H5SM_compare_udata_t;


/********************/
/* Local Prototypes */
/********************/
static herr_t H5SM_compare_cb(const void *obj, size_t obj_len, void *udata);
static herr_t H5SM_compare_iter_op(H5O_t *oh, H5O_mesg_t *mesg, unsigned sequence,
    unsigned *oh_modified, void *udata);


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


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


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



/*-------------------------------------------------------------------------
 * Function:	H5SM_compare_cb
 *
 * Purpose:	Callback for H5HF_op, used in H5SM__message_compare below.
 *              Determines whether the search key passed in in _UDATA is
 *              equal to OBJ or not.
 *
 *              Passes back the result in _UDATA->RET
 *
 * Return:	Negative on error, non-negative on success
 *
 * Programmer:	James Laird
 *              Monday, January 8, 2007
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5SM_compare_cb(const void *obj, size_t obj_len, void *_udata)
{
    H5SM_compare_udata_t *udata = (H5SM_compare_udata_t *)_udata;

    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /* If the encoding sizes are different, it's not the same object */
    if(udata->key->encoding_size > obj_len)
        udata->ret = 1;
    else if(udata->key->encoding_size < obj_len)
        udata->ret = -1;
    else
        /* Sizes are the same.  Return result of memcmp */
        udata->ret = HDmemcmp(udata->key->encoding, obj, obj_len);

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


/*-------------------------------------------------------------------------
 * Function:	H5SM_compare_iter_op
 *
 * Purpose:	OH iteration callback to compare a key against a message in
 *              an OH
 *
 * Return:	0 if this is not the message we're searching for
 *              1 if this is the message we're searching for (with memcmp
 *                      result returned in udata)
 *              negative on error
 *
 * Programmer:	James Laird
 *              Wednesday, February 7, 2007
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5SM_compare_iter_op(H5O_t *oh, H5O_mesg_t *mesg/*in,out*/, unsigned sequence,
    unsigned H5_ATTR_UNUSED *oh_modified, void *_udata/*in,out*/)
{
    H5SM_compare_udata_t *udata = (H5SM_compare_udata_t *) _udata;
    herr_t ret_value = H5_ITER_CONT;

    FUNC_ENTER_NOAPI_NOINIT

    /*
     * Check arguments.
     */
    HDassert(oh);
    HDassert(mesg);
    HDassert(udata && udata->key);

    /* Check the creation index for this message */
    if(sequence == udata->idx) {
        size_t aligned_encoded_size = H5O_ALIGN_OH(oh, udata->key->encoding_size);

        /* Sanity check the message's length */
        HDassert(mesg->raw_size > 0);

        if(aligned_encoded_size > mesg->raw_size)
            udata->ret = 1;
        else if(aligned_encoded_size < mesg->raw_size)
            udata->ret = -1;
        else {
            /* Check if the message is dirty & flush it to the object header if so */
            if(mesg->dirty)
                if(H5O_msg_flush(udata->key->file, oh, mesg) < 0)
                    HGOTO_ERROR(H5E_SOHM, H5E_CANTENCODE, H5_ITER_ERROR, "unable to encode object header message")

            HDassert(udata->key->encoding_size <= mesg->raw_size);
            udata->ret = HDmemcmp(udata->key->encoding, mesg->raw, udata->key->encoding_size);
        } /* end else */

        /* Indicate that we found the message we were looking for */
        ret_value = H5_ITER_STOP;
    } /* end if */

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


/*-------------------------------------------------------------------------
 * Function:	H5SM__message_compare
 *
 * Purpose:	Determine whether the search key rec1 represents a shared
 *              message that is equal to rec2 or not, and if not, whether
 *              rec1 is "greater than" or "less than" rec2.
 *
 * Return:	0 if rec1 == rec2
 *              Negative if rec1 < rec2
 *              Positive if rec1 > rec2
 *
 * Programmer:	James Laird
 *              Monday, November 6, 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5SM__message_compare(const void *rec1, const void *rec2, int *result)
{
    const H5SM_mesg_key_t *key = (const H5SM_mesg_key_t *) rec1;
    const H5SM_sohm_t *mesg = (const H5SM_sohm_t *) rec2;
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    /* If the key has an fheap ID, we're looking for a message that's
     * already in the index; if the fheap ID matches, we've found the message
     * and can stop immediately.
     * Likewise, if the message has an OH location that is matched by the
     * message in the index, we've found the message.
     */
    if(mesg->location == H5SM_IN_HEAP && key->message.location == H5SM_IN_HEAP) {
        if(key->message.u.heap_loc.fheap_id.val == mesg->u.heap_loc.fheap_id.val) {
            *result = 0;
            HGOTO_DONE(SUCCEED);
        }
    } /* end if */
    else if(mesg->location == H5SM_IN_OH && key->message.location == H5SM_IN_OH) {
        if(key->message.u.mesg_loc.oh_addr == mesg->u.mesg_loc.oh_addr &&
           key->message.u.mesg_loc.index == mesg->u.mesg_loc.index &&
           key->message.msg_type_id == mesg->msg_type_id) {
            *result = 0;
            HGOTO_DONE(SUCCEED);
        }
    } /* end if */

    /* Compare hash values */
    if(key->message.hash > mesg->hash)
        *result = 1;
    else if(key->message.hash < mesg->hash)
        *result = -1;
    /* If the hash values match, make sure the messages are really the same */
    else {
        /* Hash values match; compare the encoded message with the one in
         * the index.
         */
        H5SM_compare_udata_t udata;

        HDassert(key->message.hash == mesg->hash);
        HDassert(key->encoding_size > 0 && key->encoding);

        /* Set up user data for callback */
        udata.key = key;

        /* Compare the encoded message with either the message in the heap or
         * the message in an object header.
         */
        if(mesg->location == H5SM_IN_HEAP) {
            /* Call heap op routine with comparison callback */
            if(H5HF_op(key->fheap, &(mesg->u.heap_loc.fheap_id), H5SM_compare_cb, &udata) < 0)
                HGOTO_ERROR(H5E_HEAP, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
        } /* end if */
        else {
            H5O_loc_t oloc;             /* Object owning the message */
            H5O_mesg_operator_t op;             /* Message operator */

            /* Sanity checks */
            HDassert(key->file);
            HDassert(mesg->location == H5SM_IN_OH);

            /* Reset the object location */
            if(H5O_loc_reset(&oloc) < 0)
                HGOTO_ERROR(H5E_SYM, H5E_CANTRESET, FAIL, "unable to initialize target location")

            /* Set up object location */
            oloc.file = key->file;
            oloc.addr = mesg->u.mesg_loc.oh_addr;

            /* Finish setting up user data for iterator */
            udata.idx = mesg->u.mesg_loc.index;

            /* Locate the right message and compare with it */
            op.op_type = H5O_MESG_OP_LIB;
            op.u.lib_op = H5SM_compare_iter_op;
            if(H5O_msg_iterate(&oloc, mesg->msg_type_id, &op, &udata) < 0)
                HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "error iterating over links")
        } /* end else */

        *result = udata.ret;
    } /* end if */

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


/*-------------------------------------------------------------------------
 * Function:	H5SM__message_encode
 *
 * Purpose:	Serialize a H5SM_sohm_t struct into a buffer RAW.
 *
 * Return:	Non-negative on success
 *              Negative on failure
 *
 * Programmer:	James Laird
 *              Monday, November 6, 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5SM__message_encode(uint8_t *raw, const void *_nrecord, void *_ctx)
{
    H5SM_bt2_ctx_t *ctx = (H5SM_bt2_ctx_t *)_ctx;       /* Callback context structure */
    const H5SM_sohm_t *message = (const H5SM_sohm_t *)_nrecord;

    FUNC_ENTER_PACKAGE_NOERR

    /* Sanity check */
    HDassert(ctx);

    *raw++ = message->location;
    UINT32ENCODE(raw, message->hash);

    if(message->location == H5SM_IN_HEAP) {
        UINT32ENCODE(raw, message->u.heap_loc.ref_count);
        HDmemcpy(raw, message->u.heap_loc.fheap_id.id, (size_t)H5O_FHEAP_ID_LEN);
    } /* end if */
    else {
        HDassert(message->location == H5SM_IN_OH);

        *raw++ = 0;     /* reserved (possible flags byte) */
        *raw++ = (uint8_t)message->msg_type_id;
        UINT16ENCODE(raw, message->u.mesg_loc.index);
        H5F_addr_encode_len((size_t)ctx->sizeof_addr, &raw, message->u.mesg_loc.oh_addr);
    } /* end else */

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* end H5SM__message_encode */


/*-------------------------------------------------------------------------
 * Function:	H5SM__message_decode
 *
 * Purpose:	Read an encoded SOHM message from RAW into an H5SM_sohm_t struct.
 *
 * Return:	Non-negative on success
 *              Negative on failure
 *
 * Programmer:	James Laird
 *              Monday, November 6, 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5SM__message_decode(const uint8_t *raw, void *_nrecord, void *_ctx)
{
    H5SM_bt2_ctx_t *ctx = (H5SM_bt2_ctx_t *)_ctx;       /* Callback context structure */
    H5SM_sohm_t *message = (H5SM_sohm_t *)_nrecord;

    FUNC_ENTER_PACKAGE_NOERR

    message->location = (H5SM_storage_loc_t)*raw++;
    UINT32DECODE(raw, message->hash);

    if(message->location == H5SM_IN_HEAP) {
        UINT32DECODE(raw, message->u.heap_loc.ref_count);
        HDmemcpy(message->u.heap_loc.fheap_id.id, raw, (size_t)H5O_FHEAP_ID_LEN);
    } /* end if */
    else {
        HDassert(message->location == H5SM_IN_OH);

        raw++;          /* reserved */
        message->msg_type_id = *raw++;
        UINT16DECODE(raw, message->u.mesg_loc.index);
        H5F_addr_decode_len((size_t)ctx->sizeof_addr, &raw, &message->u.mesg_loc.oh_addr);
    } /* end else */

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* end H5SM__message_decode */

