/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html.  If you do not have     *
 * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*-------------------------------------------------------------------------
 *
 * Created:    H5O.c
 *      Aug  5 1997
 *      Robb Matzke <matzke@llnl.gov>
 *
 * Purpose:    Object header virtual functions.
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */

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

/* Interface initialization */
#define H5_INTERFACE_INIT_FUNC  H5O_init_interface


#include "H5private.h"    /* Generic Functions      */
#include "H5ACprivate.h"  /* Metadata cache      */
#include "H5Eprivate.h"    /* Error handling        */
#include "H5Fpkg.h"             /* File access        */
#include "H5FLprivate.h"  /* Free lists                           */
#include "H5Iprivate.h"    /* IDs            */
#include "H5MFprivate.h"  /* File memory management    */
#include "H5MMprivate.h"  /* Memory management      */
#include "H5Opkg.h"             /* Object headers      */
#include "H5Pprivate.h"    /* Property lists      */

#ifdef H5_HAVE_GETTIMEOFDAY
#include <sys/time.h>
#endif /* H5_HAVE_GETTIMEOFDAY */

/* Private typedefs */

/* User data for iteration while removing a message */
typedef struct {
    H5F_t      *f;              /* Pointer to file for insertion */
    hid_t dxpl_id;              /* DXPL during iteration */
    int sequence;               /* Sequence # to search for */
    unsigned nfailed;           /* # of failed message removals */
    H5O_operator_t op;          /* Callback routine for removal operations */
    void *op_data;              /* Callback data for removal operations */
    hbool_t adj_link;           /* Whether to adjust links when removing messages */
} H5O_iter_ud1_t;

/* Typedef for "internal" iteration operations */
typedef herr_t (*H5O_operator_int_t)(H5O_mesg_t *mesg/*in,out*/, unsigned idx,
    unsigned * oh_flags_ptr, void *operator_data/*in,out*/);

/* PRIVATE PROTOTYPES */
static herr_t H5O_init(H5F_t *f, hid_t dxpl_id, size_t size_hint,
                       H5G_entry_t *ent/*out*/, haddr_t header);
static herr_t H5O_reset_real(const H5O_class_t *type, void *native);
static herr_t H5O_free_mesg(H5O_mesg_t *mesg);
static void * H5O_copy_real(const H5O_class_t *type, const void *mesg,
        void *dst);
static int H5O_count_real (H5G_entry_t *ent, const H5O_class_t *type,
        hid_t dxpl_id);
static htri_t H5O_exists_real(H5G_entry_t *ent, const H5O_class_t *type,
        int sequence, hid_t dxpl_id);
#ifdef NOT_YET
static herr_t H5O_share(H5F_t *f, hid_t dxpl_id, const H5O_class_t *type, const void *mesg,
       H5HG_t *hobj/*out*/);
#endif /* NOT_YET */
static unsigned H5O_find_in_ohdr(H5F_t *f, hid_t dxpl_id, H5O_t *oh,
                                 const H5O_class_t **type_p, int sequence);
static int H5O_modify_real(H5G_entry_t *ent, const H5O_class_t *type,
    int overwrite, unsigned flags, unsigned update_flags, const void *mesg,
    hid_t dxpl_id);
static int H5O_append_real(H5F_t *f, hid_t dxpl_id, H5O_t *oh,
    const H5O_class_t *type, unsigned flags, const void *mesg);
static herr_t H5O_remove_real(H5G_entry_t *ent, const H5O_class_t *type,
    int sequence, H5O_operator_t op, void *op_data, hbool_t adj_link, hid_t dxpl_id);
static unsigned H5O_alloc(H5F_t *f, H5O_t *oh, const H5O_class_t *type,
          size_t size);
static unsigned H5O_alloc_extend_chunk(H5F_t *f, H5O_t *oh, unsigned chunkno, size_t size);
static unsigned H5O_alloc_new_chunk(H5F_t *f, H5O_t *oh, size_t size);
static herr_t H5O_delete_oh(H5F_t *f, hid_t dxpl_id, H5O_t *oh);
static herr_t H5O_delete_mesg(H5F_t *f, hid_t dxpl_id, H5O_mesg_t *mesg,
    hbool_t adj_link);
static unsigned H5O_new_mesg(H5F_t *f, H5O_t *oh, unsigned *flags,
    const H5O_class_t *orig_type, const void *orig_mesg, H5O_shared_t *sh_mesg,
    const H5O_class_t **new_type, const void **new_mesg, hid_t dxpl_id);
static herr_t H5O_write_mesg(H5O_t *oh, unsigned idx, const H5O_class_t *type,
    const void *mesg, unsigned flags, unsigned update_flags);
static herr_t H5O_iterate_real(const H5G_entry_t *ent, const H5O_class_t *type,
    H5AC_protect_t prot, hbool_t internal, void *op, void *op_data, hid_t dxpl_id);

/* 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_dest(H5F_t *f, 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 */
static 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,
}};

/* ID to type mapping */
static const H5O_class_t *const message_type_g[] = {
    H5O_NULL,    /*0x0000 Null          */
    H5O_SDSPACE,  /*0x0001 Simple Dimensionality      */
    NULL,    /*0x0002 Data space (fiber bundle?)    */
    H5O_DTYPE,    /*0x0003 Data Type        */
    H5O_FILL,         /*0x0004 Old data storage -- fill value         */
    H5O_FILL_NEW,  /*0x0005 New Data storage -- fill value   */
    NULL,    /*0x0006 Data storage -- compact object    */
    H5O_EFL,    /*0x0007 Data storage -- external data files  */
    H5O_LAYOUT,    /*0x0008 Data Layout        */
#ifdef H5O_ENABLE_BOGUS
    H5O_BOGUS,    /*0x0009 "Bogus"        */
#else /* H5O_ENABLE_BOGUS */
    NULL,    /*0x0009 "Bogus"        */
#endif /* H5O_ENABLE_BOGUS */
    NULL,    /*0x000A Not assigned        */
    H5O_PLINE,    /*0x000B Data storage -- filter pipeline  */
    H5O_ATTR,    /*0x000C Attribute list        */
    H5O_NAME,    /*0x000D Object name        */
    H5O_MTIME,    /*0x000E Object modification date and time  */
    H5O_SHARED,    /*0x000F Shared header message      */
    H5O_CONT,    /*0x0010 Object header continuation    */
    H5O_STAB,    /*0x0011 Symbol table        */
    H5O_MTIME_NEW,  /*0x0012 New Object modification date and time  */
};

/*
 * An array of functions indexed by symbol table entry cache type
 * (H5G_type_t) that are called to retrieve constant messages cached in the
 * symbol table entry.
 */
static void *(*H5O_fast_g[H5G_NCACHED]) (const H5G_cache_t *,
           const H5O_class_t *,
           void *);

/* Declare a free list to manage the H5O_t struct */
H5FL_DEFINE_STATIC(H5O_t);

/* Declare a free list to manage the H5O_mesg_t sequence information */
H5FL_SEQ_DEFINE_STATIC(H5O_mesg_t);

/* Declare a free list to manage the H5O_chunk_t sequence information */
H5FL_SEQ_DEFINE_STATIC(H5O_chunk_t);

/* Declare a free list to manage the chunk image information */
H5FL_BLK_DEFINE_STATIC(chunk_image);

/* Declare external the free list for time_t's */
H5FL_EXTERN(time_t);

/* Declare extern the free list for H5O_cont_t's */
H5FL_EXTERN(H5O_cont_t);


/*-------------------------------------------------------------------------
 * Function:  H5O_init_interface
 *
 * Purpose:  Initialize the H5O interface.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Robb Matzke
 *    Tuesday, January  6, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_init_interface(void)
{
    FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5O_init_interface);

    /*
     * Initialize functions that decode messages from symbol table entries.
     */
    H5O_fast_g[H5G_CACHED_STAB] = H5O_stab_fast;

    FUNC_LEAVE_NOAPI(SUCCEED);
}


/*-------------------------------------------------------------------------
 * Function:  H5O_create
 *
 * Purpose:  Creates a new object header. Allocates space for it and
 *              then calls an initialization function. The object header
 *              is opened for write access and should eventually be
 *              closed by calling H5O_close().
 *
 * Return:  Success:  Non-negative, the ENT argument contains
 *        information about the object header,
 *        including its address.
 *
 *    Failure:  Negative
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug  5 1997
 *
 * Modifications:
 *
 *      Bill Wendling, 1. November 2002
 *      Separated the create function into two different functions. One
 *      which allocates space and an initialization function which
 *      does the rest of the work (initializes, caches, and opens the
 *      object header).
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_create(H5F_t *f, hid_t dxpl_id, size_t size_hint, H5G_entry_t *ent/*out*/)
{
    haddr_t  header;
    herr_t      ret_value = SUCCEED;    /* return value */

    FUNC_ENTER_NOAPI(H5O_create, FAIL);

    /* check args */
    assert(f);
    assert(ent);

    size_hint = H5O_ALIGN (MAX (H5O_MIN_SIZE, size_hint));

    /* allocate disk space for header and first chunk */
    if (HADDR_UNDEF == (header = H5MF_alloc(f, H5FD_MEM_OHDR, dxpl_id,
                                            (hsize_t)H5O_SIZEOF_HDR(f) + size_hint)))
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
                    "file allocation failed for object header header");

    /* initialize the object header */
    if (H5O_init(f, dxpl_id, size_hint, ent, header) != SUCCEED)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to initialize object header");

