/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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 files COPYING and Copyright.html.  COPYING can be found at the root   *
 * of the source code distribution tree; Copyright.html can be found at the  *
 * root level of an installed copy of the electronic HDF5 document set and   *
 * is linked from the top-level documents page.  It can also be found at     *
 * http://hdfgroup.org/HDF5/doc/Copyright.html.  If you do not have          *
 * access to either file, you may request a copy from help@hdfgroup.org.     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*-------------------------------------------------------------------------
 *
 * Created:		H5Ocache.c
 *			Sep 28 2005
 *			Quincey Koziol <koziol@ncsa.uiuc.edu>
 *
 * Purpose:		Object header metadata cache virtual functions.
 *
 *-------------------------------------------------------------------------
 */

#define H5O_PACKAGE		/*suppress error about including H5Opkg	  */

/* Interface initialization */
#define H5_INTERFACE_INIT_FUNC	H5O_cache_init_interface


#include "H5private.h"		/* Generic Functions			*/
#include "H5Eprivate.h"		/* Error handling		  	*/
#include "H5FLprivate.h"	/* Free lists                           */
#include "H5MFprivate.h"	/* File memory management		*/
#include "H5Opkg.h"             /* Object headers			*/

/* Private typedefs */

/* PRIVATE PROTOTYPES */

/* Metadata cache callbacks */
static H5O_t *H5O_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, const void *_udata1,
		       void *_udata2);
static herr_t H5O_flush(H5F_t *f, hid_t dxpl_id, hbool_t destroy, haddr_t addr, H5O_t *oh);
static herr_t H5O_clear(H5F_t *f, H5O_t *oh, hbool_t destroy);
static herr_t H5O_compute_size(const H5F_t *f, const H5O_t *oh, size_t *size_ptr);

/* H5O inherits cache-like properties from H5AC */
const H5AC_class_t H5AC_OHDR[1] = {{
    H5AC_OHDR_ID,
    (H5AC_load_func_t)H5O_load,
    (H5AC_flush_func_t)H5O_flush,
    (H5AC_dest_func_t)H5O_dest,
    (H5AC_clear_func_t)H5O_clear,
    (H5AC_size_func_t)H5O_compute_size,
}};


/*-------------------------------------------------------------------------
 * Function:	H5O_cache_init_interface
 *
 * Purpose:	Initialize the H5O interface.  (Just calls
 *                  H5O_init_iterface currently).
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Quincey Koziol
 *		Wednesday, September 28, 2005
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_cache_init_interface(void)
{
    FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5O_cache_init_interface)

    FUNC_LEAVE_NOAPI(H5O_init())
} /* end H5O_cache_init_interface() */


