/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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:		H5Ochunk.c
 *			Jul 13 2008
 *			Quincey Koziol <koziol@hdfgroup.org>
 *
 * Purpose:		Object header chunk routines.
 *
 *-------------------------------------------------------------------------
 */

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

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


/***********/
/* Headers */
/***********/
#include "H5private.h"		/* Generic Functions			*/
#include "H5Eprivate.h"		/* Error handling		  	*/
#include "H5Opkg.h"             /* Object headers			*/


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


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


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


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


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

/* Declare the free list for H5O_chunk_proxy_t's */
H5FL_DEFINE(H5O_chunk_proxy_t);


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


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



/*-------------------------------------------------------------------------
 * Function:	H5O_chunk_add
 *
 * Purpose:	Add new chunk for object header to metadata cache
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 * Programmer:	Quincey Koziol
 *		koziol@hdfgroup.org
 *		Jul 13 2008
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_chunk_add(H5F_t *f, hid_t dxpl_id, H5O_t *oh, unsigned idx,
    unsigned cont_chunkno)
{
    H5O_chunk_proxy_t *chk_proxy = NULL;        /* Proxy for chunk, to mark it dirty in the cache */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI_TAG(dxpl_id, oh->cache_info.addr, FAIL)

    /* check args */
    HDassert(f);
    HDassert(oh);
    HDassert(idx < oh->nchunks);
    HDassert(idx > 0);

    /* Allocate space for the object header data structure */
    if(NULL == (chk_proxy = H5FL_CALLOC(H5O_chunk_proxy_t)))
	HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed")

    /* Increment reference count on object header */
    if(H5O_inc_rc(oh) < 0)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINC, FAIL, "can't increment reference count on object header")

    /* Set the values in the chunk proxy */
    chk_proxy->f = f;
    chk_proxy->oh = oh;
    chk_proxy->chunkno = idx;
    chk_proxy->cont_chunkno = cont_chunkno;

    /* Insert the chunk proxy into the cache */
    if(H5AC_insert_entry(f, dxpl_id, H5AC_OHDR_CHK, oh->chunk[idx].addr, chk_proxy, H5AC__NO_FLAGS_SET) < 0)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "unable to cache object header chunk")

    chk_proxy = NULL;

done:
    if(ret_value < 0)
        if(chk_proxy && H5O__chunk_dest(chk_proxy) < 0)
            HDONE_ERROR(H5E_OHDR, H5E_CANTRELEASE, FAIL, "unable to destroy object header chunk")

    FUNC_LEAVE_NOAPI_TAG(ret_value, FAIL)
} /* end H5O_chunk_add() */


/*-------------------------------------------------------------------------
 * Function:	H5O_chunk_protect
 *
 * Purpose:	Protect an object header chunk for modifications
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 * Programmer:	Quincey Koziol
 *		koziol@hdfgroup.org
 *		Jul 17 2008
 *
 *-------------------------------------------------------------------------
 */
H5O_chunk_proxy_t *
H5O_chunk_protect(H5F_t *f, hid_t dxpl_id, H5O_t *oh, unsigned idx)
{
    H5O_chunk_proxy_t *chk_proxy = NULL;        /* Proxy for protected chunk */
    H5O_chunk_proxy_t *ret_value = NULL;        /* Return value */

    FUNC_ENTER_NOAPI_TAG(dxpl_id, oh->cache_info.addr, NULL)

    /* check args */
    HDassert(f);
    HDassert(oh);
    HDassert(idx < oh->nchunks);

    /* Check for protecting first chunk */
    if(0 == idx) {
        /* Create new "fake" chunk proxy for first chunk */
        /* (since the first chunk is already handled by the H5O_t object) */
        if(NULL == (chk_proxy = H5FL_CALLOC(H5O_chunk_proxy_t)))
            HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, NULL, "memory allocation failed")

        /* Increment reference count on object header */
        if(H5O_inc_rc(oh) < 0)
            HGOTO_ERROR(H5E_OHDR, H5E_CANTINC, NULL, "can't increment reference count on object header")

        /* Set chunk proxy fields */
        chk_proxy->f = f;
        chk_proxy->oh = oh;
        chk_proxy->chunkno = idx;
    } /* end if */
    else {
        H5O_chk_cache_ud_t chk_udata;       /* User data for loading chunk */

        /* Construct the user data for protecting chunk proxy */
        /* (and _not_ decoding it) */
        HDmemset(&chk_udata, 0, sizeof(chk_udata));
        chk_udata.oh = oh;
        chk_udata.chunkno = idx;
        chk_udata.size = oh->chunk[idx].size;

        /* Get the chunk proxy */
        if(NULL == (chk_proxy = (H5O_chunk_proxy_t *)H5AC_protect(f, dxpl_id, H5AC_OHDR_CHK, oh->chunk[idx].addr, &chk_udata, H5AC__NO_FLAGS_SET)))
            HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, NULL, "unable to load object header chunk")

        /* Sanity check */
        HDassert(chk_proxy->oh == oh);
        HDassert(chk_proxy->chunkno == idx);
    } /* end else */

    /* Set return value */
    ret_value = chk_proxy;

