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

/*-------------------------------------------------------------------------
 *
 * Created:     H5HFtiny.c
 *              Aug 14 2006
 *              Quincey Koziol <koziol@hdfgroup.org>
 *
 * Purpose:     Routines for "tiny" objects in fractal heap
 *
 *-------------------------------------------------------------------------
 */

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

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


/***********/
/* Headers */
/***********/
#include "H5private.h"      /* Generic Functions        */
#include "H5Eprivate.h"     /* Error handling           */
#include "H5HFpkg.h"        /* Fractal heaps            */


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

/* Tiny object length information */
#define H5HF_TINY_LEN_SHORT     16          /* Max. length able to be encoded in first heap ID byte */
#define H5HF_TINY_MASK_SHORT    0x0F        /* Mask for length in first heap ID byte                */
#define H5HF_TINY_MASK_EXT      0x0FFF      /* Mask for length in two heap ID bytes                 */
#define H5HF_TINY_MASK_EXT_1    0x0F00      /* Mask for length in first byte of two heap ID bytes   */
#define H5HF_TINY_MASK_EXT_2    0x00FF      /* Mask for length in second byte of two heap ID bytes  */


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


/********************/
/* Package Typedefs */
/********************/


/********************/
/* Local Prototypes */
/********************/
static herr_t H5HF_tiny_op_real(H5HF_hdr_t *hdr, const uint8_t *id,
    H5HF_operator_t op, void *op_data);


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


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


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


/*-------------------------------------------------------------------------
 * Function:    H5HF_tiny_init
 *
 * Purpose:     Initialize information for tracking 'tiny' objects
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:  Quincey Koziol
 *              koziol@hdfgroup.org
 *              Aug 14 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5HF_tiny_init(H5HF_hdr_t *hdr)
{
    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /*
     * Check arguments.
     */
    HDassert(hdr);

    /* Compute information about 'tiny' objects for the heap */

    /* Check if tiny objects need an extra byte for their length */
    /* (account for boundary condition when length of an object would need an
     *  extra byte, but using that byte means that the extra length byte is
     *  unneccessary)
     */
    if((hdr->id_len - 1) <= H5HF_TINY_LEN_SHORT) {
        hdr->tiny_max_len = hdr->id_len - 1;
        hdr->tiny_len_extended = FALSE;
    } /* end if */
    else if((hdr->id_len - 1) == (H5HF_TINY_LEN_SHORT + 1)) {
        hdr->tiny_max_len = H5HF_TINY_LEN_SHORT;
        hdr->tiny_len_extended = FALSE;
    } /* end if */
    else {
        hdr->tiny_max_len = hdr->id_len - 2;
        hdr->tiny_len_extended = TRUE;
    } /* end else */

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


/*-------------------------------------------------------------------------
 * Function:    H5HF_tiny_insert
 *
 * Purpose:     Pack a 'tiny' object in a heap ID
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:  Quincey Koziol
 *              koziol@hdfgroup.org
 *              Aug 14 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5HF_tiny_insert(H5HF_hdr_t *hdr, size_t obj_size, const void *obj, void *_id)
{
    uint8_t *id = (uint8_t *)_id;       /* Pointer to ID buffer */
    size_t enc_obj_size;                /* Encoded object size */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI_NOINIT
#ifdef QAK
HDfprintf(stderr, "%s: obj_size = %Zu\n", FUNC, obj_size);
#endif /* QAK */

    /*
     * Check arguments.
     */
    HDassert(hdr);
    HDassert(obj_size <= hdr->tiny_max_len);
    HDassert(obj_size <= (H5HF_TINY_MASK_EXT + 1));
    HDassert(obj);
    HDassert(id);

    /* Adjust object's size for encoding it */
    enc_obj_size = obj_size - 1;

    /* Encode object into ID */
    if(!hdr->tiny_len_extended) {
        *id++ = (uint8_t)(H5HF_ID_VERS_CURR | H5HF_ID_TYPE_TINY |
                (enc_obj_size & H5HF_TINY_MASK_SHORT));
    } /* end if */
    else {
        *id++ = (uint8_t)(H5HF_ID_VERS_CURR | H5HF_ID_TYPE_TINY |
                ((enc_obj_size & H5HF_TINY_MASK_EXT_1) >> 8));
        *id++ = enc_obj_size & H5HF_TINY_MASK_EXT_2;
    } /* end else */

    HDmemcpy(id, obj, obj_size);
    HDmemset(id + obj_size, 0, (hdr->id_len - ((size_t)1 + (size_t)hdr->tiny_len_extended + obj_size)));

    /* Update statistics about heap */
    hdr->tiny_size += obj_size;
    hdr->tiny_nobjs++;

    /* Mark heap header as modified */
    if(H5HF_hdr_dirty(hdr) < 0)
        HGOTO_ERROR(H5E_HEAP, H5E_CANTDIRTY, FAIL, "can't mark heap header as dirty")

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