/*-------------------------------------------------------------------------
 * Function:	H5O_flush_msgs
 *
 * Purpose:	Flushes messages for object header.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Quincey Koziol
 *		koziol@ncsa.uiuc.edu
 *		Nov 21 2005
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_flush_msgs(H5F_t *f, hid_t dxpl_id, H5O_t *oh)
{
    uint8_t	*p;             /* Temporary pointer to encode with */
    int	id;                     /* ID of message to encode */
    H5O_mesg_t *curr_msg;       /* Pointer to current message being operated on */
    herr_t	(*encode)(H5F_t*, uint8_t*, const void*) = NULL;
    unsigned	u;              /* Local index variable */
    herr_t      ret_value = SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_flush_msgs)

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

    /* Encode any dirty messages */
    for(u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++) {
        if(curr_msg->dirty) {
            p = curr_msg->raw - H5O_SIZEOF_MSGHDR(f);

            id = curr_msg->type->id;
            UINT16ENCODE(p, id);
            HDassert(curr_msg->raw_size < H5O_MAX_SIZE);
            UINT16ENCODE(p, curr_msg->raw_size);
            *p++ = curr_msg->flags;
            *p++ = 0; /*reserved*/
            *p++ = 0; /*reserved*/
            *p++ = 0; /*reserved*/

            if(curr_msg->native) {
                HDassert(curr_msg->type->encode);

                /* allocate file space for chunks that have none yet */
                if(H5O_CONT_ID == id && !H5F_addr_defined(((H5O_cont_t *)(curr_msg->native))->addr)) {
                    H5O_cont_t *cont = (H5O_cont_t *) (curr_msg->native);
                    assert(cont->chunkno < oh->nchunks);
                    assert(!H5F_addr_defined(oh->chunk[cont->chunkno].addr));
                    cont->size = oh->chunk[cont->chunkno].size;

                    /* Free the space we'd reserved in the file to hold this chunk */
                    H5MF_free_reserved(f, (hsize_t)cont->size);

                    if (HADDR_UNDEF==(cont->addr=H5MF_alloc(f, H5FD_MEM_OHDR, dxpl_id, (hsize_t)cont->size)))
                        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate space for object header data");
                    oh->chunk[cont->chunkno].addr = cont->addr;
                } /* end if */

                /*
                 * Encode the message.  If the message is shared then we
                 * encode a Shared Object message instead of the object
                 * which is being shared.
                 */
                HDassert(curr_msg->raw >= oh->chunk[curr_msg->chunkno].image);
                HDassert(curr_msg->raw_size == H5O_ALIGN (curr_msg->raw_size));
                HDassert(curr_msg->raw + curr_msg->raw_size <=
                       oh->chunk[curr_msg->chunkno].image + oh->chunk[curr_msg->chunkno].size);
                if(curr_msg->flags & H5O_FLAG_SHARED)
                    encode = H5O_MSG_SHARED->encode;
                else
                    encode = curr_msg->type->encode;

#ifndef NDEBUG
/* Sanity check */
if(!(curr_msg->flags & H5O_FLAG_SHARED)) {
    size_t msg_size;

    /* Check for encoded message bigger than space available */
    msg_size = curr_msg->type->raw_size(f, curr_msg->native);
    msg_size = H5O_ALIGN(msg_size);
    HDassert(msg_size <= curr_msg->raw_size);
} /* end if */
#endif /* NDEBUG */

                if((encode)(f, curr_msg->raw, curr_msg->native) < 0)
                    HGOTO_ERROR(H5E_OHDR, H5E_CANTENCODE, FAIL, "unable to encode object header message")
            } /* end if */
            curr_msg->dirty = FALSE;
            oh->chunk[curr_msg->chunkno].dirty = TRUE;
        } /* end if */
    } /* end for */

    /* Sanity check for the correct # of messages in object header */
    if(oh->nmesgs != u)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTFLUSH, FAIL, "corrupt object header - too few messages")

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


/*-------------------------------------------------------------------------
 * Function:	H5O_load
 *
 * Purpose:	Loads an object header from disk.
 *
 * Return:	Success:	Pointer to the new object header.
 *
 *		Failure:	NULL
 *
 * Programmer:	Robb Matzke
 *		matzke@llnl.gov
 *		Aug  5 1997
 *
 *-------------------------------------------------------------------------
 */