done:
    /* Cleanup on error */
    if(!ret_value)
        if(0 == idx && chk_proxy && H5O__chunk_dest(chk_proxy) < 0)
            HDONE_ERROR(H5E_OHDR, H5E_CANTRELEASE, NULL, "unable to destroy object header chunk")

    FUNC_LEAVE_NOAPI_TAG(ret_value, NULL)
} /* end H5O_chunk_protect() */


/*-------------------------------------------------------------------------
 * Function:	H5O_chunk_unprotect
 *
 * Purpose:	Unprotect an object header chunk after modifications
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 * Programmer:	Quincey Koziol
 *		koziol@hdfgroup.org
 *		Jul 17 2008
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_chunk_unprotect(H5F_t *f, hid_t dxpl_id, H5O_chunk_proxy_t *chk_proxy,
    hbool_t dirtied)
{
    herr_t ret_value = SUCCEED;        /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    /* check args */
    HDassert(f);
    HDassert(chk_proxy);

    /* Check for releasing first chunk */
    if(0 == chk_proxy->chunkno) {
        /* Check for dirtying the first chunk */
        if(dirtied) {
            /* Mark object header as dirty in cache */
            if(H5AC_mark_entry_dirty(chk_proxy->oh) < 0)
                HGOTO_ERROR(H5E_OHDR, H5E_CANTMARKDIRTY, FAIL, "unable to mark object header as dirty")
        } /* end else/if */

        /* Decrement reference count of object header */
        if(H5O_dec_rc(chk_proxy->oh) < 0)
            HGOTO_ERROR(H5E_OHDR, H5E_CANTDEC, FAIL, "can't decrement reference count on object header")

        /* Free fake chunk proxy */
        chk_proxy = H5FL_FREE(H5O_chunk_proxy_t, chk_proxy);
    } /* end if */
    else {
        /* Release the chunk proxy from the cache, possibly marking it dirty */
        if(H5AC_unprotect(f, dxpl_id, H5AC_OHDR_CHK, chk_proxy->oh->chunk[chk_proxy->chunkno].addr, chk_proxy, (dirtied ? H5AC__DIRTIED_FLAG : H5AC__NO_FLAGS_SET)) < 0)
            HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to release object header chunk")
    } /* end else */

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


/*-------------------------------------------------------------------------
 * Function:	H5O_chunk_resize
 *
 * Purpose:	Resize an object header chunk
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 * Programmer:	Quincey Koziol
 *		koziol@hdfgroup.org
 *		May  6 2010
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_chunk_resize(H5O_t *oh, H5O_chunk_proxy_t *chk_proxy)
{
    herr_t ret_value = SUCCEED;        /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    /* check args */
    HDassert(oh);
    HDassert(chk_proxy);

    /* Check for resizing first chunk */
    if(0 == chk_proxy->chunkno) {
        /* Resize object header in cache */
        if(H5AC_resize_entry(oh, oh->chunk[0].size) < 0)
            HGOTO_ERROR(H5E_OHDR, H5E_CANTRESIZE, FAIL, "unable to resize chunk in cache")
    } /* end if */
    else {
        /* Resize chunk in cache */
        if(H5AC_resize_entry(chk_proxy, oh->chunk[chk_proxy->chunkno].size) < 0)
            HGOTO_ERROR(H5E_OHDR, H5E_CANTRESIZE, FAIL, "unable to resize chunk in cache")
    } /* end else */

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