done:
    FUNC_LEAVE_NOAPI(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:  H5O_init
 *
 * Purpose:  Initialize a new object header, sets the link count to 0,
 *              and caches the header. The object header is opened for
 *              write access and should eventually be closed by calling
 *              H5O_close().
 *
 * Return:  Success:    SUCCEED, the ENT argument contains
 *                          information about the object header,
 *                          including its address.
 *    Failure:    FAIL
 *
 * Programmer:  Bill Wendling
 *    1, November 2002
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_init(H5F_t *f, hid_t dxpl_id, size_t size_hint, H5G_entry_t *ent/*out*/, haddr_t header)
{
    H5O_t      *oh = NULL;
    haddr_t     tmp_addr;
    herr_t      ret_value = SUCCEED;    /* return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_init);

    /* check args */
    assert(f);
    assert(ent);

    size_hint = H5O_ALIGN(MAX(H5O_MIN_SIZE, size_hint));
    ent->file = f;
    ent->header = header;

    /* allocate the object header and fill in header fields */
    if (NULL == (oh = H5FL_MALLOC(H5O_t)))
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");

    oh->cache_info.is_dirty = TRUE;
    oh->version = H5O_VERSION;
    oh->nlink = 0;

    /* create the chunk list and initialize the first chunk */
    oh->nchunks = 1;
    oh->alloc_nchunks = H5O_NCHUNKS;

    if (NULL == (oh->chunk = H5FL_SEQ_MALLOC(H5O_chunk_t, (size_t)oh->alloc_nchunks)))
        HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");

    tmp_addr = ent->header + (hsize_t)H5O_SIZEOF_HDR(f);
    oh->chunk[0].dirty = TRUE;
    oh->chunk[0].addr = tmp_addr;
    oh->chunk[0].size = size_hint;

    if (NULL == (oh->chunk[0].image = H5FL_BLK_CALLOC(chunk_image, size_hint)))
        HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");

    /* create the message list and initialize the first message */
    oh->nmesgs = 1;
    oh->alloc_nmesgs = H5O_NMESGS;

    if (NULL == (oh->mesg = H5FL_SEQ_CALLOC(H5O_mesg_t, (size_t)oh->alloc_nmesgs)))
        HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");

    oh->mesg[0].type = H5O_NULL;
    oh->mesg[0].dirty = TRUE;
    oh->mesg[0].native = NULL;
    oh->mesg[0].raw = oh->chunk[0].image + H5O_SIZEOF_MSGHDR(f);
    oh->mesg[0].raw_size = size_hint - H5O_SIZEOF_MSGHDR(f);
    oh->mesg[0].chunkno = 0;

    /* cache it */
    if (H5AC_set(f, dxpl_id, H5AC_OHDR, ent->header, oh) < 0)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to cache object header");

    /* open it */
    if (H5O_open(ent) < 0)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTOPENOBJ, FAIL, "unable to open object header");

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

    FUNC_LEAVE_NOAPI(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:  H5O_open
 *
 * Purpose:  Opens an object header which is described by the symbol table
 *    entry OBJ_ENT.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Robb Matzke
 *    Monday, January   5, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_open(const H5G_entry_t *obj_ent)
{
    herr_t ret_value=SUCCEED;   /* Return value */

    FUNC_ENTER_NOAPI(H5O_open, FAIL);

    /* Check args */
    assert(obj_ent);
    assert(obj_ent->file);

#ifdef H5O_DEBUG
    if (H5DEBUG(O))
  HDfprintf(H5DEBUG(O), "> %a\n", obj_ent->header);
#endif

    /* Increment open-lock counters */
    obj_ent->file->nopen_objs++;

done:
    FUNC_LEAVE_NOAPI(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:  H5O_close
 *
 * Purpose:  Closes an object header that was previously open.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Robb Matzke
 *    Monday, January   5, 1998
 *
 * Modifications:
 *
 *      Pedro Vicente, <pvn@ncsa.uiuc.edu> 22 Aug 2002
 *      Added `id to name' support.
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_close(H5G_entry_t *obj_ent)
{
    herr_t ret_value=SUCCEED;   /* Return value */

    FUNC_ENTER_NOAPI(H5O_close, FAIL);

    /* Check args */
    assert(obj_ent);
    assert(obj_ent->file);
    assert(obj_ent->file->nopen_objs > 0);

    /* Decrement open-lock counters */
    --obj_ent->file->nopen_objs;

#ifdef H5O_DEBUG
    if (H5DEBUG(O)) {
  if (obj_ent->file->file_id < 0 && 1==obj_ent->file->shared->nrefs) {
      HDfprintf(H5DEBUG(O), "< %a auto %lu remaining\n",
          obj_ent->header,
          (unsigned long)(obj_ent->file->nopen_objs));
  } else {
      HDfprintf(H5DEBUG(O), "< %a\n", obj_ent->header);
  }
    }
#endif

    /*
     * If the file open object count has reached the number of open mount points
     * (each of which has a group open in the file) attempt to close the file.
     */
    if(obj_ent->file->nopen_objs == obj_ent->file->mtab.nmounts) {
        /* Attempt to close down the file hierarchy */
        if(H5F_try_close(obj_ent->file) < 0)
            HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "problem attempting file close")
    } /* end if */

    /* Free the ID to name buffers */
    H5G_free_ent_name(obj_ent);

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


/*-------------------------------------------------------------------------
 * 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
 *
 * Modifications:
 *
 *  Robb Matzke, 1997-08-30
 *  Plugged memory leaks that occur during error handling.
 *
 *  Robb Matzke, 1998-01-07
 *  Able to distinguish between constant and variable messages.
 *
 *   Robb Matzke, 1999-07-28
 *  The ADDR argument is passed by value.
 *
 *  Quincey Koziol, 2002-7-180
 *  Added dxpl parameter to allow more control over I/O from metadata
 *      cache.
 *-------------------------------------------------------------------------
 */
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;
    haddr_t  chunk_addr;
    size_t  chunk_size;
    uint8_t  flags;

    FUNC_ENTER_NOAPI(H5O_load, NULL);

    /* check args */
    assert(f);
    assert(H5F_addr_defined(addr));
    assert(!_udata1);
    assert(!_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 = MAX(H5O_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;
  }

  /* 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);
      assert (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(message_type_g) || NULL == message_type_g[id])
                continue;

      if (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;
      } else {
    /* new message */
    if (oh->nmesgs >= nmesgs)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, "corrupt object header");
    mesgno = oh->nmesgs++;
    oh->mesg[mesgno].type = message_type_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;
      }
  }

        assert(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_CONT->decode) (f, dxpl_id, oh->mesg[curmesg].raw, NULL);
                oh->mesg[curmesg].native = cont;
                chunk_addr = cont->addr;
                chunk_size = cont->size;
                cont->chunkno = oh->nchunks;  /*the next chunk to allocate */
            }
        }
    }

    /* 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");
    }

    FUNC_LEAVE_NOAPI(ret_value);
}


/*-------------------------------------------------------------------------
 * 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
 *
 * Modifications:
 *
 *  Robb Matzke, 1998-01-07
 *  Handles constant vs non-constant messages.
 *
 *      rky, 1998-08-28
 *  Only p0 writes metadata to disk.
 *
 *   Robb Matzke, 1999-07-28
 *  The ADDR argument is passed by value.
 *
 *  Quincey Koziol, 2002-7-180
 *  Added dxpl parameter to allow more control over I/O from metadata
 *      cache.
 *-------------------------------------------------------------------------
 */
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;
    int  id;
    unsigned  u;
    H5O_mesg_t *curr_msg;       /* Pointer to current message being operated on */
    H5O_cont_t  *cont = NULL;
    herr_t  (*encode)(H5F_t*, uint8_t*, const void*) = NULL;
    unsigned combine=0;        /* Whether to combine the object header prefix & the first chunk */
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI(H5O_flush, FAIL);

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

    /* flush */
    if (oh->cache_info.is_dirty) {
  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=1;
        } /* 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 */

  /* encode 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);
                assert (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) {
                    assert(curr_msg->type->encode);

                    /* allocate file space for chunks that have none yet */
                    if (H5O_CONT_ID == curr_msg->type->id &&
                            !H5F_addr_defined(((H5O_cont_t *)(curr_msg->native))->addr)) {
                        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;
                    }

                    /*
                     * Encode the message.  If the message is shared then we
                     * encode a Shared Object message instead of the object
                     * which is being shared.
                     */
                    assert(curr_msg->raw >=
                       oh->chunk[curr_msg->chunkno].image);
                    assert (curr_msg->raw_size ==
                        H5O_ALIGN (curr_msg->raw_size));
                    assert(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_SHARED->encode;
                    } else {
                        encode = curr_msg->type->encode;
                    }
                    if ((encode)(f, curr_msg->raw, curr_msg->native)<0)
                        HGOTO_ERROR(H5E_OHDR, H5E_CANTENCODE, FAIL, "unable to encode object header message");
                }
                curr_msg->dirty = FALSE;
                oh->chunk[curr_msg->chunkno].dirty = TRUE;
      }
  }

  /* write each chunk to disk */
  for (u = 0; u < oh->nchunks; u++) {
      if (oh->chunk[u].dirty) {
                assert(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;
    }

    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);
}


/*-------------------------------------------------------------------------
 * 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
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static 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
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
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
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
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() */


/*-------------------------------------------------------------------------
 * Function:  H5O_reset
 *
 * Purpose:  Some message data structures have internal fields that
 *    need to be freed.  This function does that if appropriate
 *    but doesn't free NATIVE.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug 12 1997
 *
 * Modifications:
 *              Changed to use IDs for types, instead of type objects, then
 *              call "real" routine.
 *              Quincey Koziol
 *    Feb 14 2003
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_reset(unsigned type_id, void *native)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    herr_t      ret_value;              /* Return value */

    FUNC_ENTER_NOAPI(H5O_reset,FAIL);

    /* check args */
    assert(type_id<NELMTS(message_type_g));
    type=message_type_g[type_id];    /* map the type ID to the actual type object */
    assert(type);

    /* Call the "real" reset routine */
    if((ret_value=H5O_reset_real(type, native))<0)
  HGOTO_ERROR(H5E_OHDR, H5E_READERROR, FAIL, "unable to reset object header");

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


/*-------------------------------------------------------------------------
 * Function:  H5O_reset_real
 *
 * Purpose:  Some message data structures have internal fields that
 *    need to be freed.  This function does that if appropriate
 *    but doesn't free NATIVE.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug 12 1997
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_reset_real(const H5O_class_t *type, void *native)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_reset_real);

    /* check args */
    assert(type);

    if (native) {
  if (type->reset) {
      if ((type->reset) (native) < 0)
    HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "reset method failed");
  } else {
      HDmemset(native, 0, type->native_size);
  }
    }

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


/*-------------------------------------------------------------------------
 * Function:  H5O_free
 *
 * Purpose:  Similar to H5O_reset() except it also frees the message
 *    pointer.
 *
 * Return:  Success:  NULL
 *
 *    Failure:  NULL
 *
 * Programmer:  Robb Matzke
 *              Thursday, May 21, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
void *
H5O_free (unsigned type_id, void *mesg)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    void * ret_value;                   /* Return value */

    FUNC_ENTER_NOAPI(H5O_free, NULL);

    /* check args */
    assert(type_id<NELMTS(message_type_g));
    type=message_type_g[type_id];    /* map the type ID to the actual type object */
    assert(type);

    /* Call the "real" free routine */
    ret_value=H5O_free_real(type, mesg);

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


/*-------------------------------------------------------------------------
 * Function:  H5O_free_mesg
 *
 * Purpose:  Call H5O_free_real() on a message.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Quincey Koziol
 *              Tuesday, Sep  6, 2005
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_free_mesg(H5O_mesg_t *mesg)
{
    FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5O_free_mesg)

    /* check args */
    HDassert(mesg);

    /* Free any native information */
    if(mesg->flags & H5O_FLAG_SHARED)
        mesg->native = H5O_free_real(H5O_SHARED, mesg->native);
    else
        mesg->native = H5O_free_real(mesg->type, mesg->native);

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


/*-------------------------------------------------------------------------
 * Function:  H5O_free_real
 *
 * Purpose:  Similar to H5O_reset() except it also frees the message
 *    pointer.
 *
 * Return:  Success:  NULL
 *
 *    Failure:  NULL
 *
 * Programmer:  Robb Matzke
 *              Thursday, May 21, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
void *
H5O_free_real(const H5O_class_t *type, void *msg_native)
{
    FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5O_free_real)

    /* check args */
    assert(type);

    if (msg_native) {
        H5O_reset_real(type, msg_native);
        if (NULL!=(type->free))
            (type->free)(msg_native);
        else
            H5MM_xfree (msg_native);
    }

    FUNC_LEAVE_NOAPI(NULL)
} /* end H5O_free_real() */


/*-------------------------------------------------------------------------
 * Function:  H5O_copy
 *
 * Purpose:  Copies a message.  If MESG is is the null pointer then a null
 *    pointer is returned with no error.
 *
 * Return:  Success:  Ptr to the new message
 *
 *    Failure:  NULL
 *
 * Programmer:  Robb Matzke
 *              Thursday, May 21, 1998
 *
 * Modifications:
 *              Changed to use IDs for types, instead of type objects, then
 *              call "real" routine.
 *              Quincey Koziol
 *    Feb 14 2003
 *
 *-------------------------------------------------------------------------
 */
void *
H5O_copy (unsigned type_id, const void *mesg, void *dst)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    void  *ret_value;             /* Return value */

    FUNC_ENTER_NOAPI(H5O_copy, NULL);

    /* check args */
    assert(type_id<NELMTS(message_type_g));
    type=message_type_g[type_id];    /* map the type ID to the actual type object */
    assert(type);

    /* Call the "real" copy routine */
    if((ret_value=H5O_copy_real(type, mesg, dst))==NULL)
  HGOTO_ERROR(H5E_OHDR, H5E_CANTCOPY, NULL, "unable to copy object header message");

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


/*-------------------------------------------------------------------------
 * Function:  H5O_copy_real
 *
 * Purpose:  Copies a message.  If MESG is is the null pointer then a null
 *    pointer is returned with no error.
 *
 * Return:  Success:  Ptr to the new message
 *
 *    Failure:  NULL
 *
 * Programmer:  Robb Matzke
 *              Thursday, May 21, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *
H5O_copy_real (const H5O_class_t *type, const void *mesg, void *dst)
{
    void  *ret_value = NULL;

    FUNC_ENTER_NOAPI_NOINIT(H5O_copy_real);

    /* check args */
    assert (type);
    assert (type->copy);

    if (mesg) {
  if (NULL==(ret_value=(type->copy)(mesg, dst, 0)))
      HGOTO_ERROR (H5E_OHDR, H5E_CANTINIT, NULL, "unable to copy object header message");
    }

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



/*-------------------------------------------------------------------------
 * Function:  H5O_link
 *
 * Purpose:  Adjust the link count for an object header by adding
 *    ADJUST to the link count.
 *
 * Return:  Success:  New link count
 *
 *    Failure:  Negative
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug  5 1997
 *
 * Modifications:
 *
 *   Robb Matzke, 1998-08-27
 *  This function can also be used to obtain the current number of links
 *  if zero is passed for ADJUST.  If that's the case then we don't check
 *  for write access on the file.
 *
 *-------------------------------------------------------------------------
 */
int
H5O_link(const H5G_entry_t *ent, int adjust, hid_t dxpl_id)
{
    H5O_t  *oh = NULL;
    hbool_t deleted=FALSE;      /* Whether the object was deleted as a result of this action */
    int  ret_value = FAIL;

    FUNC_ENTER_NOAPI(H5O_link, FAIL);

    /* check args */
    assert(ent);
    assert(ent->file);
    assert(H5F_addr_defined(ent->header));
    if (adjust!=0 && 0==(ent->file->intent & H5F_ACC_RDWR))
  HGOTO_ERROR (H5E_OHDR, H5E_WRITEERROR, FAIL, "no write intent on file");

    /* get header */
    if (NULL == (oh = H5AC_protect(ent->file, dxpl_id, H5AC_OHDR, ent->header,
           NULL, NULL, H5AC_WRITE)))
  HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to load object header");

    /* adjust link count */
    if (adjust<0) {
  if (oh->nlink + adjust < 0)
      HGOTO_ERROR(H5E_OHDR, H5E_LINKCOUNT, FAIL, "link count would be negative");
  oh->nlink += adjust;
  oh->cache_info.is_dirty = TRUE;

        /* Check if the object should be deleted */
        if(oh->nlink==0) {
            /* Check if the object is still open by the user */
            if(H5FO_opened(ent->file,ent->header)!=NULL) {
                /* Flag the object to be deleted when it's closed */
                if(H5FO_mark(ent->file,ent->header,TRUE)<0)
                    HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "can't mark object for deletion");
            } /* end if */
            else {
                /* Delete object right now */
                if(H5O_delete_oh(ent->file,dxpl_id,oh)<0)
                    HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "can't delete object from file");

                /* Mark the object header as deleted */
                deleted=TRUE;
            } /* end else */
        } /* end if */
    } else if (adjust>0) {
        /* A new object, or one that will be deleted */
        if(oh->nlink==0) {
            /* Check if the object is current open, but marked for deletion */
            if(H5FO_marked(ent->file,ent->header)>0) {
                /* Remove "delete me" flag on the object */
                if(H5FO_mark(ent->file,ent->header,FALSE)<0)
                    HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "can't mark object for deletion");
            } /* end if */
        } /* end if */

  oh->nlink += adjust;
  oh->cache_info.is_dirty = TRUE;
    }

    /* Set return value */
    ret_value = oh->nlink;

done:
    if (oh && H5AC_unprotect(ent->file, dxpl_id, H5AC_OHDR, ent->header, oh, deleted) < 0)
  HDONE_ERROR(H5E_OHDR, H5E_PROTECT, FAIL, "unable to release object header");

    FUNC_LEAVE_NOAPI(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:  H5O_count
 *
 * Purpose:  Counts the number of messages in an object header which are a
 *    certain type.
 *
 * Return:  Success:  Number of messages of specified type.
 *
 *    Failure:  Negative
 *
 * Programmer:  Robb Matzke
 *              Tuesday, April 21, 1998
 *
 * Modifications:
 *              Changed to use IDs for types, instead of type objects, then
 *              call "real" routine.
 *              Quincey Koziol
 *    Feb 14 2003
 *
 *-------------------------------------------------------------------------
 */
int
H5O_count (H5G_entry_t *ent, unsigned type_id, hid_t dxpl_id)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    int  ret_value;                      /* Return value */

    FUNC_ENTER_NOAPI(H5O_count, FAIL);

    /* Check args */
    assert (ent);
    assert (ent->file);
    assert (H5F_addr_defined(ent->header));
    assert(type_id<NELMTS(message_type_g));
    type=message_type_g[type_id];    /* map the type ID to the actual type object */
    assert (type);

    /* Call the "real" count routine */
    if((ret_value=H5O_count_real(ent, type, dxpl_id))<0)
  HGOTO_ERROR(H5E_OHDR, H5E_CANTCOUNT, FAIL, "unable to count object header messages");

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


/*-------------------------------------------------------------------------
 * Function:  H5O_count_real
 *
 * Purpose:  Counts the number of messages in an object header which are a
 *    certain type.
 *
 * Return:  Success:  Number of messages of specified type.
 *
 *    Failure:  Negative
 *
 * Programmer:  Robb Matzke
 *              Tuesday, April 21, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int
H5O_count_real (H5G_entry_t *ent, const H5O_class_t *type, hid_t dxpl_id)
{
    H5O_t  *oh = NULL;
    int  acc;
    unsigned  u;
    int  ret_value;

    FUNC_ENTER_NOAPI(H5O_count_real, FAIL);

    /* Check args */
    assert (ent);
    assert (ent->file);
    assert (H5F_addr_defined(ent->header));
    assert (type);

    /* Load the object header */
    if (NULL == (oh = H5AC_protect(ent->file, dxpl_id, H5AC_OHDR, ent->header, NULL, NULL, H5AC_READ)))
  HGOTO_ERROR (H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to load object header");

    /* Loop over all messages, counting the ones of the type looked for */
    for (u=acc=0; u<oh->nmesgs; u++)
  if (oh->mesg[u].type==type)
            acc++;

    /* Set return value */
    ret_value=acc;

done:
    if (oh && H5AC_unprotect(ent->file, dxpl_id, H5AC_OHDR, ent->header, oh, FALSE) != SUCCEED)
  HDONE_ERROR(H5E_OHDR, H5E_PROTECT, FAIL, "unable to release object header");

    FUNC_LEAVE_NOAPI(ret_value);
} /* end H5O_count_real() */


/*-------------------------------------------------------------------------
 * Function:  H5O_exists
 *
 * Purpose:  Determines if a particular message exists in an object
 *    header without trying to decode the message.
 *
 * Return:  Success:  FALSE if the message does not exist; TRUE if
 *        th message exists.
 *
 *    Failure:  FAIL if the existence of the message could
 *        not be determined due to some error such as
 *        not being able to read the object header.
 *
 * Programmer:  Robb Matzke
 *              Monday, November  2, 1998
 *
 * Modifications:
 *              Changed to use IDs for types, instead of type objects, then
 *              call "real" routine.
 *              Quincey Koziol
 *    Feb 14 2003
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5O_exists(H5G_entry_t *ent, unsigned type_id, int sequence, hid_t dxpl_id)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    htri_t      ret_value;              /* Return value */

    FUNC_ENTER_NOAPI(H5O_exists, FAIL);

    assert(ent);
    assert(ent->file);
    assert(type_id<NELMTS(message_type_g));
    type=message_type_g[type_id];    /* map the type ID to the actual type object */
    assert(type);
    assert(sequence>=0);

    /* Call the "real" exists routine */
    if((ret_value=H5O_exists_real(ent, type, sequence, dxpl_id))<0)
  HGOTO_ERROR(H5E_OHDR, H5E_READERROR, FAIL, "unable to verify object header message");

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


/*-------------------------------------------------------------------------
 * Function:  H5O_exists_real
 *
 * Purpose:  Determines if a particular message exists in an object
 *    header without trying to decode the message.
 *
 * Return:  Success:  FALSE if the message does not exist; TRUE if
 *        th message exists.
 *
 *    Failure:  FAIL if the existence of the message could
 *        not be determined due to some error such as
 *        not being able to read the object header.
 *
 * Programmer:  Robb Matzke
 *              Monday, November  2, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static htri_t
H5O_exists_real(H5G_entry_t *ent, const H5O_class_t *type, int sequence, hid_t dxpl_id)
{
    H5O_t  *oh=NULL;
    unsigned  u;
    htri_t      ret_value;       /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_exists_real);

    assert(ent);
    assert(ent->file);
    assert(type);
    assert(sequence>=0);

    /* Load the object header */
    if (NULL == (oh = H5AC_protect(ent->file, dxpl_id, H5AC_OHDR, ent->header, NULL, NULL, H5AC_READ)))
  HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to load object header");

    /* Scan through the messages looking for the right one */
    for (u=0; u<oh->nmesgs; u++) {
  if (type->id!=oh->mesg[u].type->id)
            continue;
  if (--sequence<0)
            break;
    }

    /* Set return value */
    ret_value=(sequence<0);

done:
    if (oh && H5AC_unprotect(ent->file, dxpl_id, H5AC_OHDR, ent->header, oh, FALSE) != SUCCEED)
  HDONE_ERROR(H5E_OHDR, H5E_PROTECT, FAIL, "unable to release object header");

    FUNC_LEAVE_NOAPI(ret_value);
} /* end H5O_exists_real() */


/*-------------------------------------------------------------------------
 * Function:  H5O_read
 *
 * Purpose:  Reads a message from an object header and returns a pointer
 *    to it.  The caller will usually supply the memory through
 *    MESG and the return value will be MESG.   But if MESG is
 *    the null pointer, then this function will malloc() memory
 *    to hold the result and return its pointer instead.
 *
 * Return:  Success:  Ptr to message in native format.  The message
 *        should be freed by calling H5O_reset().  If
 *        MESG is a null pointer then the caller should
 *        also call H5MM_xfree() on the return value
 *        after calling H5O_reset().
 *
 *    Failure:  NULL
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug  6 1997
 *
 * Modifications:
 *              Changed to use IDs for types, instead of type objects, then
 *              call "real" routine.
 *              Quincey Koziol
 *    Feb 14 2003
 *
 *-------------------------------------------------------------------------
 */
void *
H5O_read(const H5G_entry_t *ent, unsigned type_id, int sequence, void *mesg, hid_t dxpl_id)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    void *ret_value;                    /* Return value */

    FUNC_ENTER_NOAPI(H5O_read, NULL);

    /* check args */
    assert(ent);
    assert(ent->file);
    assert(H5F_addr_defined(ent->header));
    assert(type_id<NELMTS(message_type_g));
    type=message_type_g[type_id];    /* map the type ID to the actual type object */
    assert(type);
    assert(sequence >= 0);

    /* Call the "real" read routine */
    if((ret_value=H5O_read_real(ent, type, sequence, mesg, dxpl_id))==NULL)
  HGOTO_ERROR(H5E_OHDR, H5E_READERROR, NULL, "unable to load object header");

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


/*-------------------------------------------------------------------------
 * Function:  H5O_read_real
 *
 * Purpose:  Reads a message from an object header and returns a pointer
 *    to it.  The caller will usually supply the memory through
 *    MESG and the return value will be MESG.   But if MESG is
 *    the null pointer, then this function will malloc() memory
 *    to hold the result and return its pointer instead.
 *
 * Return:  Success:  Ptr to message in native format.  The message
 *        should be freed by calling H5O_reset().  If
 *        MESG is a null pointer then the caller should
 *        also call H5MM_xfree() on the return value
 *        after calling H5O_reset().
 *
 *    Failure:  NULL
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug  6 1997
 *
 * Modifications:
 *      Bill Wendling, 2003-09-30
 *      Protect the object header and pass it into the H5O_find_in_ohdr
 *      function. This is done because the H5O_find_in_ohdr used to
 *      protect the ohdr, find the message, and then unprotect it. This
 *      saves time and also helps the FPHDF5 stuff, where unprotecting
 *      actually destroys the object in the cache.
 *-------------------------------------------------------------------------
 */
void *
H5O_read_real(const H5G_entry_t *ent, const H5O_class_t *type, int sequence, void *mesg, hid_t dxpl_id)
{
    H5O_t          *oh = NULL;
    int             idx;
    const H5G_cache_t    *cache = NULL;
    H5G_type_t      cache_type;
    void           *ret_value = NULL;

    FUNC_ENTER_NOAPI_NOINIT(H5O_read_real);

    /* check args */
    assert(ent);
    assert(ent->file);
    assert(H5F_addr_defined(ent->header));
    assert(type);
    assert(sequence >= 0);

    /* can we get it from the symbol table entry? */
    cache = H5G_ent_cache(ent, &cache_type);

    if (H5O_fast_g[cache_type]) {
  ret_value = (H5O_fast_g[cache_type]) (cache, type, mesg);
  if (ret_value)
      HGOTO_DONE(ret_value);
  H5E_clear(); /*don't care, try reading from header */
    }

    /* copy the message to the user-supplied buffer */
    if (NULL == (oh = H5AC_protect(ent->file, dxpl_id, H5AC_OHDR, ent->header, NULL, NULL, H5AC_READ)))
  HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, "unable to load object header");

    /* can we get it from the object header? */
    if ((idx = H5O_find_in_ohdr(ent->file, dxpl_id, oh, &type, sequence)) < 0)
  HGOTO_ERROR(H5E_OHDR, H5E_NOTFOUND, NULL, "unable to find message in object header");

    if (oh->mesg[idx].flags & H5O_FLAG_SHARED) {
  /*
   * If the message is shared then then the native pointer points to an
   * H5O_SHARED message.  We use that information to look up the real
   * message in the global heap or some other object header.
   */
  H5O_shared_t *shared;

  shared = (H5O_shared_t *)(oh->mesg[idx].native);
        ret_value=H5O_shared_read(ent->file,dxpl_id,shared,type,mesg);
    } else {
  /*
   * The message is not shared, but rather exists in the object
   * header.  The object header caches the native message (along with
   * the raw message) so we must copy the native message before
   * returning.
   */
  if (NULL==(ret_value = (type->copy) (oh->mesg[idx].native, mesg, 0)))
      HGOTO_ERROR (H5E_OHDR, H5E_CANTINIT, NULL, "unable to copy message to user space");
    }

done:
    if (oh && H5AC_unprotect(ent->file, dxpl_id, H5AC_OHDR, ent->header, oh, FALSE) < 0)
  HDONE_ERROR(H5E_OHDR, H5E_PROTECT, NULL, "unable to release object header");

    FUNC_LEAVE_NOAPI(ret_value);
} /* end H5O_read_real() */


/*-------------------------------------------------------------------------
 * Function:  H5O_find_in_ohdr
 *
 * Purpose:     Find a message in the object header without consulting
 *              a symbol table entry.
 *
 * Return:      Success:    Index number of message.
 *              Failure:    Negative
 *
 * Programmer:  Robb Matzke
 *              matzke@llnl.gov
 *              Aug  6 1997
 *
 * Modifications:
 *      Robb Matzke, 1999-07-28
 *      The ADDR argument is passed by value.
 *
 *      Bill Wendling, 2003-09-30
 *      Modified so that the object header needs to be AC_protected
 *      before calling this function.
 *-------------------------------------------------------------------------
 */
static unsigned
H5O_find_in_ohdr(H5F_t *f, hid_t dxpl_id, H5O_t *oh, const H5O_class_t **type_p, int sequence)
{
    unsigned    u;
    const H5O_class_t  *type = NULL;
    unsigned    ret_value;

    FUNC_ENTER_NOAPI_NOINIT(H5O_find_in_ohdr);

    /* Check args */
    assert(f);
    assert(oh);
    assert(type_p);

    /* Scan through the messages looking for the right one */
    for (u = 0; u < oh->nmesgs; u++) {
  if (*type_p && (*type_p)->id != oh->mesg[u].type->id)
            continue;
  if (--sequence < 0)
            break;
    }

    if (sequence >= 0)
  HGOTO_ERROR(H5E_OHDR, H5E_NOTFOUND, UFAIL, "unable to find object header message");

    /*
     * Decode the message if necessary.  If the message is shared then decode
     * a shared message, ignoring the message type.
     */
    if (oh->mesg[u].flags & H5O_FLAG_SHARED)
  type = H5O_SHARED;
    else
  type = oh->mesg[u].type;

    if (NULL == oh->mesg[u].native) {
  assert(type->decode);
  oh->mesg[u].native = (type->decode) (f, dxpl_id, oh->mesg[u].raw, NULL);
  if (NULL == oh->mesg[u].native)
      HGOTO_ERROR(H5E_OHDR, H5E_CANTDECODE, UFAIL, "unable to decode message");
    }

    /*
     * Return the message type. If this is a shared message then return the
     * pointed-to type.
     */
    *type_p = oh->mesg[u].type;

    /* Set return value */
    ret_value=u;

done:
    FUNC_LEAVE_NOAPI(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:  H5O_modify
 *
 * Purpose:  Modifies an existing message or creates a new message.
 *    The cache fields in that symbol table entry ENT are *not*
 *    updated, you must do that separately because they often
 *    depend on multiple object header messages.  Besides, we
 *    don't know which messages will be constant and which will
 *    not.
 *
 *    The OVERWRITE argument is either a sequence number of a
 *    message to overwrite (usually zero) or the constant
 *    H5O_NEW_MESG (-1) to indicate that a new message is to
 *    be created.  If the message to overwrite doesn't exist then
 *    it is created (but only if it can be inserted so its sequence
 *    number is OVERWRITE; that is, you can create a message with
 *    the sequence number 5 if there is no message with sequence
 *    number 4).
 *
 *              The UPDATE_TIME argument is a boolean that allows the caller
 *              to skip updating the modification time.  This is useful when
 *              several calls to H5O_modify will be made in a sequence.
 *
 * Return:  Success:  The sequence number of the message that
 *        was modified or created.
 *
 *    Failure:  Negative
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug  6 1997
 *
 * Modifications:
 *
 *  Robb Matzke, 7 Jan 1998
 *  Handles constant vs non-constant messages.  Once a message is made
 *  constant it can never become non-constant.  Constant messages cannot
 *  be modified.
 *
 *      Changed to use IDs for types, instead of type objects, then
 *      call "real" routine.
 *      Quincey Koziol
 *  Feb 14 2003
 *
 *-------------------------------------------------------------------------
 */
int
H5O_modify(H5G_entry_t *ent, unsigned type_id, int overwrite,
   unsigned flags, unsigned update_flags, const void *mesg, hid_t dxpl_id)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    int  ret_value;              /* Return value */

    FUNC_ENTER_NOAPI(H5O_modify, FAIL);

    /* check args */
    assert(ent);
    assert(ent->file);
    assert(H5F_addr_defined(ent->header));
    assert(type_id<NELMTS(message_type_g));
    type=message_type_g[type_id];    /* map the type ID to the actual type object */
    assert(type);
    assert(mesg);
    assert (0==(flags & ~H5O_FLAG_BITS));

    /* Call the "real" modify routine */
    if((ret_value= H5O_modify_real(ent, type, overwrite, flags, update_flags, mesg, dxpl_id))<0)
  HGOTO_ERROR(H5E_OHDR, H5E_WRITEERROR, FAIL, "unable to write object header");

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


/*-------------------------------------------------------------------------
 * Function:  H5O_modify_real
 *
 * Purpose:  Modifies an existing message or creates a new message.
 *    The cache fields in that symbol table entry ENT are *not*
 *    updated, you must do that separately because they often
 *    depend on multiple object header messages.  Besides, we
 *    don't know which messages will be constant and which will
 *    not.
 *
 *    The OVERWRITE argument is either a sequence number of a
 *    message to overwrite (usually zero) or the constant
 *    H5O_NEW_MESG (-1) to indicate that a new message is to
 *    be created.  If the message to overwrite doesn't exist then
 *    it is created (but only if it can be inserted so its sequence
 *    number is OVERWRITE; that is, you can create a message with
 *    the sequence number 5 if there is no message with sequence
 *    number 4).
 *
 *              The UPDATE_TIME argument is a boolean that allows the caller
 *              to skip updating the modification time.  This is useful when
 *              several calls to H5O_modify will be made in a sequence.
 *
 * Return:  Success:  The sequence number of the message that
 *        was modified or created.
 *
 *    Failure:  Negative
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug  6 1997
 *
 * Modifications:
 *
 *  Robb Matzke, 7 Jan 1998
 *  Handles constant vs non-constant messages.  Once a message is made
 *  constant it can never become non-constant.  Constant messages cannot
 *  be modified.
 *
 *-------------------------------------------------------------------------
 */
static int
H5O_modify_real(H5G_entry_t *ent, const H5O_class_t *type, int overwrite,
   unsigned flags, unsigned update_flags, const void *mesg, hid_t dxpl_id)
{
    H5O_t    *oh=NULL;
    int            sequence;
    unsigned    idx;            /* Index of message to modify */
    H5O_mesg_t         *idx_msg;        /* Pointer to message to modify */
    H5O_shared_t  sh_mesg;
    int            ret_value;

    FUNC_ENTER_NOAPI_NOINIT(H5O_modify_real)

    /* check args */
    assert(ent);
    assert(ent->file);
    assert(H5F_addr_defined(ent->header));
    assert(type);
    assert(mesg);
    assert (0==(flags & ~H5O_FLAG_BITS));

    if (0==(ent->file->intent & H5F_ACC_RDWR))
  HGOTO_ERROR (H5E_OHDR, H5E_WRITEERROR, FAIL, "no write intent on file");

    if (NULL == (oh = H5AC_protect(ent->file, dxpl_id, H5AC_OHDR, ent->header, NULL, NULL, H5AC_WRITE)))
  HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to load object header");

    /* Count similar messages */
    for (idx = 0, sequence = -1, idx_msg=&oh->mesg[0]; idx < oh->nmesgs; idx++, idx_msg++) {
  if (type->id != idx_msg->type->id)
            continue;
  if (++sequence == overwrite)
            break;
    } /* end for */

    /* Was the right message found? */
    if (overwrite >= 0 && (idx >= oh->nmesgs || sequence != overwrite)) {
  /* But can we insert a new one with this sequence number? */
  if (overwrite == sequence + 1)
      overwrite = -1;
  else
      HGOTO_ERROR(H5E_OHDR, H5E_NOTFOUND, FAIL, "message not found");
    } /* end if */

    /* Check for creating new message */
    if (overwrite < 0) {
        /* Create a new message */
        if((idx=H5O_new_mesg(ent->file,oh,&flags,type,mesg,&sh_mesg,&type,&mesg,dxpl_id))==UFAIL)
      HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to create new message");

        /* Set the correct sequence number for the message created */
  sequence++;

    } else if (oh->mesg[idx].flags & H5O_FLAG_CONSTANT) {
  HGOTO_ERROR(H5E_OHDR, H5E_WRITEERROR, FAIL, "unable to modify constant message");
    } else if (oh->mesg[idx].flags & H5O_FLAG_SHARED) {
  HGOTO_ERROR (H5E_OHDR, H5E_WRITEERROR, FAIL, "unable to modify shared (constant) message");
    }

    /* Write the information to the message */
    if(H5O_write_mesg(oh,idx,type,mesg,flags,update_flags)<0)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to write message");

    /* Update the modification time message if any */
    if(update_flags&H5O_UPDATE_TIME)
        H5O_touch_oh(ent->file, oh, FALSE);

    /* Set return value */
    ret_value = sequence;

done:
    if (oh && H5AC_unprotect(ent->file, dxpl_id, H5AC_OHDR, ent->header, oh, FALSE) < 0)
  HDONE_ERROR(H5E_OHDR, H5E_PROTECT, FAIL, "unable to release object header");

    FUNC_LEAVE_NOAPI(ret_value);
} /* end H5O_modify_real() */


/*-------------------------------------------------------------------------
 * Function:  H5O_protect
 *
 * Purpose:  Wrapper around H5AC_protect for use during a H5O_protect->
 *              H5O_append->...->H5O_append->H5O_unprotect sequence of calls
 *              during an object's creation.
 *
 * Return:  Success:  Pointer to the object header structure for the
 *                              object.
 *
 *    Failure:  NULL
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Dec 31 2002
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
H5O_t *
H5O_protect(H5G_entry_t *ent, hid_t dxpl_id)
{
    H5O_t         *ret_value;      /* Return value */

    FUNC_ENTER_NOAPI(H5O_protect, NULL);

    /* check args */
    assert(ent);
    assert(ent->file);
    assert(H5F_addr_defined(ent->header));

    if (0==(ent->file->intent & H5F_ACC_RDWR))
  HGOTO_ERROR (H5E_OHDR, H5E_WRITEERROR, NULL, "no write intent on file");

    if (NULL == (ret_value = H5AC_protect(ent->file, dxpl_id, H5AC_OHDR, ent->header, NULL, NULL, H5AC_WRITE)))
  HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, "unable to load object header");

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


/*-------------------------------------------------------------------------
 * Function:  H5O_unprotect
 *
 * Purpose:  Wrapper around H5AC_unprotect for use during a H5O_protect->
 *              H5O_append->...->H5O_append->H5O_unprotect sequence of calls
 *              during an object's creation.
 *
 * Return:  Success:  Non-negative
 *
 *    Failure:  Negative
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Dec 31 2002
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_unprotect(H5G_entry_t *ent, H5O_t *oh, hid_t dxpl_id)
{
    herr_t ret_value=SUCCEED;      /* Return value */

    FUNC_ENTER_NOAPI(H5O_unprotect, FAIL);

    /* check args */
    assert(ent);
    assert(ent->file);
    assert(H5F_addr_defined(ent->header));
    assert(oh);

    if (H5AC_unprotect(ent->file, dxpl_id, H5AC_OHDR, ent->header, oh, FALSE) < 0)
  HGOTO_ERROR(H5E_OHDR, H5E_PROTECT, FAIL, "unable to release object header");

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


/*-------------------------------------------------------------------------
 * Function:  H5O_append
 *
 * Purpose:  Simplified version of H5O_modify, used when creating a new
 *              object header message (usually during object creation)
 *
 * Return:  Success:  The sequence number of the message that
 *        was created.
 *
 *    Failure:  Negative
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Dec 31 2002
 *
 * Modifications:
 *              Changed to use IDs for types, instead of type objects, then
 *              call "real" routine.
 *              Quincey Koziol
 *    Feb 14 2003
 *
 *-------------------------------------------------------------------------
 */
int
H5O_append(H5F_t *f, hid_t dxpl_id, H5O_t *oh, unsigned type_id, unsigned flags,
    const void *mesg)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    int  ret_value;                      /* Return value */

    FUNC_ENTER_NOAPI(H5O_append,FAIL);

    /* check args */
    assert(f);
    assert(oh);
    assert(type_id<NELMTS(message_type_g));
    type=message_type_g[type_id];    /* map the type ID to the actual type object */
    assert(type);
    assert(0==(flags & ~H5O_FLAG_BITS));
    assert(mesg);

    /* Call the "real" append routine */
    if((ret_value=H5O_append_real( f, dxpl_id, oh, type, flags, mesg))<0)
  HGOTO_ERROR(H5E_OHDR, H5E_WRITEERROR, FAIL, "unable to append to object header");

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


/*-------------------------------------------------------------------------
 * Function:  H5O_append_real
 *
 * Purpose:  Simplified version of H5O_modify, used when creating a new
 *              object header message (usually during object creation)
 *
 * Return:  Success:  The sequence number of the message that
 *        was created.
 *
 *    Failure:  Negative
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Dec 31 2002
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int
H5O_append_real(H5F_t *f, hid_t dxpl_id, H5O_t *oh, const H5O_class_t *type,
    unsigned flags, const void *mesg)
{
    unsigned    idx;            /* Index of message to modify */
    H5O_shared_t  sh_mesg;
    int            ret_value = FAIL;

    FUNC_ENTER_NOAPI_NOINIT(H5O_append_real);

    /* check args */
    assert(f);
    assert(oh);
    assert(type);
    assert(0==(flags & ~H5O_FLAG_BITS));
    assert(mesg);

    /* Create a new message */
    if((idx=H5O_new_mesg(f,oh,&flags,type,mesg,&sh_mesg,&type,&mesg,dxpl_id))==UFAIL)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to create new message");

    /* Write the information to the message */
    if(H5O_write_mesg(oh,idx,type,mesg,flags,0)<0)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to write message");

    /* Set return value */
    ret_value = idx;

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


/*-------------------------------------------------------------------------
 * Function:  H5O_new_mesg
 *
 * Purpose:  Create a new message in an object header
 *
 * Return:  Success:  Index of message
 *    Failure:  Negative
 *
 * Programmer:  Quincey Koziol
 *              Friday, September  3, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static unsigned
H5O_new_mesg(H5F_t *f, H5O_t *oh, unsigned *flags, const H5O_class_t *orig_type,
    const void *orig_mesg, H5O_shared_t *sh_mesg, const H5O_class_t **new_type,
    const void **new_mesg, hid_t dxpl_id)
{
    size_t  size;                   /* Size of space allocated for object header */
    unsigned    ret_value=UFAIL;        /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_new_mesg);

    /* check args */
    assert(f);
    assert(oh);
    assert(flags);
    assert(orig_type);
    assert(orig_mesg);
    assert(sh_mesg);
    assert(new_mesg);
    assert(new_type);

    /* Check for shared message */
    if (*flags & H5O_FLAG_SHARED) {
        HDmemset(sh_mesg,0,sizeof(H5O_shared_t));

        if (NULL==orig_type->get_share)
            HGOTO_ERROR (H5E_OHDR, H5E_UNSUPPORTED, UFAIL, "message class is not sharable");
        if ((orig_type->get_share)(f, orig_mesg, sh_mesg/*out*/)<0) {
            /*
             * If the message isn't shared then turn off the shared bit
             * and treat it as an unshared message.
             */
            H5E_clear ();
            *flags &= ~H5O_FLAG_SHARED;
        } else {
            /* Change type & message to use shared information */
            *new_type=H5O_SHARED;
            *new_mesg=sh_mesg;
        } /* end else */
    } /* end if */
    else {
        *new_type=orig_type;
        *new_mesg=orig_mesg;
    } /* end else */

    /* Compute the size needed to store the message on disk */
    if ((size = ((*new_type)->raw_size) (f, *new_mesg)) >=H5O_MAX_SIZE)
        HGOTO_ERROR (H5E_OHDR, H5E_CANTINIT, UFAIL, "object header message is too large (16k max)");

    /* Allocate space in the object headed for the message */
    if ((ret_value = H5O_alloc(f, oh, orig_type, size)) == UFAIL)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, UFAIL, "unable to allocate space for message");

    /* Increment any links in message */
    if((*new_type)->link && ((*new_type)->link)(f,dxpl_id,(*new_mesg))<0)
        HGOTO_ERROR (H5E_OHDR, H5E_LINK, UFAIL, "unable to adjust shared object link count");

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


/*-------------------------------------------------------------------------
 * Function:  H5O_write_mesg
 *
 * Purpose:  Write message to object header
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Quincey Koziol
 *              Friday, September  3, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_write_mesg(H5O_t *oh, unsigned idx, const H5O_class_t *type,
    const void *mesg, unsigned flags, unsigned update_flags)
{
    H5O_mesg_t         *idx_msg;        /* Pointer to message to modify */
    herr_t      ret_value=SUCCEED;      /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_write_mesg);

    /* check args */
    assert(oh);
    assert(type);
    assert(mesg);

    /* Set pointer to the correct message */
    idx_msg=&oh->mesg[idx];

    /* Reset existing native information */
    if(!(update_flags&H5O_UPDATE_DATA_ONLY))
        H5O_reset_real(type, idx_msg->native);

    /* Copy the native value for the message */
    if (NULL == (idx_msg->native = (type->copy) (mesg, idx_msg->native, update_flags)))
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to copy message to object header");

    idx_msg->flags = flags;
    idx_msg->dirty = TRUE;
    oh->cache_info.is_dirty = TRUE;

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


/*-------------------------------------------------------------------------
 * Function:  H5O_touch_oh
 *
 * Purpose:  If FORCE is non-zero then create a modification time message
 *    unless one already exists.  Then update any existing
 *    modification time message with the current time.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Robb Matzke
 *              Monday, July 27, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_touch_oh(H5F_t *f, H5O_t *oh, hbool_t force)
{
    unsigned  idx;
#ifdef H5_HAVE_GETTIMEOFDAY
    struct timeval now_tv;
#endif /* H5_HAVE_GETTIMEOFDAY */
    time_t  now;
    size_t  size;
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_touch_oh);

    assert(oh);

    /* Look for existing message */
    for (idx=0; idx<oh->nmesgs; idx++) {
  if (H5O_MTIME==oh->mesg[idx].type || H5O_MTIME_NEW==oh->mesg[idx].type)
            break;
    }

#ifdef H5_HAVE_GETTIMEOFDAY
    HDgettimeofday(&now_tv,NULL);
    now=now_tv.tv_sec;
#else /* H5_HAVE_GETTIMEOFDAY */
    now = HDtime(NULL);
#endif /* H5_HAVE_GETTIMEOFDAY */

    /* Create a new message */
    if (idx==oh->nmesgs) {
  if (!force)
            HGOTO_DONE(SUCCEED); /*nothing to do*/
  size = (H5O_MTIME_NEW->raw_size)(f, &now);
  if ((idx=H5O_alloc(f, oh, H5O_MTIME_NEW, size))==UFAIL)
      HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to allocate space for modification time message");
    }

    /* Update the native part */
    if (NULL==oh->mesg[idx].native) {
  if (NULL==(oh->mesg[idx].native = H5FL_MALLOC(time_t)))
      HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "memory allocation failed for modification time message");
    }
    *((time_t*)(oh->mesg[idx].native)) = now;
    oh->mesg[idx].dirty = TRUE;
    oh->cache_info.is_dirty = TRUE;

done:
    FUNC_LEAVE_NOAPI(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:  H5O_touch
 *
 * Purpose:  Touch an object by setting the modification time to the
 *    current time and marking the object as dirty.  Unless FORCE
 *    is non-zero, nothing happens if there is no MTIME message in
 *    the object header.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Robb Matzke
 *              Monday, July 27, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_touch(H5G_entry_t *ent, hbool_t force, hid_t dxpl_id)
{
    H5O_t  *oh = NULL;
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI(H5O_touch, FAIL);

    /* check args */
    assert(ent);
    assert(ent->file);
    assert(H5F_addr_defined(ent->header));
    if (0==(ent->file->intent & H5F_ACC_RDWR))
  HGOTO_ERROR(H5E_OHDR, H5E_WRITEERROR, FAIL, "no write intent on file");

    /* Get the object header */
    if (NULL==(oh=H5AC_protect(ent->file, dxpl_id, H5AC_OHDR, ent->header, NULL, NULL, H5AC_WRITE)))
  HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to load object header");

    /* Create/Update the modification time message */
    if (H5O_touch_oh(ent->file, oh, force)<0)
  HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to update object modificaton time");

done:
    if (oh && H5AC_unprotect(ent->file, dxpl_id, H5AC_OHDR, ent->header, oh, FALSE)<0)
  HDONE_ERROR(H5E_OHDR, H5E_PROTECT, FAIL, "unable to release object header");

    FUNC_LEAVE_NOAPI(ret_value);
}

#ifdef H5O_ENABLE_BOGUS

/*-------------------------------------------------------------------------
 * Function:  H5O_bogus_oh
 *
 * Purpose:  Create a "bogus" message unless one already exists.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Quincey Koziol
 *              <koziol@ncsa.uiuc.edu>
 *              Tuesday, January 21, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_bogus_oh(H5F_t *f, H5O_t *oh)
{
    int  idx;
    size_t  size;
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER(H5O_bogus_oh, FAIL);

    assert(f);
    assert(oh);

    /* Look for existing message */
    for (idx=0; idx<oh->nmesgs; idx++)
  if (H5O_BOGUS==oh->mesg[idx].type)
            break;

    /* Create a new message */
    if (idx==oh->nmesgs) {
  size = (H5O_BOGUS->raw_size)(f, NULL);
  if ((idx=H5O_alloc(f, oh, H5O_BOGUS, size))<0)
      HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to allocate space for 'bogus' message");

        /* Allocate the native message in memory */
  if (NULL==(oh->mesg[idx].native = H5MM_malloc(sizeof(H5O_bogus_t))))
      HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "memory allocation failed for 'bogus' message");

        /* Update the native part */
        ((H5O_bogus_t *)(oh->mesg[idx].native))->u = H5O_BOGUS_VALUE;

        /* Mark the message and object header as dirty */
        oh->mesg[idx].dirty = TRUE;
        oh->dirty = TRUE;
    } /* end if */

done:
    FUNC_LEAVE(ret_value);
} /* end H5O_bogus_oh() */


/*-------------------------------------------------------------------------
 * Function:  H5O_bogus
 *
 * Purpose:  Create a "bogus" message in an object.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Quincey Koziol
 *              <koziol@ncsa.uiuc.edu>
 *              Tuesday, January 21, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_bogus(H5G_entry_t *ent, hid_t dxpl_id)
{
    H5O_t  *oh = NULL;
    herr_t  ret_value = SUCCEED;

    FUNC_ENTER(H5O_bogus, FAIL);

    /* check args */
    assert(ent);
    assert(ent->file);
    assert(H5F_addr_defined(ent->header));

    /* Verify write access to the file */
    if (0==(ent->file->intent & H5F_ACC_RDWR))
  HGOTO_ERROR(H5E_OHDR, H5E_WRITEERROR, FAIL, "no write intent on file");

    /* Get the object header */
    if (NULL==(oh=H5AC_protect(ent->file, dxpl_id, H5AC_OHDR, ent->header, NULL, NULL, H5AC_WRITE)))
  HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to load object header");

    /* Create the "bogus" message */
    if (H5O_bogus_oh(ent->file, oh)<0)
  HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to update object 'bogus' message");

done:
    if (oh && H5AC_unprotect(ent->file, dxpl_id, H5AC_OHDR, ent->header, oh, FALSE)<0)
  HDONE_ERROR(H5E_OHDR, H5E_PROTECT, FAIL, "unable to release object header");

    FUNC_LEAVE(ret_value);
} /* end H5O_bogus() */
#endif /* H5O_ENABLE_BOGUS */


/*-------------------------------------------------------------------------
 * Function:  H5O_remove
 *
 * Purpose:  Removes the specified message from the object header.
 *    If sequence is H5O_ALL (-1) then all messages of the
 *    specified type are removed.  Removing a message causes
 *    the sequence numbers to change for subsequent messages of
 *    the same type.
 *
 *    No attempt is made to join adjacent free areas of the
 *    object header into a single larger free area.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug 28 1997
 *
 * Modifications:
 *
 *  Robb Matzke, 7 Jan 1998
 *  Does not remove constant messages.
 *
 *      Changed to use IDs for types, instead of type objects, then
 *      call "real" routine.
 *      Quincey Koziol
 *  Feb 14 2003
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_remove(H5G_entry_t *ent, unsigned type_id, int sequence, hbool_t adj_link, hid_t dxpl_id)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    herr_t      ret_value;              /* Return value */

    FUNC_ENTER_NOAPI(H5O_remove, FAIL)

    /* check args */
    HDassert(ent);
    HDassert(ent->file);
    HDassert(H5F_addr_defined(ent->header));
    HDassert(type_id < NELMTS(message_type_g));
    type = message_type_g[type_id];    /* map the type ID to the actual type object */
    HDassert(type);

    /* Call the "real" remove routine */
    if((ret_value = H5O_remove_real(ent, type, sequence, NULL, NULL, adj_link, dxpl_id))<0)
  HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "unable to remove object header message")

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


/*-------------------------------------------------------------------------
 * Function:  H5O_remove_op
 *
 * Purpose:  Removes messages from the object header that a callback
 *              routine indicates should be removed.
 *
 *    No attempt is made to join adjacent free areas of the
 *    object header into a single larger free area.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Sep  6 2005
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_remove_op(H5G_entry_t *ent, unsigned type_id, 
    H5O_operator_t op, void *op_data, hbool_t adj_link, hid_t dxpl_id)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    herr_t      ret_value;              /* Return value */

    FUNC_ENTER_NOAPI(H5O_remove_op, FAIL)

    /* check args */
    HDassert(ent);
    HDassert(ent->file);
    HDassert(H5F_addr_defined(ent->header));
    HDassert(type_id < NELMTS(message_type_g));
    type = message_type_g[type_id];    /* map the type ID to the actual type object */
    HDassert(type);

    /* Call the "real" remove routine */
    if((ret_value = H5O_remove_real(ent, type, H5O_ALL, op, op_data, adj_link, dxpl_id))<0)
  HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "unable to remove object header message")

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


/*-------------------------------------------------------------------------
 * Function:  H5O_remove_cb
 *
 * Purpose:  Object header iterator callback routine to remove messages
 *              of a particular type that match a particular sequence number,
 *              or all messages if the sequence number is H5O_ALL (-1).
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Sep  6 2005
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_remove_cb(H5O_mesg_t *mesg/*in,out*/, unsigned idx, unsigned * oh_flags_ptr,
    void *_udata/*in,out*/)
{
    H5O_iter_ud1_t *udata = (H5O_iter_ud1_t *)_udata;   /* Operator user data */
    htri_t try_remove = FALSE;         /* Whether to try removing a message */
    herr_t ret_value = H5O_ITER_CONT;   /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_remove_cb)

    /* check args */
    HDassert(mesg);

    /* Check for callback routine */
    if(udata->op) {
        /* Call the iterator callback */
        if((try_remove = (udata->op)(mesg->native, idx, udata->op_data)) < 0)
            HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, H5O_ITER_ERROR, "unable to delete file space for object header message")
    } /* end if */
    else {
        /* If there's no callback routine, does the sequence # match? */
        if ((int)idx == udata->sequence || H5O_ALL == udata->sequence)
            try_remove = TRUE;
    } /* end else */

    /* Try removing the message, if indicated */
    if(try_remove) {
        /*
         * Keep track of how many times we failed trying to remove constant
         * messages.
         */
        if (mesg->flags & H5O_FLAG_CONSTANT) {
            udata->nfailed++;
        } /* end if */
        else {
            /* Free any space referred to in the file from this message */
            if(H5O_delete_mesg(udata->f, udata->dxpl_id, mesg, udata->adj_link)<0)
                HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, H5O_ITER_ERROR, "unable to delete file space for object header message")

            /* Free any native information */
            H5O_free_mesg(mesg);

            /* Change message type to nil and zero it */
            mesg->type = H5O_NULL;
            HDmemset(mesg->raw, 0, mesg->raw_size);

            /* Indicate that the message was modified */
            mesg->dirty = TRUE;

            /* Indicate that the object header was modified */
            *oh_flags_ptr = TRUE;
        } /* end else */

        /* Break out now, if we've found the correct message */
        if(udata->sequence != H5O_ALL)
            HGOTO_DONE(H5O_ITER_STOP)
    } /* end if */

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


/*-------------------------------------------------------------------------
 * Function:  H5O_remove_real
 *
 * Purpose:  Removes the specified message from the object header.
 *    If sequence is H5O_ALL (-1) then all messages of the
 *    specified type are removed.  Removing a message causes
 *    the sequence numbers to change for subsequent messages of
 *    the same type.
 *
 *    No attempt is made to join adjacent free areas of the
 *    object header into a single larger free area.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug 28 1997
 *
 * Modifications:
 *
 *  Robb Matzke, 7 Jan 1998
 *  Does not remove constant messages.
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_remove_real(H5G_entry_t *ent, const H5O_class_t *type, int sequence,
    H5O_operator_t op, void *op_data, hbool_t adj_link, hid_t dxpl_id)
{
    H5O_iter_ud1_t udata;               /* User data for iterator */
    herr_t ret_value=SUCCEED;           /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_remove_real)

    /* check args */
    HDassert(ent);
    HDassert(ent->file);
    HDassert(type);

    if (0==(ent->file->intent & H5F_ACC_RDWR))
  HGOTO_ERROR (H5E_HEAP, H5E_WRITEERROR, FAIL, "no write intent on file")

    /* Set up iterator operator data */
    udata.f = ent->file;
    udata.dxpl_id = dxpl_id;
    udata.sequence = sequence;
    udata.nfailed = 0;
    udata.op = op;
    udata.op_data = op_data;
    udata.adj_link = adj_link;

    /* Iterate over the messages, deleting appropriate one(s) */
    if(H5O_iterate_real(ent, type, H5AC_WRITE, TRUE, (void *)H5O_remove_cb, &udata, dxpl_id) < 0)
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "error iterating over messages")

    /* Fail if we tried to remove any constant messages */
    if (udata.nfailed)
  HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to remove constant message(s)")

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


/*-------------------------------------------------------------------------
 * Function:  H5O_alloc_extend_chunk
 *
 * Purpose:  Extends a chunk which hasn't been allocated on disk yet
 *    to make the chunk large enough to contain a message whose
 *    data size is exactly SIZE bytes (SIZE need not be aligned).
 *
 *    If the last message of the chunk is the null message, then
 *    that message will be extended with the chunk.  Otherwise a
 *    new null message is created.
 *
 *    F is the file in which the chunk will be written.  It is
 *    included to ensure that there is enough space to extend
 *    this chunk.
 *
 * Return:  Success:  Message index for null message which
 *        is large enough to hold SIZE bytes.
 *
 *    Failure:  Negative
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug  7 1997
 *
 * Modifications:
 *    Robb Matzke, 1999-08-26
 *    If new memory is allocated as a multiple of some alignment
 *    then we're careful to initialize the part of the new memory
 *    from the end of the expected message to the end of the new
 *    memory.
 *-------------------------------------------------------------------------
 */
static unsigned
H5O_alloc_extend_chunk(H5F_t *f, H5O_t *oh, unsigned chunkno, size_t size)
{
    unsigned  u;
    unsigned  idx;
    size_t  delta, old_size;
    size_t  aligned_size = H5O_ALIGN(size);
    uint8_t  *old_addr;
    unsigned  ret_value;

    FUNC_ENTER_NOAPI_NOINIT(H5O_alloc_extend_chunk);

    /* check args */
    assert(oh);
    assert(chunkno < oh->nchunks);
    assert(size > 0);

    if (H5F_addr_defined(oh->chunk[chunkno].addr))
  HGOTO_ERROR(H5E_OHDR, H5E_NOSPACE, UFAIL, "chunk is on disk");

    /* try to extend a null message */
    for (idx=0; idx<oh->nmesgs; idx++) {
  if (oh->mesg[idx].chunkno==chunkno) {
            if (H5O_NULL_ID == oh->mesg[idx].type->id &&
                    (oh->mesg[idx].raw + oh->mesg[idx].raw_size ==
                     oh->chunk[chunkno].image + oh->chunk[chunkno].size)) {

                delta = MAX (H5O_MIN_SIZE, aligned_size - oh->mesg[idx].raw_size);
                assert (delta=H5O_ALIGN (delta));

                /* Reserve space in the file to hold the increased chunk size */
                if( H5MF_reserve(f, (hsize_t)delta) < 0 )
                    HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, UFAIL, "unable to reserve space in file");

                oh->mesg[idx].dirty = TRUE;
                oh->mesg[idx].raw_size += delta;

                old_addr = oh->chunk[chunkno].image;

                /* Be careful not to indroduce garbage */
                oh->chunk[chunkno].image = H5FL_BLK_REALLOC(chunk_image,old_addr,
                                                        (oh->chunk[chunkno].size + delta));
                if (NULL==oh->chunk[chunkno].image)
                    HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, UFAIL, "memory allocation failed");
                HDmemset(oh->chunk[chunkno].image + oh->chunk[chunkno].size,
                         0, delta);
                oh->chunk[chunkno].size += delta;

                /* adjust raw addresses for messages of this chunk */
                if (old_addr != oh->chunk[chunkno].image) {
                    for (u = 0; u < oh->nmesgs; u++) {
                        if (oh->mesg[u].chunkno == chunkno)
                            oh->mesg[u].raw = oh->chunk[chunkno].image +
                                              (oh->mesg[u].raw - old_addr);
                    }
                }
                HGOTO_DONE(idx);
            }
        } /* end if */
    }

    /* Reserve space in the file */
    delta = MAX(H5O_MIN_SIZE, aligned_size+H5O_SIZEOF_MSGHDR(f));
    delta = H5O_ALIGN(delta);

    if( H5MF_reserve(f, (hsize_t)delta) < 0 )
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, UFAIL, "unable to reserve space in file");

    /* create a new null message */
    if (oh->nmesgs >= oh->alloc_nmesgs) {
        unsigned na = oh->alloc_nmesgs + H5O_NMESGS;
        H5O_mesg_t *x = H5FL_SEQ_REALLOC (H5O_mesg_t, oh->mesg, (size_t)na);

        if (NULL==x)
            HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, UFAIL, "memory allocation failed");
        oh->alloc_nmesgs = na;
        oh->mesg = x;
    }
    idx = oh->nmesgs++;
    oh->mesg[idx].type = H5O_NULL;
    oh->mesg[idx].dirty = TRUE;
    oh->mesg[idx].native = NULL;
    oh->mesg[idx].raw = oh->chunk[chunkno].image +
      oh->chunk[chunkno].size +
      H5O_SIZEOF_MSGHDR(f);
    oh->mesg[idx].raw_size = delta - H5O_SIZEOF_MSGHDR(f);
    oh->mesg[idx].chunkno = chunkno;

    old_addr = oh->chunk[chunkno].image;
    old_size = oh->chunk[chunkno].size;
    oh->chunk[chunkno].size += delta;
    oh->chunk[chunkno].image = H5FL_BLK_REALLOC(chunk_image,old_addr,
              oh->chunk[chunkno].size);
    if (NULL==oh->chunk[chunkno].image)
  HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, UFAIL, "memory allocation failed");
    HDmemset(oh->chunk[chunkno].image+old_size, 0,
       oh->chunk[chunkno].size - old_size);

    /* adjust raw addresses for messages of this chunk */
    if (old_addr != oh->chunk[chunkno].image) {
  for (u = 0; u < oh->nmesgs; u++) {
      if (oh->mesg[u].chunkno == chunkno)
    oh->mesg[u].raw = oh->chunk[chunkno].image +
        (oh->mesg[u].raw - old_addr);
  }
    }

    /* Set return value */
    ret_value=idx;

done:
    FUNC_LEAVE_NOAPI(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:  H5O_alloc_new_chunk
 *
 * Purpose:  Allocates a new chunk for the object header but doen't
 *    give the new chunk a file address yet.  One of the other
 *    chunks will get an object continuation message.   If there
 *    isn't room in any other chunk for the object continuation
 *    message, then some message from another chunk is moved into
 *    this chunk to make room.
 *
 *     SIZE need not be aligned.
 *
 * Return:  Success:  Index number of the null message for the
 *        new chunk.  The null message will be at
 *        least SIZE bytes not counting the message
 *        ID or size fields.
 *
 *    Failure:  Negative
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug  7 1997
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static unsigned
H5O_alloc_new_chunk(H5F_t *f, H5O_t *oh, size_t size)
{
    size_t  cont_size;    /*continuation message size  */
    int  found_null = (-1);  /*best fit null message    */
    int  found_other = (-1);  /*best fit other message  */
    unsigned  idx;            /*message number */
    uint8_t  *p = NULL;    /*ptr into new chunk    */
    H5O_cont_t  *cont = NULL;    /*native continuation message  */
    int  chunkno;
    unsigned  u;
    unsigned  ret_value;    /*return value  */

    FUNC_ENTER_NOAPI_NOINIT(H5O_alloc_new_chunk);

    /* check args */
    assert (oh);
    assert (size > 0);
    size = H5O_ALIGN(size);

    /*
     * Find the smallest null message that will hold an object
     * continuation message.  Failing that, find the smallest message
     * that could be moved to make room for the continuation message.
     * Don't ever move continuation message from one chunk to another.
     */
    cont_size = H5O_ALIGN (H5F_SIZEOF_ADDR(f) + H5F_SIZEOF_SIZE(f));
    for (u=0; u<oh->nmesgs; u++) {
  if (H5O_NULL_ID == oh->mesg[u].type->id) {
      if (cont_size == oh->mesg[u].raw_size) {
    found_null = u;
    break;
      } else if (oh->mesg[u].raw_size >= cont_size &&
           (found_null < 0 ||
      (oh->mesg[u].raw_size <
       oh->mesg[found_null].raw_size))) {
    found_null = u;
      }
  } else if (H5O_CONT_ID == oh->mesg[u].type->id) {
      /*don't consider continuation messages */
  } else if (oh->mesg[u].raw_size >= cont_size &&
       (found_other < 0 ||
        oh->mesg[u].raw_size < oh->mesg[found_other].raw_size)) {
      found_other = u;
  }
    }
    assert(found_null >= 0 || found_other >= 0);

    /*
     * If we must move some other message to make room for the null
     * message, then make sure the new chunk has enough room for that
     * other message.
     */
    if (found_null < 0)
  size += H5O_SIZEOF_MSGHDR(f) + oh->mesg[found_other].raw_size;

    /*
     * The total chunk size must include the requested space plus enough
     * for the message header.  This must be at least some minimum and a
     * multiple of the alignment size.
     */
    size = MAX(H5O_MIN_SIZE, size + H5O_SIZEOF_MSGHDR(f));
    assert (size == H5O_ALIGN (size));

    /* Reserve space in the file to hold the new chunk */
    if( H5MF_reserve(f, (hsize_t)size) < 0 )
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, UFAIL, "unable to reserve space in file for new chunk");

    /*
     * Create the new chunk without giving it a file address.
     */
    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, UFAIL, "memory allocation failed");
        oh->alloc_nchunks = na;
        oh->chunk = x;
    }
    chunkno = oh->nchunks++;
    oh->chunk[chunkno].dirty = TRUE;
    oh->chunk[chunkno].addr = HADDR_UNDEF;
    oh->chunk[chunkno].size = size;
    if (NULL==(oh->chunk[chunkno].image = p = H5FL_BLK_CALLOC(chunk_image,size)))
  HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, UFAIL, "memory allocation failed");

    /*
     * Make sure we have enough space for all possible new messages
     * that could be generated below.
     */
    if (oh->nmesgs + 3 > oh->alloc_nmesgs) {
        int old_alloc=oh->alloc_nmesgs;
        unsigned na = oh->alloc_nmesgs + MAX (H5O_NMESGS, 3);
        H5O_mesg_t *x = H5FL_SEQ_REALLOC (H5O_mesg_t, oh->mesg, (size_t)na);

        if (!x)
            HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, UFAIL, "memory allocation failed");
        oh->alloc_nmesgs = na;
        oh->mesg = x;

        /* Set new object header info to zeros */
        HDmemset(&oh->mesg[old_alloc], 0,
     (oh->alloc_nmesgs-old_alloc)*sizeof(H5O_mesg_t));
    }

    /*
     * Describe the messages of the new chunk.
     */
    if (found_null < 0) {
  found_null = u = oh->nmesgs++;
  oh->mesg[u].type = H5O_NULL;
  oh->mesg[u].dirty = TRUE;
  oh->mesg[u].native = NULL;
  oh->mesg[u].raw = oh->mesg[found_other].raw;
  oh->mesg[u].raw_size = oh->mesg[found_other].raw_size;
  oh->mesg[u].chunkno = oh->mesg[found_other].chunkno;

  oh->mesg[found_other].dirty = TRUE;
        /* Copy the message to the new location */
        HDmemcpy(p+H5O_SIZEOF_MSGHDR(f),oh->mesg[found_other].raw,oh->mesg[found_other].raw_size);
  oh->mesg[found_other].raw = p + H5O_SIZEOF_MSGHDR(f);
  oh->mesg[found_other].chunkno = chunkno;
  p += H5O_SIZEOF_MSGHDR(f) + oh->mesg[found_other].raw_size;
  size -= H5O_SIZEOF_MSGHDR(f) + oh->mesg[found_other].raw_size;
    }
    idx = oh->nmesgs++;
    oh->mesg[idx].type = H5O_NULL;
    oh->mesg[idx].dirty = TRUE;
    oh->mesg[idx].native = NULL;
    oh->mesg[idx].raw = p + H5O_SIZEOF_MSGHDR(f);
    oh->mesg[idx].raw_size = size - H5O_SIZEOF_MSGHDR(f);
    oh->mesg[idx].chunkno = chunkno;

    /*
     * If the null message that will receive the continuation message
     * is larger than the continuation message, then split it into
     * two null messages.
     */
    if (oh->mesg[found_null].raw_size > cont_size) {
  u = oh->nmesgs++;
  oh->mesg[u].type = H5O_NULL;
  oh->mesg[u].dirty = TRUE;
  oh->mesg[u].native = NULL;
  oh->mesg[u].raw = oh->mesg[found_null].raw +
        cont_size +
        H5O_SIZEOF_MSGHDR(f);
  oh->mesg[u].raw_size = oh->mesg[found_null].raw_size -
             (cont_size + H5O_SIZEOF_MSGHDR(f));
  oh->mesg[u].chunkno = oh->mesg[found_null].chunkno;

  oh->mesg[found_null].dirty = TRUE;
  oh->mesg[found_null].raw_size = cont_size;
    }

    /*
     * Initialize the continuation message.
     */
    oh->mesg[found_null].type = H5O_CONT;
    oh->mesg[found_null].dirty = TRUE;
    if (NULL==(cont = H5FL_MALLOC(H5O_cont_t)))
  HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, UFAIL, "memory allocation failed");
    cont->addr = HADDR_UNDEF;
    cont->size = 0;
    cont->chunkno = chunkno;
    oh->mesg[found_null].native = cont;

    /* Set return value */
    ret_value=idx;

done:
    FUNC_LEAVE_NOAPI(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:  H5O_alloc
 *
 * Purpose:  Allocate enough space in the object header for this message.
 *
 * Return:  Success:  Index of message
 *
 *    Failure:  Negative
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug  6 1997
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static unsigned
H5O_alloc(H5F_t *f, H5O_t *oh, const H5O_class_t *type, size_t size)
{
    unsigned  idx;
    H5O_mesg_t *msg;            /* Pointer to newly allocated message */
    size_t  aligned_size = H5O_ALIGN(size);
    unsigned  ret_value;      /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_alloc);

    /* check args */
    assert (oh);
    assert (type);

    /* look for a null message which is large enough */
    for (idx = 0; idx < oh->nmesgs; idx++) {
  if (H5O_NULL_ID == oh->mesg[idx].type->id &&
                oh->mesg[idx].raw_size >= aligned_size)
      break;
    }

#ifdef LATER
    /*
     * Perhaps if we join adjacent null messages we could make one
     * large enough... we leave this as an exercise for future
     * programmers :-)  This isn't a high priority because when an
     * object header is read from disk the null messages are combined
     * anyway.
     */
#endif

    /* if we didn't find one, then allocate more header space */
    if (idx >= oh->nmesgs) {
        unsigned  chunkno;

  /*
   * Look for a chunk which hasn't had disk space allocated yet
   * since we can just increase the size of that chunk.
   */
  for (chunkno = 0; chunkno < oh->nchunks; chunkno++) {
      if ((idx = H5O_alloc_extend_chunk(f, oh, chunkno, size)) != UFAIL) {
    break;
      }
      H5E_clear();
  }

  /*
   * Create a new chunk
   */
  if (idx == UFAIL) {
      if ((idx = H5O_alloc_new_chunk(f, oh, size)) == UFAIL)
    HGOTO_ERROR(H5E_OHDR, H5E_NOSPACE, UFAIL, "unable to create a new object header data chunk");
  }
    }

    /* Set pointer to newly allocated message */
    msg=&oh->mesg[idx];

    /* do we need to split the null message? */
    if (msg->raw_size > aligned_size) {
        H5O_mesg_t *null_msg;       /* Pointer to null message */
        size_t  mesg_size = aligned_size+ H5O_SIZEOF_MSGHDR(f); /* Total size of newly allocated message */

  assert(msg->raw_size - aligned_size >= H5O_SIZEOF_MSGHDR(f));

  if (oh->nmesgs >= oh->alloc_nmesgs) {
      int old_alloc=oh->alloc_nmesgs;
      unsigned na = oh->alloc_nmesgs + H5O_NMESGS;
      H5O_mesg_t *x = H5FL_SEQ_REALLOC (H5O_mesg_t, oh->mesg, (size_t)na);

      if (!x)
                HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, UFAIL, "memory allocation failed");
      oh->alloc_nmesgs = na;
      oh->mesg = x;

      /* Set new object header info to zeros */
      HDmemset(&oh->mesg[old_alloc],0,
         (oh->alloc_nmesgs-old_alloc)*sizeof(H5O_mesg_t));

            /* "Retarget" local 'msg' pointer into newly allocated array of messages */
            msg=&oh->mesg[idx];
  }
        null_msg=&oh->mesg[oh->nmesgs++];
  null_msg->type = H5O_NULL;
  null_msg->dirty = TRUE;
  null_msg->native = NULL;
  null_msg->raw = msg->raw + mesg_size;
  null_msg->raw_size = msg->raw_size - mesg_size;
  null_msg->chunkno = msg->chunkno;
  msg->raw_size = aligned_size;
    }

    /* initialize the new message */
    msg->type = type;
    msg->dirty = TRUE;
    msg->native = NULL;

    oh->cache_info.is_dirty = TRUE;

    /* Set return value */
    ret_value=idx;

done:
    FUNC_LEAVE_NOAPI(ret_value);
}

#ifdef NOT_YET

/*-------------------------------------------------------------------------
 * Function:  H5O_share
 *
 * Purpose:  Writes a message to the global heap.
 *
 * Return:  Success:  Non-negative, and HOBJ describes the global heap
 *        object.
 *
 *    Failure:  Negative
 *
 * Programmer:  Robb Matzke
 *              Thursday, April  2, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_share (H5F_t *f, hid_t dxpl_id, const H5O_class_t *type, const void *mesg,
     H5HG_t *hobj/*out*/)
{
    size_t  size;
    void  *buf = NULL;
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_share);

    /* Check args */
    assert (f);
    assert (type);
    assert (mesg);
    assert (hobj);

    /* Encode the message put it in the global heap */
    if ((size = (type->raw_size)(f, mesg))>0) {
  if (NULL==(buf = H5MM_malloc (size)))
      HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
  if ((type->encode)(f, buf, mesg)<0)
      HGOTO_ERROR (H5E_OHDR, H5E_CANTENCODE, FAIL, "unable to encode message");
  if (H5HG_insert (f, dxpl_id, size, buf, hobj)<0)
      HGOTO_ERROR (H5E_OHDR, H5E_CANTINIT, FAIL, "unable to store message in global heap");
    }

done:
    if(buf)
        H5MM_xfree (buf);

    FUNC_LEAVE_NOAPI(ret_value);
}
#endif /* NOT_YET */


/*-------------------------------------------------------------------------
 * Function:  H5O_raw_size
 *
 * Purpose:  Call the 'raw_size' method for a
 *              particular class of object header.
 *
 * Return:  Size of message on success, 0 on failure
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Feb 13 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
size_t
H5O_raw_size(unsigned type_id, const H5F_t *f, const void *mesg)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    size_t      ret_value;       /* Return value */

    FUNC_ENTER_NOAPI(H5O_raw_size,0)

    /* Check args */
    HDassert(type_id<NELMTS(message_type_g));
    type=message_type_g[type_id];    /* map the type ID to the actual type object */
    HDassert(type);
    HDassert(type->raw_size);
    HDassert(f);
    HDassert(mesg);

    /* Compute the raw data size for the mesg */
    if ((ret_value = (type->raw_size)(f, mesg))==0)
        HGOTO_ERROR (H5E_OHDR, H5E_CANTCOUNT, 0, "unable to determine size of message")

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


/*-------------------------------------------------------------------------
 * Function:  H5O_mesg_size
 *
 * Purpose:  Calculate the final size of an encoded message in an object
 *              header.
 *
 * Return:  Size of message on success, 0 on failure
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Sep  6 2005
 *
 *-------------------------------------------------------------------------
 */
size_t
H5O_mesg_size(unsigned type_id, const H5F_t *f, const void *mesg)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    size_t      ret_value;       /* Return value */

    FUNC_ENTER_NOAPI(H5O_mesg_size,0)

    /* Check args */
    HDassert(type_id<NELMTS(message_type_g));
    type=message_type_g[type_id];    /* map the type ID to the actual type object */
    HDassert(type);
    HDassert(type->raw_size);
    HDassert(f);
    HDassert(mesg);

    /* Compute the raw data size for the mesg */
    if((ret_value = (type->raw_size)(f, mesg)) == 0)
        HGOTO_ERROR (H5E_OHDR, H5E_CANTCOUNT, 0, "unable to determine size of message")

    /* Adjust size for alignment, if necessary */
    ret_value = H5O_ALIGN(ret_value);

    /* Add space for message header */
    ret_value += H5O_SIZEOF_MSGHDR(f);

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


/*-------------------------------------------------------------------------
 * Function:  H5O_get_share
 *
 * Purpose:  Call the 'get_share' method for a
 *              particular class of object header.
 *
 * Return:  Success:  Non-negative, and SHARE describes the shared
 *        object.
 *
 *    Failure:  Negative
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Oct  2 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_get_share(unsigned type_id, H5F_t *f, const void *mesg, H5O_shared_t *share)
{
    const H5O_class_t *type;    /* Actual H5O class type for the ID */
    herr_t ret_value;           /* Return value */

    FUNC_ENTER_NOAPI(H5O_get_share,FAIL);

    /* Check args */
    assert(type_id<NELMTS(message_type_g));
    type=message_type_g[type_id];    /* map the type ID to the actual type object */
    assert (type);
    assert (type->get_share);
    assert (f);
    assert (mesg);
    assert (share);

    /* Compute the raw data size for the mesg */
    if ((ret_value = (type->get_share)(f, mesg, share))<0)
        HGOTO_ERROR (H5E_OHDR, H5E_CANTGET, FAIL, "unable to retrieve shared message information");

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


/*-------------------------------------------------------------------------
 * Function:  H5O_delete
 *
 * Purpose:  Delete an object header from a file.  This frees the file
 *              space used for the object header (and it's continuation blocks)
 *              and also walks through each header message and asks it to
 *              remove all the pieces of the file referenced by the header.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Mar 19 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_delete(H5F_t *f, hid_t dxpl_id, haddr_t addr)
{
    H5O_t *oh=NULL;             /* Object header information */
    herr_t ret_value=SUCCEED;   /* Return value */

    FUNC_ENTER_NOAPI(H5O_delete,FAIL);

    /* Check args */
    assert (f);
    assert(H5F_addr_defined(addr));

    /* Get the object header information */
    if (NULL == (oh = H5AC_protect(f, dxpl_id, H5AC_OHDR, addr, NULL, NULL, H5AC_WRITE)))
  HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to load object header");

    /* Delete object */
    if(H5O_delete_oh(f,dxpl_id,oh)<0)
        HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "can't delete object from file");

done:
    if (oh && H5AC_unprotect(f, dxpl_id, H5AC_OHDR, addr, oh, TRUE)<0)
  HDONE_ERROR(H5E_OHDR, H5E_PROTECT, FAIL, "unable to release object header");

    FUNC_LEAVE_NOAPI(ret_value);
} /* end H5O_delete() */


/*-------------------------------------------------------------------------
 * Function:  H5O_delete_oh
 *
 * Purpose:  Internal function to:
 *              Delete an object header from a file.  This frees the file
 *              space used for the object header (and it's continuation blocks)
 *              and also walks through each header message and asks it to
 *              remove all the pieces of the file referenced by the header.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Mar 19 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_delete_oh(H5F_t *f, hid_t dxpl_id, H5O_t *oh)
{
    H5O_mesg_t *curr_msg;       /* Pointer to current message being operated on */
    H5O_chunk_t *curr_chk;      /* Pointer to current chunk being operated on */
    unsigned  u;
    herr_t ret_value=SUCCEED;   /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_delete_oh);

    /* Check args */
    assert (f);
    assert (oh);

    /* Walk through the list of object header messages, asking each one to
     * delete any file space used
     */
    for (u = 0, curr_msg=&oh->mesg[0]; u < oh->nmesgs; u++,curr_msg++) {
        /* Free any space referred to in the file from this message */
        if(H5O_delete_mesg(f, dxpl_id, curr_msg, TRUE)<0)
            HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "unable to delete file space for object header message");
    } /* end for */

    /* Free all the chunks for the object header */
    for (u = 0, curr_chk=&oh->chunk[0]; u < oh->nchunks; u++,curr_chk++) {
        haddr_t     chk_addr;   /* Actual address of chunk */
        hsize_t     chk_size;   /* Actual size of chunk */

        if(u==0) {
            chk_addr = curr_chk->addr - H5O_SIZEOF_HDR(f);
            chk_size = curr_chk->size + H5O_SIZEOF_HDR(f);
        } /* end if */
        else {
            chk_addr = curr_chk->addr;
            chk_size = curr_chk->size;
        } /* end else */

        /* Free the file space for the chunk */
  if (H5MF_xfree(f, H5FD_MEM_OHDR, dxpl_id, chk_addr, chk_size)<0)
      HGOTO_ERROR(H5E_OHDR, H5E_CANTFREE, FAIL, "unable to free object header");
    } /* end for */

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


/*-------------------------------------------------------------------------
 * Function:  H5O_delete_mesg
 *
 * Purpose:  Internal function to:
 *              Delete an object header message from a file.  This frees the file
 *              space used for anything referred to in the object header message.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    September 26 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_delete_mesg(H5F_t *f, hid_t dxpl_id, H5O_mesg_t *mesg, hbool_t adj_link)
{
    const H5O_class_t  *type;  /* Type of object to free */
    herr_t ret_value=SUCCEED;   /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_delete_mesg);

    /* Check args */
    assert (f);
    assert (mesg);

    /* Get the message to free's type */
    if(mesg->flags & H5O_FLAG_SHARED)
        type=H5O_SHARED;
    else
        type = mesg->type;

    /* Check if there is a file space deletion callback for this type of message */
    if(type->del) {
        /*
         * Decode the message if necessary.
         */
        if (NULL == mesg->native) {
            assert(type->decode);
            mesg->native = (type->decode) (f, dxpl_id, mesg->raw, NULL);
            if (NULL == mesg->native)
                HGOTO_ERROR(H5E_OHDR, H5E_CANTDECODE, FAIL, "unable to decode message");
        } /* end if */

        if ((type->del)(f, dxpl_id, mesg->native, adj_link)<0)
            HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "unable to delete file space for object header message");
    } /* end if */

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


/*-------------------------------------------------------------------------
 * Function:  H5O_get_info
 *
 * Purpose:  Retrieve information about an object header
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Oct  7 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_get_info(H5G_entry_t *ent, H5O_stat_t *ostat, hid_t dxpl_id)
{
    H5O_t *oh=NULL;             /* Object header information */
    H5O_mesg_t *curr_msg;       /* Pointer to current message being operated on */
    hsize_t total_size;         /* Total amount of space used in file */
    hsize_t free_space;         /* Free space in object header */
    unsigned u;                 /* Local index variable */
    herr_t ret_value=SUCCEED;   /* Return value */

    FUNC_ENTER_NOAPI(H5O_get_info,FAIL);

    /* Check args */
    assert (ent);
    assert (ostat);

    /* Get the object header information */
    if (NULL == (oh = H5AC_protect(ent->file, dxpl_id, H5AC_OHDR, ent->header, NULL, NULL, H5AC_READ)))
  HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to load object header");

    /* Iterate over all the messages, accumulating the total size & free space */
    total_size=H5O_SIZEOF_HDR(ent->file);
    free_space=0;
    for (u = 0, curr_msg=&oh->mesg[0]; u < oh->nmesgs; u++,curr_msg++) {
        /* Accumulate the size for this message */
        total_size+= H5O_SIZEOF_MSGHDR(ent->file) + curr_msg->raw_size;

        /* Check for this message being free space */
  if (H5O_NULL_ID == curr_msg->type->id)
            free_space+= H5O_SIZEOF_MSGHDR(ent->file) + curr_msg->raw_size;
    } /* end for */

    /* Set the information for this object header */
    ostat->size=total_size;
    ostat->free=free_space;
    ostat->nmesgs=oh->nmesgs;
    ostat->nchunks=oh->nchunks;

done:
    if (oh && H5AC_unprotect(ent->file, dxpl_id, H5AC_OHDR, ent->header, oh, FALSE)<0)
  HDONE_ERROR(H5E_OHDR, H5E_PROTECT, FAIL, "unable to release object header");

    FUNC_LEAVE_NOAPI(ret_value);
} /* end H5O_get_info() */


/*-------------------------------------------------------------------------
 * Function:  H5O_iterate
 *
 * Purpose:  Iterate through object headers of a certain type.
 *
 * Return:  Returns a negative value if something is wrong, the return
 *      value of the last operator if it was non-zero, or zero if all
 *      object headers were processed.
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Nov 19 2004
 *
 * Description:
 *      This function interates over the object headers of an object
 *  specified with 'ent' of type 'type_id'.  For each object header of the
 *  object, the 'op_data' and some additional information (specified below) are
 *  passed to the 'op' function.
 *      The operation receives a pointer to the object header message for the
 *  object being iterated over ('mesg'), and the pointer to the operator data
 *  passed in to H5O_iterate ('op_data').  The return values from an operator
 *  are:
 *      A. Zero causes the iterator to continue, returning zero when all
 *          object headers of that type have been processed.
 *      B. Positive causes the iterator to immediately return that positive
 *          value, indicating short-circuit success.
 *      C. Negative causes the iterator to immediately return that value,
 *          indicating failure.
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_iterate(const H5G_entry_t *ent, unsigned type_id, H5O_operator_t op,
    void *op_data, hid_t dxpl_id)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    herr_t              ret_value=0;    /* Return value */

    FUNC_ENTER_NOAPI(H5O_iterate, FAIL)

    /* check args */
    HDassert(ent);
    HDassert(ent->file);
    HDassert(H5F_addr_defined(ent->header));
    HDassert(type_id<NELMTS(message_type_g));
    type=message_type_g[type_id];    /* map the type ID to the actual type object */
    HDassert(type);

    /* Call the "real" iterate routine */
    if((ret_value=H5O_iterate_real(ent, type, H5AC_READ, FALSE, (void *)op, op_data, dxpl_id))<0)
  HGOTO_ERROR(H5E_OHDR, H5E_BADITER, FAIL, "unable to iterate over object header messages")

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


/*-------------------------------------------------------------------------
 * Function:  H5O_iterate_real
 *
 * Purpose:  Iterate through object headers of a certain type.
 *
 * Return:  Returns a negative value if something is wrong, the return
 *      value of the last operator if it was non-zero, or zero if all
 *      object headers were processed.
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Sep  6 2005
 *
 * Description:
 *      This function interates over the object headers of an object
 *  specified with 'ent' of type 'type_id'.  For each object header of the
 *  object, the 'op_data' and some additional information (specified below) are
 *  passed to the 'op' function.
 *      The operation receives a pointer to the object header message for the
 *  object being iterated over ('mesg'), and the pointer to the operator data
 *  passed in to H5O_iterate ('op_data').  The return values from an operator
 *  are:
 *      A. Zero causes the iterator to continue, returning zero when all
 *          object headers of that type have been processed.
 *      B. Positive causes the iterator to immediately return that positive
 *          value, indicating short-circuit success.
 *      C. Negative causes the iterator to immediately return that value,
 *          indicating failure.
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5O_iterate_real(const H5G_entry_t *ent, const H5O_class_t *type, H5AC_protect_t prot,
    hbool_t internal, void *op, void *op_data, hid_t dxpl_id)
{
    H5O_t    *oh=NULL;       /* Pointer to actual object header */
    unsigned            oh_flags = FALSE;  /* Start iteration with no flags set on object header */
    unsigned    idx;            /* Absolute index of current message in all messages */
    unsigned    sequence;       /* Relative index of current message for messages of type */
    H5O_mesg_t         *idx_msg;        /* Pointer to current message */
    herr_t              ret_value=0;    /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5O_iterate_real)

    /* check args */
    HDassert(ent);
    HDassert(ent->file);
    HDassert(H5F_addr_defined(ent->header));
    HDassert(type);
    HDassert(op);

    /* Protect the object header to iterate over */
    if (NULL == (oh = H5AC_protect(ent->file, dxpl_id, H5AC_OHDR, ent->header, NULL, NULL, prot)))
  HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to load object header");

    /* Iterate over messages */
    for (sequence = 0, idx = 0, idx_msg = &oh->mesg[0]; idx < oh->nmesgs; idx++, idx_msg++) {
  if (type->id == idx_msg->type->id) {
            /*
             * Decode the message if necessary.  If the message is shared then decode
             * a shared message, ignoring the message type.
             */
            if (NULL == idx_msg->native) {
                const H5O_class_t  *decode_type;

                if (idx_msg->flags & H5O_FLAG_SHARED)
                    decode_type = H5O_SHARED;
                else
                    decode_type = type;

                /* Decode the message if necessary */
                HDassert(decode_type->decode);
                if (NULL == (idx_msg->native = (decode_type->decode) (ent->file, dxpl_id, idx_msg->raw, NULL)))
                    HGOTO_ERROR(H5E_OHDR, H5E_CANTDECODE, FAIL, "unable to decode message")
            } /* end if */

            /* Check for making an "internal" (i.e. within the H5O package) callback */
            if(internal) {
                /* Call the "internal" iterator callback */
                if((ret_value = ((H5O_operator_int_t)op)(idx_msg, sequence, &oh_flags, op_data)) != 0)
                    break;
            } /* end if */
            else {
                /* Call the iterator callback */
                if((ret_value = ((H5O_operator_t)op)(idx_msg->native, sequence, op_data)) != 0)
                    break;
            } /* end else */

            /* Increment sequence value for message type */
            sequence++;
        } /* end if */
    } /* end for */

    /* Check if object header was modified */
    if(oh_flags) {
        /* Shouldn't be able to modify object header if we don't have write access */
        HDassert(prot == H5AC_WRITE);
        oh->cache_info.is_dirty = TRUE;
        H5O_touch_oh(ent->file, oh, FALSE);
    } /* end if */

done:
    if (oh && H5AC_unprotect(ent->file, dxpl_id, H5AC_OHDR, ent->header, oh, FALSE) < 0)
  HDONE_ERROR(H5E_OHDR, H5E_PROTECT, FAIL, "unable to release object header")

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


/*-------------------------------------------------------------------------
 * Function:  H5O_debug_id
 *
 * Purpose:  Act as a proxy for calling the 'debug' method for a
 *              particular class of object header.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Quincey Koziol
 *    koziol@ncsa.uiuc.edu
 *    Feb 13 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5O_debug_id(hid_t type_id, H5F_t *f, hid_t dxpl_id, const void *mesg, FILE *stream, int indent, int fwidth)
{
    const H5O_class_t *type;            /* Actual H5O class type for the ID */
    herr_t      ret_value;       /* Return value */

    FUNC_ENTER_NOAPI(H5O_debug_id,FAIL);

    /* Check args */
    assert(type_id>=0 && type_id<(hid_t)(sizeof(message_type_g)/sizeof(message_type_g[0])));
    type=message_type_g[type_id];    /* map the type ID to the actual type object */
    assert(type);
    assert(type->debug);
    assert(f);
    assert(mesg);
    assert(stream);
    assert(indent >= 0);
    assert(fwidth >= 0);

    /* Call the debug method in the class */
    if ((ret_value = (type->debug)(f, dxpl_id, mesg, stream, indent, fwidth))<0)
        HGOTO_ERROR (H5E_OHDR, H5E_BADTYPE, FAIL, "unable to debug message");

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


/*-------------------------------------------------------------------------
 * Function:  H5O_debug
 *
 * Purpose:  Prints debugging info about an object header.
 *
 * Return:  Non-negative on success/Negative on failure
 *
 * Programmer:  Robb Matzke
 *    matzke@llnl.gov
 *    Aug  6 1997
 *
 * Modifications:
 *    Robb Matzke, 1999-07-28
 *    The ADDR argument is passed by value.
 *-------------------------------------------------------------------------
 */
herr_t
H5O_debug(H5F_t *f, hid_t dxpl_id, haddr_t addr, FILE *stream, int indent, int fwidth)
{
    H5O_t  *oh = NULL;
    unsigned  i, chunkno;
    size_t  mesg_total = 0, chunk_total = 0;
    int    *sequence;
    haddr_t  tmp_addr;
    herr_t  ret_value = SUCCEED;
    void  *(*decode)(H5F_t*, hid_t, const uint8_t*, H5O_shared_t*);
    herr_t      (*debug)(H5F_t*, hid_t, const void*, FILE*, int, int)=NULL;

    FUNC_ENTER_NOAPI(H5O_debug, FAIL);

    /* check args */
    assert(f);
    assert(H5F_addr_defined(addr));
    assert(stream);
    assert(indent >= 0);
    assert(fwidth >= 0);

    if (NULL == (oh = H5AC_protect(f, dxpl_id, H5AC_OHDR, addr, NULL, NULL, H5AC_READ)))
  HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to load object header");

    /* debug */
    HDfprintf(stream, "%*sObject Header...\n", indent, "");

    HDfprintf(stream, "%*s%-*s %d\n", indent, "", fwidth,
        "Dirty:",
        (int) (oh->cache_info.is_dirty));
    HDfprintf(stream, "%*s%-*s %d\n", indent, "", fwidth,
        "Version:",
        (int) (oh->version));
    HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth,
        "Header size (in bytes):",
        (unsigned) H5O_SIZEOF_HDR(f));
    HDfprintf(stream, "%*s%-*s %d\n", indent, "", fwidth,
        "Number of links:",
        (int) (oh->nlink));
    HDfprintf(stream, "%*s%-*s %u (%u)\n", indent, "", fwidth,
        "Number of messages (allocated):",
        (unsigned) (oh->nmesgs), (unsigned) (oh->alloc_nmesgs));
    HDfprintf(stream, "%*s%-*s %u (%u)\n", indent, "", fwidth,
        "Number of chunks (allocated):",
        (unsigned) (oh->nchunks), (unsigned) (oh->alloc_nchunks));

    /* debug each chunk */
    for (i=0, chunk_total=0; i<oh->nchunks; i++) {
  chunk_total += oh->chunk[i].size;
  HDfprintf(stream, "%*sChunk %d...\n", indent, "", i);

  HDfprintf(stream, "%*s%-*s %d\n", indent + 3, "", MAX(0, fwidth - 3),
      "Dirty:",
      (int) (oh->chunk[i].dirty));

  HDfprintf(stream, "%*s%-*s %a\n", indent + 3, "", MAX(0, fwidth - 3),
      "Address:", oh->chunk[i].addr);

  tmp_addr = addr + (hsize_t)H5O_SIZEOF_HDR(f);
  if (0 == i && H5F_addr_ne(oh->chunk[i].addr, tmp_addr))
      HDfprintf(stream, "*** WRONG ADDRESS!\n");
  HDfprintf(stream, "%*s%-*s %lu\n", indent + 3, "", MAX(0, fwidth - 3),
      "Size in bytes:",
      (unsigned long) (oh->chunk[i].size));
    }

    /* debug each message */
    if (NULL==(sequence = H5MM_calloc(NELMTS(message_type_g)*sizeof(int))))
  HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
    for (i=0, mesg_total=0; i<oh->nmesgs; i++) {
  mesg_total += H5O_SIZEOF_MSGHDR(f) + oh->mesg[i].raw_size;
  HDfprintf(stream, "%*sMessage %d...\n", indent, "", i);

  /* check for bad message id */
  if (oh->mesg[i].type->id < 0 ||
      oh->mesg[i].type->id >= (int)NELMTS(message_type_g)) {
      HDfprintf(stream, "*** BAD MESSAGE ID 0x%04x\n",
          oh->mesg[i].type->id);
      continue;
  }

  /* message name and size */
  HDfprintf(stream, "%*s%-*s 0x%04x `%s' (%d)\n",
      indent + 3, "", MAX(0, fwidth - 3),
      "Message ID (sequence number):",
      (unsigned) (oh->mesg[i].type->id),
      oh->mesg[i].type->name,
      sequence[oh->mesg[i].type->id]++);
  HDfprintf (stream, "%*s%-*s %s\n", indent+3, "", MAX (0, fwidth-3),
       "Shared:",
       (oh->mesg[i].flags & H5O_FLAG_SHARED) ? "Yes" : "No");
  HDfprintf(stream, "%*s%-*s %s\n", indent + 3, "", MAX(0, fwidth - 3),
      "Constant:",
      (oh->mesg[i].flags & H5O_FLAG_CONSTANT) ? "Yes" : "No");
  if (oh->mesg[i].flags & ~H5O_FLAG_BITS) {
      HDfprintf (stream, "%*s%-*s 0x%02x\n", indent+3,"",MAX(0,fwidth-3),
           "*** ADDITIONAL UNKNOWN FLAGS --->",
           oh->mesg[i].flags & ~H5O_FLAG_BITS);
  }
  HDfprintf(stream, "%*s%-*s %lu bytes\n", indent+3, "", MAX(0,fwidth-3),
      "Raw size in obj header:",
      (unsigned long) (oh->mesg[i].raw_size));
  HDfprintf(stream, "%*s%-*s %d\n", indent + 3, "", MAX(0, fwidth - 3),
      "Chunk number:",
      (int) (oh->mesg[i].chunkno));
  chunkno = oh->mesg[i].chunkno;
  if (chunkno >= oh->nchunks)
      HDfprintf(stream, "*** BAD CHUNK NUMBER\n");

  /* check the size */
  if ((oh->mesg[i].raw + oh->mesg[i].raw_size >
                 oh->chunk[chunkno].image + oh->chunk[chunkno].size) ||
                (oh->mesg[i].raw < oh->chunk[chunkno].image)) {
      HDfprintf(stream, "*** BAD MESSAGE RAW ADDRESS\n");
  }

  /* decode the message */
  if (oh->mesg[i].flags & H5O_FLAG_SHARED) {
      decode = H5O_SHARED->decode;
      debug = H5O_SHARED->debug;
  } else {
      decode = oh->mesg[i].type->decode;
      debug = oh->mesg[i].type->debug;
  }
  if (NULL==oh->mesg[i].native && decode)
      oh->mesg[i].native = (decode)(f, dxpl_id, oh->mesg[i].raw, NULL);
  if (NULL==oh->mesg[i].native)
      debug = NULL;

  /* print the message */
  HDfprintf(stream, "%*s%-*s\n", indent + 3, "", MAX(0, fwidth - 3),
      "Message Information:");
  if (debug)
      (debug)(f, dxpl_id, oh->mesg[i].native, stream, indent+6, MAX(0, fwidth-6));
  else
      HDfprintf(stream, "%*s<No info for this message>\n", indent + 6, "");

  /* If the message is shared then also print the pointed-to message */
  if (oh->mesg[i].flags & H5O_FLAG_SHARED) {
      H5O_shared_t *shared = (H5O_shared_t*)(oh->mesg[i].native);
      void *mesg = NULL;

      if (shared->in_gh) {
    void *p = H5HG_read (f, dxpl_id, oh->mesg[i].native, NULL);
    mesg = (oh->mesg[i].type->decode)(f, dxpl_id, p, oh->mesg[i].native);
    H5MM_xfree (p);
      } else {
    mesg = H5O_read_real(&(shared->u.ent), oh->mesg[i].type, 0, NULL, dxpl_id);
      }
      if (oh->mesg[i].type->debug) {
    (oh->mesg[i].type->debug)(f, dxpl_id, mesg, stream, indent+3,
            MAX (0, fwidth-3));
      }
      H5O_free_real(oh->mesg[i].type, mesg);
  }
    }
    sequence = H5MM_xfree(sequence);

    if (mesg_total != chunk_total)
  HDfprintf(stream, "*** TOTAL SIZE DOES NOT MATCH ALLOCATED SIZE!\n");

done:
    if (oh && H5AC_unprotect(f, dxpl_id, H5AC_OHDR, addr, oh, FALSE) < 0)
  HDONE_ERROR(H5E_OHDR, H5E_PROTECT, FAIL, "unable to release object header");

    FUNC_LEAVE_NOAPI(ret_value);
}