static H5O_t *
H5O_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, const void UNUSED * _udata1,
	 void UNUSED * _udata2)
{
    H5O_t	*oh = NULL;
    H5O_t	*ret_value;
    uint8_t	buf[16], *p;
    size_t	mesg_size;
    size_t	hdr_size;
    unsigned	id;
    int	mesgno;
    unsigned	curmesg = 0, nmesgs;
    unsigned	chunkno;
    unsigned    skipped_msgs = 0;       /* Number of unknown messages skipped */
    unsigned    merged_null_msgs = 0;   /* Number of null messages merged together */
    haddr_t	chunk_addr;
    size_t	chunk_size;
    uint8_t	flags;

    FUNC_ENTER_NOAPI(H5O_load, NULL)

    /* check args */
    HDassert(f);
    HDassert(H5F_addr_defined(addr));
    HDassert(!_udata1);
    HDassert(!_udata2);

    /* allocate ohdr and init chunk list */
    if (NULL==(oh = H5FL_CALLOC(H5O_t)))
	HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");

    /* read fixed-lenth part of object header */
    hdr_size = H5O_SIZEOF_HDR(f);
    assert(hdr_size<=sizeof(buf));
    if (H5F_block_read(f, H5FD_MEM_OHDR, addr, hdr_size, dxpl_id, buf) < 0)
	HGOTO_ERROR(H5E_OHDR, H5E_READERROR, NULL, "unable to read object header");
    p = buf;

    /* decode version */
    oh->version = *p++;
    if (H5O_VERSION != oh->version)
	HGOTO_ERROR(H5E_OHDR, H5E_VERSION, NULL, "bad object header version number");

    /* reserved */
    p++;

    /* decode number of messages */
    UINT16DECODE(p, nmesgs);

    /* decode link count */
    UINT32DECODE(p, oh->nlink);

    /* decode first chunk info */
    chunk_addr = addr + (hsize_t)hdr_size;
    UINT32DECODE(p, chunk_size);

    /* build the message array */
    oh->alloc_nmesgs = nmesgs;
    if (NULL==(oh->mesg=H5FL_SEQ_CALLOC(H5O_mesg_t,(size_t)oh->alloc_nmesgs)))
	HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");

    /* read each chunk from disk */
    while(H5F_addr_defined(chunk_addr)) {
	/* increase chunk array size */
	if(oh->nchunks >= oh->alloc_nchunks) {
	    unsigned na = oh->alloc_nchunks + H5O_NCHUNKS;
	    H5O_chunk_t *x = H5FL_SEQ_REALLOC (H5O_chunk_t, oh->chunk, (size_t)na);

	    if(!x)
                HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
	    oh->alloc_nchunks = na;
	    oh->chunk = x;
	} /* end if */

	/* read the chunk raw data */
	chunkno = oh->nchunks++;
	oh->chunk[chunkno].dirty = FALSE;
	oh->chunk[chunkno].addr = chunk_addr;
	oh->chunk[chunkno].size = chunk_size;
	if(NULL==(oh->chunk[chunkno].image = H5FL_BLK_MALLOC(chunk_image, chunk_size)))
	    HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
	if(H5F_block_read(f, H5FD_MEM_OHDR, chunk_addr, chunk_size, dxpl_id, oh->chunk[chunkno].image) < 0)
	    HGOTO_ERROR(H5E_OHDR, H5E_READERROR, NULL, "unable to read object header data");

	/* load messages from this chunk */
	for(p = oh->chunk[chunkno].image; p < oh->chunk[chunkno].image + chunk_size; p += mesg_size) {
	    UINT16DECODE(p, id);
	    UINT16DECODE(p, mesg_size);
	    HDassert(mesg_size==H5O_ALIGN (mesg_size));
	    flags = *p++;
	    p += 3; /*reserved*/

            /* Try to detect invalidly formatted object header messages */
	    if(p + mesg_size > oh->chunk[chunkno].image + chunk_size)
		HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, NULL, "corrupt object header")

            /* Skip header messages we don't know about */
            /* (Usually from future versions of the library */
	    if(id >= NELMTS(H5O_msg_class_g) || NULL == H5O_msg_class_g[id]) {
                skipped_msgs++;
                continue;
            } /* end if */

            if((H5F_get_intent(f) & H5F_ACC_RDWR) &&
	            H5O_NULL_ID == id && oh->nmesgs > 0 &&
                    H5O_NULL_ID == oh->mesg[oh->nmesgs - 1].type->id &&
                    oh->mesg[oh->nmesgs - 1].chunkno == chunkno) {
		/* combine adjacent null messages */
		mesgno = oh->nmesgs - 1;
		oh->mesg[mesgno].raw_size += H5O_SIZEOF_MSGHDR(f) + mesg_size;
		oh->mesg[mesgno].dirty = TRUE;
                merged_null_msgs++;
	    } else {
		/* new message */
		if (oh->nmesgs >= nmesgs)
		    HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, "corrupt object header - too many messages");
		mesgno = oh->nmesgs++;
		oh->mesg[mesgno].type = H5O_msg_class_g[id];
		oh->mesg[mesgno].dirty = FALSE;
		oh->mesg[mesgno].flags = flags;
		oh->mesg[mesgno].native = NULL;
		oh->mesg[mesgno].raw = p;
		oh->mesg[mesgno].raw_size = mesg_size;
		oh->mesg[mesgno].chunkno = chunkno;
	    } /* end else */
	} /* end for */

        HDassert(p == oh->chunk[chunkno].image + chunk_size);

        /* decode next object header continuation message */
        for(chunk_addr = HADDR_UNDEF; !H5F_addr_defined(chunk_addr) && curmesg < oh->nmesgs; ++curmesg) {
            if(H5O_CONT_ID == oh->mesg[curmesg].type->id) {
                H5O_cont_t *cont;

                cont = (H5O_MSG_CONT->decode) (f, dxpl_id, oh->mesg[curmesg].raw);
                oh->mesg[curmesg].native = cont;
                chunk_addr = cont->addr;
                chunk_size = cont->size;
                cont->chunkno = oh->nchunks;	/*the next chunk to allocate */
            } /* end if */
        } /* end for */
    } /* end while */

    /* Mark the object header dirty if we've merged a message */
    if(merged_null_msgs)
	oh->cache_info.is_dirty = TRUE;

    /* Sanity check for the correct # of messages in object header */
    if((oh->nmesgs + skipped_msgs + merged_null_msgs) != nmesgs)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, "corrupt object header - too few messages")

    /* Set return value */
    ret_value = oh;