/*-------------------------------------------------------------------------
 * Function:	H5O_chunk_update_idx
 *
 * Purpose:	Update the chunk index for a chunk proxy
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 * Programmer:	Quincey Koziol
 *		koziol@hdfgroup.org
 *		Jul 13 2008
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_chunk_update_idx(H5F_t *f, hid_t dxpl_id, H5O_t *oh, unsigned idx)
{
    H5O_chunk_proxy_t *chk_proxy;       /* Proxy for chunk, to mark it dirty in the cache */
    H5O_chk_cache_ud_t chk_udata;       /* User data for loading chunk */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    /* check args */
    HDassert(f);
    HDassert(oh);
    HDassert(idx < oh->nchunks);
    HDassert(idx > 0);

    /* Construct the user data for protecting chunk proxy */
    /* (and _not_ decoding it) */
    HDmemset(&chk_udata, 0, sizeof(chk_udata));
    chk_udata.oh = oh;
    chk_udata.chunkno = idx;
    chk_udata.size = oh->chunk[idx].size;

    /* Get the chunk proxy */
    if(NULL == (chk_proxy = (H5O_chunk_proxy_t *)H5AC_protect(f, dxpl_id, H5AC_OHDR_CHK, oh->chunk[idx].addr, &chk_udata, H5AC__NO_FLAGS_SET)))
        HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header chunk")

    /* Update index for chunk proxy in cache */
    chk_proxy->chunkno = idx;

    /* Release the chunk proxy from the cache, marking it deleted */
    if(H5AC_unprotect(f, dxpl_id, H5AC_OHDR_CHK, oh->chunk[idx].addr, chk_proxy, H5AC__DIRTIED_FLAG) < 0)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to release object header chunk")

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


/*-------------------------------------------------------------------------
 * Function:	H5O_chunk_delete
 *
 * Purpose:	Notify metadata cache that a chunk has been deleted
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 * Programmer:	Quincey Koziol
 *		koziol@hdfgroup.org
 *		Jul 13 2008
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_chunk_delete(H5F_t *f, hid_t dxpl_id, H5O_t *oh, unsigned idx)
{
    H5O_chunk_proxy_t *chk_proxy;       /* Proxy for chunk, to mark it dirty in the cache */
    H5O_chk_cache_ud_t chk_udata;       /* User data for loading chunk */
    unsigned cache_flags = H5AC__DELETED_FLAG; /* Flags for unprotecting proxy */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI_TAG(dxpl_id, oh->cache_info.addr, FAIL)

    /* check args */
    HDassert(f);
    HDassert(oh);
    HDassert(idx < oh->nchunks);
    HDassert(idx > 0);

    /* Construct the user data for protecting chunk proxy */
    /* (and _not_ decoding it) */
    HDmemset(&chk_udata, 0, sizeof(chk_udata));
    chk_udata.oh = oh;
    chk_udata.chunkno = idx;
    chk_udata.size = oh->chunk[idx].size;

    /* Get the chunk proxy */
    if(NULL == (chk_proxy = (H5O_chunk_proxy_t *)H5AC_protect(f, dxpl_id, H5AC_OHDR_CHK, oh->chunk[idx].addr, &chk_udata, H5AC__NO_FLAGS_SET)))
        HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header chunk")

    /* Sanity check */
    HDassert(chk_proxy->oh == oh);
    HDassert(chk_proxy->chunkno == idx);

    /* Only free file space if not doing SWMR writes */
    if(!oh->swmr_write)
        cache_flags |= H5AC__DIRTIED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;

    /* Release the chunk proxy from the cache, marking it deleted */
    if(H5AC_unprotect(f, dxpl_id, H5AC_OHDR_CHK, oh->chunk[idx].addr, chk_proxy, cache_flags) < 0)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to release object header chunk")

done:
    FUNC_LEAVE_NOAPI_TAG(ret_value, FAIL)
} /* end H5O_chunk_delete() */


/*-------------------------------------------------------------------------
 * Function:	H5O__chunk_dest
 *
 * Purpose:	Destroy a chunk proxy object
 *
 * Return:	Success: SUCCEED
 *              Failure: FAIL
 *
 * Programmer:	Quincey Koziol
 *              koziol@hdfgroup.org
 *              July 13, 2008
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O__chunk_dest(H5O_chunk_proxy_t *chk_proxy)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Check arguments */
    HDassert(chk_proxy);

    /* Decrement reference count of object header */
    if(chk_proxy->oh && H5O_dec_rc(chk_proxy->oh) < 0)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTDEC, FAIL, "can't decrement reference count on object header")

    /* Release the chunk proxy object */
    chk_proxy = H5FL_FREE(H5O_chunk_proxy_t, chk_proxy);

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