/*-------------------------------------------------------------------------
 * Function:    H5HF_tiny_get_obj_len
 *
 * Purpose:     Get the size of a 'tiny' object in a fractal heap
 *
 * Return:      SUCCEED (Can't fail)
 *
 * Programmer:  Quincey Koziol
 *              koziol@hdfgroup.org
 *              Aug 14 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5HF_tiny_get_obj_len(H5HF_hdr_t *hdr, const uint8_t *id, size_t *obj_len_p)
{
    size_t enc_obj_size;                /* Encoded object size */

    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /*
     * Check arguments.
     */
    HDassert(hdr);
    HDassert(id);
    HDassert(obj_len_p);

    /* Check if 'tiny' object ID is in extended form, and retrieve encoded size */
    if(!hdr->tiny_len_extended)
        enc_obj_size = *id & H5HF_TINY_MASK_SHORT;
    else
        /* (performed in this odd way to avoid compiler bug on tg-login3 with
         *  gcc 3.2.2 - QAK)
         */
        enc_obj_size = (size_t)*(id + 1) | ((size_t)(*id & H5HF_TINY_MASK_EXT_1) << 8);

    /* Set the object's length */
    *obj_len_p = enc_obj_size + 1;

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


/*-------------------------------------------------------------------------
 * Function:    H5HF_tiny_op_real
 *
 * Purpose:     Internal routine to perform operation on 'tiny' object
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:  Quincey Koziol
 *              koziol@hdfgroup.org
 *              Sep 11 2006
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5HF_tiny_op_real(H5HF_hdr_t *hdr, const uint8_t *id, H5HF_operator_t op,
    void *op_data)
{
    size_t enc_obj_size;                /* Encoded object size */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI_NOINIT

    /*
     * Check arguments.
     */
    HDassert(hdr);
    HDassert(id);
    HDassert(op);
    
    /* Get the object's encoded length */
    /* H5HF_tiny_obj_len can't fail */
    ret_value = H5HF_tiny_get_obj_len(hdr, id, &enc_obj_size);
    
    /* Advance past flag byte(s) */
    if(!hdr->tiny_len_extended)
        id++;
    else {
        /* (performed in two steps to avoid compiler bug on tg-login3 with
         *  gcc 3.2.2 - QAK)
         */
        id++; id++;
    }

    /* Call the user's 'op' callback */
    if(op(id, enc_obj_size, op_data) < 0)
        HGOTO_ERROR(H5E_HEAP, H5E_CANTOPERATE, FAIL, "application's callback failed")

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


/*-------------------------------------------------------------------------
 * Function:    H5HF_tiny_read
 *
 * Purpose:     Read a 'tiny' object from the heap
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:  Quincey Koziol
 *              koziol@hdfgroup.org
 *              Aug  8 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5HF_tiny_read(H5HF_hdr_t *hdr, const uint8_t *id, void *obj)
{
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI_NOINIT

    /*
     * Check arguments.
     */
    HDassert(hdr);
    HDassert(id);
    HDassert(obj);

    /* Call the internal 'op' routine */
    if(H5HF_tiny_op_real(hdr, id, H5HF_op_read, obj) < 0)
        HGOTO_ERROR(H5E_HEAP, H5E_CANTOPERATE, FAIL, "unable to operate on heap object")

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


/*-------------------------------------------------------------------------
 * Function:    H5HF_tiny_op
 *
 * Purpose:     Operate directly on a 'tiny' object
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:  Quincey Koziol
 *              koziol@hdfgroup.org
 *              Sept 11 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5HF_tiny_op(H5HF_hdr_t *hdr, const uint8_t *id, H5HF_operator_t op,
    void *op_data)
{
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI_NOINIT

    /*
     * Check arguments.
     */
    HDassert(hdr);
    HDassert(id);
    HDassert(op);

    /* Call the internal 'op' routine routine */
    if(H5HF_tiny_op_real(hdr, id, op, op_data) < 0)
        HGOTO_ERROR(H5E_HEAP, H5E_CANTOPERATE, FAIL, "unable to operate on heap object")

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


/*-------------------------------------------------------------------------
 * Function:    H5HF_tiny_remove
 *
 * Purpose:     Remove a 'tiny' object from the heap statistics
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:  Quincey Koziol
 *              koziol@hdfgroup.org
 *              Aug 14 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5HF_tiny_remove(H5HF_hdr_t *hdr, const uint8_t *id)
{
    size_t enc_obj_size;                /* Encoded object size */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI_NOINIT

    /*
     * Check arguments.
     */
    HDassert(hdr);
    HDassert(id);

    /* Get the object's encoded length */
    /* H5HF_tiny_obj_len can't fail */
    ret_value = H5HF_tiny_get_obj_len(hdr, id, &enc_obj_size);

    /* Update statistics about heap */
    hdr->tiny_size -= enc_obj_size;
    hdr->tiny_nobjs--;

    /* Mark heap header as modified */
    if(H5HF_hdr_dirty(hdr) < 0)
        HGOTO_ERROR(H5E_HEAP, H5E_CANTDIRTY, FAIL, "can't mark heap header as dirty")

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