done:
    if(!ret_value && oh) {
        if(H5O_dest(f,oh) < 0)
	    HDONE_ERROR(H5E_OHDR, H5E_CANTFREE, NULL, "unable to destroy object header data")
    } /* end if */

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


/*-------------------------------------------------------------------------
 * Function:	H5O_flush
 *
 * Purpose:	Flushes (and destroys) an object header.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Robb Matzke
 *		matzke@llnl.gov
 *		Aug  5 1997
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_flush(H5F_t *f, hid_t dxpl_id, hbool_t destroy, haddr_t addr, H5O_t *oh)
{
    uint8_t	buf[16], *p;
    hbool_t combine = FALSE;    /* Whether to combine the object header prefix & the first chunk */
    unsigned	u;              /* Local index variable */
    herr_t      ret_value = SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI(H5O_flush, FAIL)

    /* check args */
    HDassert(f);
    HDassert(H5F_addr_defined(addr));
    HDassert(oh);

    /* flush */
    if(oh->cache_info.is_dirty) {
	/* Encode any dirty messages */
        if(H5O_flush_msgs(f, dxpl_id, oh) < 0)
            HGOTO_ERROR(H5E_OHDR, H5E_CANTFLUSH, FAIL, "unable to flush object header messages")

        /* Encode header prefix */
	p = buf;

	/* encode version */
	*p++ = oh->version;

	/* reserved */
	*p++ = 0;

	/* encode number of messages */
	UINT16ENCODE(p, oh->nmesgs);

	/* encode link count */
	UINT32ENCODE(p, oh->nlink);

	/* encode body size */
	UINT32ENCODE(p, oh->chunk[0].size);

	/* zero to alignment */
	HDmemset (p, 0, (size_t)(H5O_SIZEOF_HDR(f)-12));

	/* write the object header prefix */

        /* Check if we can combine the object header prefix & the first chunk into one I/O operation */
        if(oh->chunk[0].dirty && (addr + H5O_SIZEOF_HDR(f)) == oh->chunk[0].addr) {
            combine = TRUE;
        } /* end if */
        else {
            if(H5F_block_write(f, H5FD_MEM_OHDR, addr, (size_t)H5O_SIZEOF_HDR(f), dxpl_id, buf) < 0)
                HGOTO_ERROR(H5E_OHDR, H5E_WRITEERROR, FAIL, "unable to write object header hdr to disk")
        } /* end else */

	/* write each chunk to disk */
	for(u = 0; u < oh->nchunks; u++) {
	    if(oh->chunk[u].dirty) {
                HDassert(H5F_addr_defined(oh->chunk[u].addr));
                if(u == 0 && combine) {
                    /* Allocate space for the combined prefix and first chunk */
                    if((p = H5FL_BLK_MALLOC(chunk_image,(H5O_SIZEOF_HDR(f)+oh->chunk[u].size))) == NULL)
                        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed")

                    /* Copy in the prefix */
                    HDmemcpy(p, buf, (size_t)H5O_SIZEOF_HDR(f));

                    /* Copy in the first chunk */
                    HDmemcpy(p + H5O_SIZEOF_HDR(f), oh->chunk[u].image, oh->chunk[u].size);

                    /* Write the combined prefix/chunk out */
                    if(H5F_block_write(f, H5FD_MEM_OHDR, addr,
                                (H5O_SIZEOF_HDR(f) + oh->chunk[u].size), dxpl_id, p) < 0)
                        HGOTO_ERROR(H5E_OHDR, H5E_WRITEERROR, FAIL, "unable to write object header data to disk")

                    /* Release the memory for the combined prefix/chunk */
                    p = H5FL_BLK_FREE(chunk_image,p);
                } /* end if */
                else {
                    if(H5F_block_write(f, H5FD_MEM_OHDR, oh->chunk[u].addr,
                                (oh->chunk[u].size), dxpl_id, oh->chunk[u].image) < 0)
                        HGOTO_ERROR(H5E_OHDR, H5E_WRITEERROR, FAIL, "unable to write object header data to disk")
                } /* end else */
                oh->chunk[u].dirty = FALSE;
	    } /* end if */
	} /* end for */
	oh->cache_info.is_dirty = FALSE;
    } /* end if */

    if (destroy) {
        if(H5O_dest(f,oh) < 0)
	    HGOTO_ERROR(H5E_OHDR, H5E_CANTFREE, FAIL, "unable to destroy object header data")
    } /* end if */

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


/*-------------------------------------------------------------------------
 * Function:	H5O_dest
 *
 * Purpose:	Destroys an object header.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Quincey Koziol
 *		koziol@ncsa.uiuc.edu
 *		Jan 15 2003
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_dest(H5F_t UNUSED *f, H5O_t *oh)
{
    unsigned	i;

    FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5O_dest);

    /* check args */
    assert(oh);

    /* Verify that node is clean */
    assert (oh->cache_info.is_dirty==FALSE);

    /* destroy chunks */
    for (i = 0; i < oh->nchunks; i++) {
        /* Verify that chunk is clean */
        assert (oh->chunk[i].dirty==0);

        oh->chunk[i].image = H5FL_BLK_FREE(chunk_image,oh->chunk[i].image);
    }
    if(oh->chunk)
        oh->chunk = H5FL_SEQ_FREE(H5O_chunk_t,oh->chunk);

    /* destroy messages */
    for (i = 0; i < oh->nmesgs; i++) {
        /* Verify that message is clean */
        assert (oh->mesg[i].dirty==0);

        H5O_free_mesg(&oh->mesg[i]);
    }
    if(oh->mesg)
        oh->mesg = H5FL_SEQ_FREE(H5O_mesg_t,oh->mesg);

    /* destroy object header */
    H5FL_FREE(H5O_t,oh);

    FUNC_LEAVE_NOAPI(SUCCEED);
} /* end H5O_dest() */


/*-------------------------------------------------------------------------
 * Function:	H5O_clear
 *
 * Purpose:	Mark a object header in memory as non-dirty.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Quincey Koziol
 *		koziol@ncsa.uiuc.edu
 *		Mar 20 2003
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_clear(H5F_t *f, H5O_t *oh, hbool_t destroy)
{
    unsigned	u;      /* Local index variable */
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_NOAPI_NOINIT(H5O_clear);

    /* check args */
    assert(oh);

    /* Mark chunks as clean */
    for (u = 0; u < oh->nchunks; u++)
        oh->chunk[u].dirty=FALSE;

    /* Mark messages as clean */
    for (u = 0; u < oh->nmesgs; u++)
        oh->mesg[u].dirty=FALSE;

    /* Mark whole header as clean */
    oh->cache_info.is_dirty=FALSE;

    if (destroy)
        if (H5O_dest(f, oh) < 0)
            HGOTO_ERROR(H5E_OHDR, H5E_CANTFREE, FAIL, "unable to destroy object header data");

done:
    FUNC_LEAVE_NOAPI(ret_value);
} /* end H5O_clear() */


/*-------------------------------------------------------------------------
 * Function:	H5O_compute_size
 *
 * Purpose:	Compute the size in bytes of the specified instance of
 *              H5O_t on disk, and return it in *len_ptr.  On failure,
 *              the value of *len_ptr is undefined.
 *
 *		The value returned will probably be low unless the object
 *		has just been flushed, as we simply total up the size of
 *		the header with the sizes of the chunks.  Thus any message
 *		that has been added since the last flush will not be
 *		reflected in the total.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	John Mainzer
 *		5/13/04
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_compute_size(const H5F_t *f, const H5O_t *oh, size_t *size_ptr)
{
    unsigned	u;
    size_t	size;

    FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5O_compute_size);

    /* check args */
    HDassert(f);
    HDassert(oh);
    HDassert(size_ptr);

    size = H5O_SIZEOF_HDR(f);

    for (u = 0; u < oh->nchunks; u++)
        size += oh->chunk[u].size;

    HDassert(size >= H5O_SIZEOF_HDR(f));

    *size_ptr = size;

    FUNC_LEAVE_NOAPI(SUCCEED);
} /* H5O_compute_size() */



syntax highlighted by Code2HTML, v. 0.9.1