/*
 * Copyright � 1999-2001 NCSA
 *                       All rights reserved.
 *
 * Programmer:  Robb Matzke <matzke@llnl.gov>
 *              Monday, July 26, 1999
 *
 * Purpose:	The Virtual File Layer as described in documentation. This is
 *		the greatest common denominator for all types of storage
 *		access whether a file, memory, network, etc. This layer
 *		usually just dispatches the request to an actual file driver
 *		layer.
 */

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

/* Packages needed by this file */
#include "H5private.h"		/*library functions			*/
#include "H5Dprivate.h"		/*datasets      			*/
#include "H5Eprivate.h"		/*error handling			*/
#include "H5Fpkg.h"		/*files					*/
#include "H5FDprivate.h"	/*virtual file driver			*/
#include "H5FLprivate.h"	/*Free Lists	  */
#include "H5Iprivate.h"		/*interface abstraction layer		*/
#include "H5MMprivate.h"	/*memory management			*/
#include "H5Pprivate.h"		/*property lists			*/

/* Interface initialization */
#define PABLO_MASK	H5FD_mask
#define INTERFACE_INIT	H5FD_init_interface
static int interface_initialize_g = 0;

/* static prototypes */
static herr_t H5FD_init_interface(void);
static herr_t H5FD_free_cls(H5FD_class_t *cls);
static haddr_t H5FD_real_alloc(H5FD_t *file, H5FD_mem_t type, hsize_t size);

/* Declare a free list to manage the H5FD_free_t struct */
H5FL_DEFINE(H5FD_free_t);

/* Declare a PQ free list to manage the metadata accumulator buffer */
H5FL_BLK_DEFINE_STATIC(meta_accum);

/* Local macro definitions */
#define H5FD_ACCUM_THROTTLE     8
#define H5FD_ACCUM_THRESHOLD    2048

/* Static local variables */

/* Global count of the number of H5FD_t's handed out.  This is used as a
 * "serial number" for files that are currently open and is used for the
 * 'fileno[2]' field in H5G_stat_t.  However, if a VFL driver is not able
 * to detect whether two files are the same, a file that has been opened
 * by H5Fopen more than once with that VFL driver will have two different
 * serial numbers.  :-/
 *
 * Also, if a file is opened, the 'fileno[2]' field is retrieved for an
 * object and the file is closed and re-opened, the 'fileno[2]' value will
 * be different.
 */
static unsigned long file_serial_no[2];

/*-------------------------------------------------------------------------
 * Function:	H5FD_init_interface
 *
 * Purpose:	Initialize the virtual file layer.
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Monday, July 26, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD_init_interface(void)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOINIT(H5FD_init_interface);

    if (H5I_init_group(H5I_VFL, H5I_VFL_HASHSIZE, 0,
		       (H5I_free_t)H5FD_free_cls)<0)
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "unable to initialize interface");

    /* Reset the file serial numbers */
    HDmemset(file_serial_no,0,sizeof(file_serial_no));

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_term_interface
 *
 * Purpose:	Terminate this interface: free all memory and reset global
 *		variables to their initial values.  Release all ID groups
 *		associated with this interface.
 *
 * Return:	Success:	Positive if anything was done that might
 *				have affected other interfaces; zero
 *				otherwise.
 *
 *		Failure:        Never fails.
 *
 * Programmer:	Robb Matzke
 *              Friday, February 19, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
int
H5FD_term_interface(void)
{
    int	n = 0;

    FUNC_ENTER_NOINIT(H5_term_interface);

    if (interface_initialize_g) {
	if ((n=H5I_nmembers(H5I_VFL))) {
	    H5I_clear_group(H5I_VFL, FALSE);
	} else {
	    H5I_destroy_group(H5I_VFL);
	    interface_initialize_g = 0;
	    n = 1; /*H5I*/
	}
    }
    FUNC_LEAVE(n);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_free_cls
 *
 * Purpose:	Frees a file driver class struct and returns an indication of
 *		success. This function is used as the free callback for the
 *		virtual file layer object identifiers (cf H5FD_init_interface).
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Monday, July 26, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD_free_cls(H5FD_class_t *cls)
{
    FUNC_ENTER_NOINIT(H5FD_free_cls);

    H5MM_xfree(cls);

    FUNC_LEAVE(SUCCEED);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDregister
 *
 * Purpose:	Registers a new file driver as a member of the virtual file
 *		driver class.  Certain fields of the class struct are
 *		required and that is checked here so it doesn't have to be
 *		checked every time the field is accessed.
 *
 * Return:	Success:	A file driver ID which is good until the
 *				library is closed or the driver is
 *				unregistered.
 *
 *		Failure:	A negative value.
 *
 * Programmer:	Robb Matzke
 *              Monday, July 26, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
hid_t
H5FDregister(const H5FD_class_t *cls)
{
    hid_t		ret_value;
    H5FD_class_t	*saved=NULL;
    H5FD_mem_t		type;

    FUNC_ENTER_API(H5FDregister, FAIL);
    H5TRACE1("i","x",cls);

    /* Check arguments */
    if (!cls)
	HGOTO_ERROR(H5E_ARGS, H5E_UNINITIALIZED, FAIL, "null class pointer is disallowed");

    if (!cls->open || !cls->close)
	HGOTO_ERROR(H5E_ARGS, H5E_UNINITIALIZED, FAIL, "`open' and/or `close' methods are not defined");

    if (!cls->get_eoa || !cls->set_eoa)
	HGOTO_ERROR(H5E_ARGS, H5E_UNINITIALIZED, FAIL, "`get_eoa' and/or `set_eoa' methods are not defined");

    if (!cls->get_eof)
	HGOTO_ERROR(H5E_ARGS, H5E_UNINITIALIZED, FAIL, "`get_eof' method is not defined");
    if (!cls->read || !cls->write)
	HGOTO_ERROR(H5E_ARGS, H5E_UNINITIALIZED, FAIL, "`read' and/or `write' method is not defined");
    for (type=H5FD_MEM_DEFAULT; type<H5FD_MEM_NTYPES; H5_INC_ENUM(H5FD_mem_t,type)) {
	if (cls->fl_map[type]<H5FD_MEM_NOLIST ||
                cls->fl_map[type]>=H5FD_MEM_NTYPES)
	    HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid free-list mapping");
    }

    /* Copy the class structure so the caller can reuse or free it */
    if (NULL==(saved=H5MM_malloc(sizeof(H5FD_class_t))))
	HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for file driver class struct");
    *saved = *cls;

    /* Create the new class ID */
    if ((ret_value=H5I_register(H5I_VFL, saved))<0)
        HGOTO_ERROR(H5E_ATOM, H5E_CANTREGISTER, FAIL, "unable to register file driver ID");

done:
    if(ret_value<0)
        if(saved)
            H5MM_xfree(saved);

    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDunregister
 *
 * Purpose:	Removes a driver ID from the library. This in no way affects
 *		file access property lists which have been defined to use
 *		this driver or files which are already opened under this
 *		driver.
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Monday, July 26, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FDunregister(hid_t driver_id)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_API(H5FDunregister, FAIL);
    H5TRACE1("e","i",driver_id);

    /* Check arguments */
    if (NULL==H5I_object_verify(driver_id,H5I_VFL))
	HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file driver");

    /* The H5FD_class_t struct will be freed by this function */
    if (H5I_dec_ref(driver_id)<0)
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "unable to unregister file driver");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_get_class
 *
 * Purpose:	Optains a pointer to the driver struct containing all the
 *		callback pointers, etc. The PLIST_ID argument can be a file
 *		access property list, a data transfer property list, or a
 *		file driver identifier.
 *
 * Return:	Success:	Ptr to the driver information. The pointer is
 *				only valid as long as the driver remains
 *				registered or some file or property list
 *				exists which references the driver.
 *
 *		Failure:	NULL
 *
 * Programmer:	Robb Matzke
 *              Friday, August 20, 1999
 *
 * Modifications:
 *
 *		Raymond Lu 
 * 		Tuesday, Oct 23, 2001
 *		Changed the file access list to the new generic property 
 *		list.
 *
 *-------------------------------------------------------------------------
 */
H5FD_class_t *
H5FD_get_class(hid_t id)
{
    H5P_genplist_t *plist;      /* Property list pointer */
    H5FD_class_t	*ret_value=NULL;
    hid_t               driver_id = -1;

    FUNC_ENTER_NOAPI(H5FD_get_class, NULL);

    if (H5I_VFL==H5I_get_type(id)) {
	ret_value = H5I_object(id);
    } else {
        /* Get the plist structure */
        if(NULL == (plist = H5I_object(id)))
            HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, NULL, "can't find object for ID");

        if (TRUE==H5P_isa_class(id,H5P_FILE_ACCESS)) {
            if(H5P_get(plist, H5F_ACS_FILE_DRV_ID_NAME, &driver_id) < 0)
                HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get driver ID");
            ret_value = H5FD_get_class(driver_id);
        } else if (TRUE==H5P_isa_class(id,H5P_DATASET_XFER)) {
            if(H5P_get(plist, H5D_XFER_VFL_ID_NAME, &driver_id) < 0)
                HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get driver ID");
            ret_value = H5FD_get_class(driver_id);
        } else {
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a driver id, file access property list or data transfer property list");
        }
    } /* end if */

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_sb_size
 *
 * Purpose:	Obtains the number of bytes required to store the driver file
 *		access data in the HDF5 superblock.
 *
 * Return:	Success:	Number of bytes required.
 *
 *		Failure:	0 if an error occurs or if the driver has no
 *				data to store in the superblock.
 *
 * Programmer:	Robb Matzke
 *              Monday, August 16, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
hsize_t
H5FD_sb_size(H5FD_t *file)
{
    hsize_t	ret_value=0;
    
    FUNC_ENTER_NOAPI(H5FD_sb_size, 0);

    assert(file && file->cls);

    if (file->cls->sb_size)
	ret_value = (file->cls->sb_size)(file);

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_sb_encode
 *
 * Purpose:	Encode driver-specific data into the output arguments. The
 *		NAME is a nine-byte buffer which should get an
 *		eight-character driver name and/or version followed by a null
 *		terminator. The BUF argument is a buffer to receive the
 *		encoded driver-specific data. The size of the BUF array is
 *		the size returned by the H5FD_sb_size() call.
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Monday, August 16, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FD_sb_encode(H5FD_t *file, char *name/*out*/, uint8_t *buf)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI(H5FD_sb_encode, FAIL);

    assert(file && file->cls);
    if (file->cls->sb_encode &&
            (file->cls->sb_encode)(file, name/*out*/, buf/*out*/)<0)
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "driver sb_encode request failed");
    
done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_sb_decode
 *
 * Purpose:	Decodes the driver information block.
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Monday, August 16, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FD_sb_decode(H5FD_t *file, const char *name, const uint8_t *buf)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI(H5FD_sb_decode, FAIL);

    assert(file && file->cls);
    if (file->cls->sb_decode &&
            (file->cls->sb_decode)(file, name, buf)<0)
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "driver sb_decode request failed");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_fapl_get
 *
 * Purpose:	Gets the file access property list associated with a file.
 *		Usually the file will copy what it needs from the original
 *		file access property list when the file is created. The
 *		purpose of this function is to create a new file access
 *		property list based on the settings in the file, which may
 *		have been modified from the original file access property
 *		list.
 *
 * Return:	Success:	Pointer to a new file access property list
 *				with all members copied.  If the file is
 *				closed then this property list lives on, and
 *				vice versa.
 *
 *		Failure:	NULL, including when the file has no
 *				properties.
 *
 * Programmer:	Robb Matzke
 *              Friday, August 13, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
void *
H5FD_fapl_get(H5FD_t *file)
{
    void	*ret_value=NULL;
    
    FUNC_ENTER_NOAPI(H5FD_fapl_get, NULL);

    assert(file);

    if (file->cls->fapl_get)
	ret_value = (file->cls->fapl_get)(file);

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_fapl_copy
 *
 * Purpose:	Copies the driver-specific part of the file access property
 *		list.
 *
 * Return:	Success:	Pointer to new driver-specific file access
 *				properties.
 *
 *		Failure:	NULL, but also returns null with no error
 *				pushed onto the error stack if the OLD_FAPL
 *				is null.
 *
 * Programmer:	Robb Matzke
 *              Tuesday, August  3, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
void *
H5FD_fapl_copy(hid_t driver_id, const void *old_fapl)
{
    void		*new_fapl = NULL;
    H5FD_class_t	*driver=NULL;
    void        *ret_value;       /* Return value */
    
    FUNC_ENTER_NOAPI(H5FD_fapl_copy, NULL);

    /* Check args */
    if (NULL==(driver=H5I_object_verify(driver_id,H5I_VFL)))
	HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a driver ID");
    if (!old_fapl)
        HGOTO_DONE(NULL); /*but no error*/

    /* Allow the driver to copy or do it ourselves */
    if (driver->fapl_copy) {
	new_fapl = (driver->fapl_copy)(old_fapl);
    } else if (driver->fapl_size>0) {
	new_fapl = H5MM_malloc(driver->fapl_size);
	HDmemcpy(new_fapl, old_fapl, driver->fapl_size);
    } else
	HGOTO_ERROR(H5E_VFL, H5E_UNSUPPORTED, NULL, "no way to copy driver file access property list");

    /* Set return value */
    ret_value=new_fapl;

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_fapl_free
 *
 * Purpose:	Frees the driver-specific file access property list.
 *
 * Return:	Success:	non-negative
 *
 *		Failure:	negative
 *
 * Programmer:	Robb Matzke
 *              Tuesday, August  3, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FD_fapl_free(hid_t driver_id, void *fapl)
{
    H5FD_class_t	*driver=NULL;
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI(H5FD_fapl_free, FAIL);

    /* Check args */
    if (NULL==(driver=H5I_object_verify(driver_id,H5I_VFL)))
	HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a driver ID");
    
    /* Allow driver to free or do it ourselves */
    if (fapl && driver->fapl_free) {
	if ((driver->fapl_free)(fapl)<0)
	    HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "driver fapl_free request failed");
    } else {
	H5MM_xfree(fapl);
    }

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_dxpl_copy
 *
 * Purpose:	Copies the driver-specific part of the data transfer property
 *		list.
 *
 * Return:	Success:	Pointer to new driver-specific data transfer
 *				properties.
 *
 *		Failure:	NULL, but also returns null with no error
 *				pushed onto the error stack if the OLD_DXPL
 *				is null.
 *
 * Programmer:	Robb Matzke
 *              Tuesday, August  3, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
void *
H5FD_dxpl_copy(hid_t driver_id, const void *old_dxpl)
{
    void		*new_dxpl = NULL;
    H5FD_class_t	*driver=NULL;
    void *ret_value;       /* Return value */
    
    FUNC_ENTER_NOAPI(H5FD_dxpl_copy, NULL);

    /* Check args */
    if (NULL==(driver=H5I_object_verify(driver_id,H5I_VFL)))
	HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a driver ID");
    if (!old_dxpl)
        HGOTO_DONE(NULL); /*but no error*/

    /* Allow the driver to copy or do it ourselves */
    if (driver->dxpl_copy) {
	new_dxpl = (driver->dxpl_copy)(old_dxpl);
    } else if (driver->dxpl_size>0) {
	new_dxpl = H5MM_malloc(driver->dxpl_size);
	HDmemcpy(new_dxpl, old_dxpl, driver->dxpl_size);
    } else
	HGOTO_ERROR(H5E_VFL, H5E_UNSUPPORTED, NULL, "no way to copy driver file access property list");

    /* Set return value */
    ret_value=new_dxpl;

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_dxpl_free
 *
 * Purpose:	Frees the driver-specific data transfer property list.
 *
 * Return:	Success:	non-negative
 *
 *		Failure:	negative
 *
 * Programmer:	Robb Matzke
 *              Tuesday, August  3, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FD_dxpl_free(hid_t driver_id, void *dxpl)
{
    H5FD_class_t	*driver=NULL;
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI(H5FD_dxpl_free, FAIL);

    /* Check args */
    if (NULL==(driver=H5I_object_verify(driver_id,H5I_VFL)))
	HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a driver ID");
    
    /* Allow driver to free or do it ourselves */
    if (dxpl && driver->dxpl_free) {
	if ((driver->dxpl_free)(dxpl)<0)
	    HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "driver dxpl_free request failed");
    } else {
	H5MM_xfree(dxpl);
    }

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDopen
 *
 * Purpose:	Opens a file named NAME for the type(s) of access described
 *		by the bit vector FLAGS according to a file access property
 *		list FAPL_ID (which may be the constant H5P_DEFAULT). The
 *		file should expect to handle format addresses in the range [0,
 *		MAXADDR] (if MAXADDR is the undefined address then the caller
 *		doesn't care about the address range).
 *
 * 		Possible values for the FLAGS bits are:
 *
 *		H5F_ACC_RDWR:	Open the file for read and write access. If
 *				this bit is not set then open the file for
 *				read only access. It is permissible to open a
 *				file for read and write access when only read
 *				access is requested by the library (the
 *				library will never attempt to write to a file
 *				which it opened with only read access).
 *
 *		H5F_ACC_CREATE:	Create the file if it doesn't already exist.
 *				However, see H5F_ACC_EXCL below.
 *
 *		H5F_ACC_TRUNC:	Truncate the file if it already exists. This
 *				is equivalent to deleting the file and then
 *				creating a new empty file.
 *
 *		H5F_ACC_EXCL:	When used with H5F_ACC_CREATE, if the file
 *				already exists then the open should fail.
 *				Note that this is unsupported/broken with
 *				some file drivers (e.g., sec2 across nfs) and
 *				will contain a race condition when used to
 *				perform file locking.
 *
 *		The MAXADDR is the maximum address which will be requested by
 *		the library during an allocation operation. Usually this is
 *		the same value as the MAXADDR field of the class structure,
 *		but it can be smaller if the driver is being used under some
 *		other driver.
 *
 *		Note that when the driver `open' callback gets control that
 *		the public part of the file struct (the H5FD_t part) will be
 *		incomplete and will be filled in after that callback returns.
 *
 * Return:	Success:	Pointer to a new file driver struct.
 *
 *		Failure:	NULL
 *
 * Programmer:	Robb Matzke
 *              Tuesday, July 27, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
H5FD_t *
H5FDopen(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr)
{
    H5FD_t	*ret_value=NULL;

    FUNC_ENTER_API(H5FDopen, NULL);

    if (NULL==(ret_value=H5FD_open(name, flags, fapl_id, maxaddr)))
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, NULL, "unable to open file");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_open
 *
 * Purpose:	Private version of H5FDopen()
 *
 * Return:	Success:	Pointer to a new file driver struct
 *
 *		Failure:	NULL
 *
 * Programmer:	Robb Matzke
 *              Wednesday, August  4, 1999
 *
 * Modifications:
 *
 *		Raymond Lu 
 * 		Tuesday, Oct 23, 2001
 *		Changed the file access list to the new generic property 
 *		list.
 *
 *-------------------------------------------------------------------------
 */
H5FD_t *
H5FD_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr)
{
    H5FD_class_t	*driver;
    H5FD_t		*file=NULL;
    hid_t               driver_id = -1;
    hsize_t             meta_block_size=0;    
    hsize_t             sdata_block_size=0;    
    H5P_genplist_t *plist;      /* Property list pointer */
    H5FD_t		*ret_value;
    
    FUNC_ENTER_NOAPI(H5FD_open, NULL);

    /* Check arguments */
    if(H5P_DEFAULT == fapl_id)
        fapl_id = H5P_FILE_ACCESS_DEFAULT;
    if(NULL == (plist = H5P_object_verify(fapl_id,H5P_FILE_ACCESS)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a file access property list"); 
 
    if (0==maxaddr)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "zero format address range");

    if(H5P_get(plist, H5F_ACS_FILE_DRV_ID_NAME, &driver_id) < 0)
        HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get driver ID");

    /* Get driver info */
    if (NULL==(driver=H5I_object_verify(driver_id,H5I_VFL)))
	HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "invalid driver ID in file access property list");
    if (NULL==driver->open)
	HGOTO_ERROR(H5E_VFL, H5E_UNSUPPORTED, NULL, "file driver has no `open' method");
    
    /* Dispatch to file driver */
    if (HADDR_UNDEF==maxaddr)
        maxaddr = driver->maxaddr;
    if (NULL==(file=(driver->open)(name, flags, fapl_id, maxaddr)))
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, NULL, "open failed");

    /*
     * Fill in public fields. We must increment the reference count on the
     * driver ID to prevent it from being freed while this file is open.
     */
    file->driver_id = driver_id;
    H5I_inc_ref(file->driver_id);
    file->cls = driver;
    file->maxaddr = maxaddr;
    HDmemset(file->fl, 0, sizeof(file->fl));
    if(H5P_get(plist, H5F_ACS_META_BLOCK_SIZE_NAME, &(meta_block_size)) < 0)
        HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get meta data block size");
    file->def_meta_block_size = meta_block_size;
    if(H5P_get(plist, H5F_ACS_SDATA_BLOCK_SIZE_NAME, &(sdata_block_size)) < 0)
        HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get 'small data' block size");
    file->def_sdata_block_size = sdata_block_size;
    file->accum_loc = HADDR_UNDEF;
    if(H5P_get(plist, H5F_ACS_ALIGN_THRHD_NAME, &(file->threshold)) < 0)
        HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get alignment threshold");
    if(H5P_get(plist, H5F_ACS_ALIGN_NAME, &(file->alignment)) < 0)
        HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get alignment");    

    /* Retrieve the VFL driver feature flags */
    if (H5FD_query(file, &(file->feature_flags))<0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, NULL, "unable to query file driver");

    /* Increment the global serial number & assign it to this H5FD_t object */
    if(++file_serial_no[0]==0) {
        /* (Just error out if we wrap both numbers around for now...) */
        if(++file_serial_no[1]==0)
            HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, NULL, "unable to get file serial number");
    } /* end if */
    HDmemcpy(file->fileno,file_serial_no,sizeof(file_serial_no));

    /* Set return value */
    ret_value=file;

done:
    /* Can't cleanup 'file' information, since we don't know what type it is */
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDclose
 *
 * Purpose:     Closes the file by calling the driver `close' callback, which
 *		should free all driver-private data and free the file struct.
 *		Note that the public part of the file struct (the H5FD_t part)
 *		will be all zero during the driver close callback like during
 *		the `open' callback.
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Tuesday, July 27, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FDclose(H5FD_t *file)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_API(H5FDclose, FAIL);
    H5TRACE1("e","x",file);

    if (!file || !file->cls)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid file pointer");

    if (H5FD_close(file)<0)
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "unable to close file");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_close
 *
 * Purpose:	Private version of H5FDclose()
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Wednesday, August  4, 1999
 *
 * Modifications:
 *              Robb Matzke, 2000-11-10
 *              Removed a call to set *file to all zero because the struct
 *              has already been freed by the close method. This fixes a write
 *              to freed memory.
 *-------------------------------------------------------------------------
 */
herr_t
H5FD_close(H5FD_t *file)
{
    const H5FD_class_t	*driver;
    H5FD_free_t		*cur, *next;
    H5FD_mem_t		i;
#ifdef H5F_DEBUG
    unsigned		nblocks=0;
    hsize_t		nbytes=0;
#endif
    herr_t      ret_value=SUCCEED;       /* Return value */
    
    FUNC_ENTER_NOAPI(H5FD_close, FAIL);

    assert(file && file->cls);

    /* Free all free-lists, leaking any memory thus described. Also leaks
     * file space allocated but not used when metadata aggregation is
     * turned on. */
    for (i=H5FD_MEM_DEFAULT; i<H5FD_MEM_NTYPES; H5_INC_ENUM(H5FD_mem_t,i)) {
	for (cur=file->fl[i]; cur; cur=next) {
#ifdef H5F_DEBUG
	    nblocks++;
	    nbytes += cur->size;
#endif
	    next = cur->next;
	    H5FL_FREE(H5FD_free_t,cur);
	}
	file->fl[i]=NULL;
    }
#ifdef H5F_DEBUG
    if (nblocks && H5DEBUG(F)) {
	fprintf(H5DEBUG(F),
		"H5F: leaked %lu bytes of file memory in %u blocks\n",
		(unsigned long)nbytes, nblocks);
    }
#endif

    /* Check if we need to reset the metadata accumulator information */
    if(file->feature_flags&H5FD_FEAT_ACCUMULATE_METADATA) {
        /* Free the buffer */
        if(file->meta_accum!=NULL)
            file->meta_accum=H5FL_BLK_FREE(meta_accum,file->meta_accum);
        
        /* Reset the buffer sizes & location */
        file->accum_buf_size=file->accum_size=0;
        file->accum_loc=HADDR_UNDEF;
        file->accum_dirty=0;
    } /* end if */

    /* Prepare to close file by clearing all public fields */
    driver = file->cls;
    H5I_dec_ref(file->driver_id);

    /*
     * Dispatch to the driver for actual close. If the driver fails to
     * close the file then the file will be in an unusable state.
     */
    assert(driver->close);
    if ((driver->close)(file)<0)
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "close failed");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDcmp
 *
 * Purpose:	Compare the keys of two files using the file driver callback
 *		if the files belong to the same driver, otherwise sort the
 *		files by driver class pointer value.
 *
 * Return:	Success:	A value like strcmp()
 *
 *		Failure:	Must never fail. If both file handles are
 *				invalid then they compare equal. If one file
 *				handle is invalid then it compares less than
 *				the other.  If both files belong to the same
 *				driver and the driver doesn't provide a
 *				comparison callback then the file pointers
 *				themselves are compared.
 *
 * Programmer:	Robb Matzke
 *              Tuesday, July 27, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
int
H5FDcmp(const H5FD_t *f1, const H5FD_t *f2)
{
    int	ret_value;
    
    FUNC_ENTER_API(H5FDcmp, -1); /*return value is arbitrary*/
    H5TRACE2("Is","xx",f1,f2);
    
    ret_value = H5FD_cmp(f1, f2);

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_cmp
 *
 * Purpose:	Private version of H5FDcmp()
 *
 * Return:	Success:	A value like strcmp()
 *
 *		Failure:	Must never fail.
 *
 * Programmer:	Robb Matzke
 *              Wednesday, August  4, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
int
H5FD_cmp(const H5FD_t *f1, const H5FD_t *f2)
{
    int	ret_value;

    FUNC_ENTER_NOAPI(H5FD_cmp, -1); /*return value is arbitrary*/

    if ((!f1 || !f1->cls) && (!f2 || !f2->cls))
        HGOTO_DONE(0);
    if (!f1 || !f1->cls)
        HGOTO_DONE(-1);
    if (!f2 || !f2->cls)
        HGOTO_DONE(1);
    if (f1->cls < f2->cls)
        HGOTO_DONE(-1);
    if (f1->cls > f2->cls)
        HGOTO_DONE(1);

    /* Files are same driver; no cmp callback */
    if (!f1->cls->cmp) {
	if (f1<f2)
            HGOTO_DONE(-1);
	if (f1>f2)
            HGOTO_DONE(1);
	HGOTO_DONE(0);
    }

    ret_value = (f1->cls->cmp)(f1, f2);

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDquery
 *
 * Purpose:	Query a VFL driver for its feature flags. (listed in H5FDpublic.h)
 *
 * Return:	Success:    non-negative
 *
 *		Failure:	negative
 *
 * Programmer:	Quincey Koziol
 *              Friday, August 25, 2000
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
int
H5FDquery(const H5FD_t *f, unsigned long *flags/*out*/)
{
    int	ret_value;

    FUNC_ENTER_API(H5FDquery, FAIL);
    H5TRACE2("Is","xx",f,flags);
    
    assert(f);
    assert(flags);
    
    ret_value = H5FD_query(f, flags);

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_query
 *
 * Purpose:	Private version of H5FDquery()
 *
 * Return:	Success:    non-negative
 *
 *		Failure:	negative
 *
 * Programmer:	Quincey Koziol
 *              Friday, August 25, 2000
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
int
H5FD_query(const H5FD_t *f, unsigned long *flags/*out*/)
{
    int	ret_value=0;

    FUNC_ENTER_NOAPI(H5FD_query, FAIL);

    assert(f);
    assert(flags);
    
    /* Check for query driver and call it */
    if (f->cls->query)
        ret_value = (f->cls->query)(f, flags);
    else
        *flags=0;

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDalloc
 *
 * Purpose:	Allocates SIZE bytes of memory from the FILE. The memory will
 *		be used according to the allocation class TYPE. First we try
 *		to satisfy the request from one of the free lists, according
 *		to the free list map provided by the driver. The free list
 *		array has one entry for each request type and the value of
 *		that array element can be one of four possibilities:
 *
 *		      It can be the constant H5FD_MEM_DEFAULT (or zero) which
 *		      indicates that the identity mapping is used. In other
 *		      words, the request type maps to its own free list.
 *
 *		      It can be the request type itself, which has the same
 *		      effect as the H5FD_MEM_DEFAULT value above.
 *
 *		      It can be the ID for another request type, which
 *		      indicates that the free list for the specified type
 *		      should be used instead.
 *
 *		      It can be the constant H5FD_MEM_NOLIST which means that
 *		      no free list should be used for this type of request.
 *
 *		If the request cannot be satisfied from a free list then
 *		either the driver's `alloc' callback is invoked (if one was
 *		supplied) or the end-of-address marker is extended. The
 *		`alloc' callback is always called with the same arguments as
 * 		the H5FDalloc().
 *
 * Return:	Success:	The format address of the new file memory.
 *
 *		Failure:	The undefined address HADDR_UNDEF
 *
 * Programmer:	Robb Matzke
 *              Tuesday, July 27, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
haddr_t
H5FDalloc(H5FD_t *file, H5FD_mem_t type, hsize_t size)
{
    haddr_t	ret_value = HADDR_UNDEF;
    
    FUNC_ENTER_API(H5FDalloc, HADDR_UNDEF);
    H5TRACE3("a","xMth",file,type,size);

    /* Check args */
    if (!file || !file->cls)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, HADDR_UNDEF, "invalid file pointer");
    if (type<0 || type>=H5FD_MEM_NTYPES)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, HADDR_UNDEF, "invalid request type");
    if (size<=0)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, HADDR_UNDEF, "zero-size request");

    /* Do the real work */
    if (HADDR_UNDEF==(ret_value=H5FD_alloc(file, type, size)))
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, HADDR_UNDEF, "unable to allocate file memory");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_alloc
 *
 * Purpose:	Private version of H5FDalloc()
 *
 * Return:	Success:	The format address of the new file memory.
 *
 *		Failure:	The undefined address HADDR_UNDEF
 *
 * Programmer:	Robb Matzke
 *              Wednesday, August  4, 1999
 *
 * Modifications:
 *	Albert Cheng, 2001/05/01
 *	Implement the allocation by alignment/threshold.
 *
 *-------------------------------------------------------------------------
 */
haddr_t
H5FD_alloc(H5FD_t *file, H5FD_mem_t type, hsize_t size)
{
    haddr_t	ret_value = HADDR_UNDEF;
    H5FD_mem_t	mapped_type;

    FUNC_ENTER_NOAPI(H5FD_alloc, HADDR_UNDEF);

    /* Check args */
    assert(file && file->cls);
    assert(type>=0 && type<H5FD_MEM_NTYPES);
    assert(size>0);
    
#ifdef H5F_DEBUG
    if (H5DEBUG(F)) {
	HDfprintf(H5DEBUG(F), "%s: alignment=%Hd, threshold=%Hd, size=%Hd\n",
	    FUNC, file->alignment, file->threshold, size);
    }
#endif
    /* Map the allocation request to a free list */
    if (H5FD_MEM_DEFAULT==file->cls->fl_map[type]) {
        mapped_type = type;
    } else {
        mapped_type = file->cls->fl_map[type];
    }

    /*
     * Try to satisfy the request from the free list.  Only perform the search
     * if the free list has the potential of satisfying the request.
     * Here, aligned requests are requests that are >= threshold and
     * alignment > 1.
     * For non-aligned request, first try to find an exact match, otherwise
     * use the best match which is the smallest size that meets the requested
     * size.
     * For aligned address request, find a block in the following order
     * of preferences:
     *   1. block address is aligned and exact match in size;
     *   2. block address is aligned with smallest size > requested size;
     *   3. block address is not aligned with smallest size >= requested size.
     */
    if (mapped_type>=0 && (0==file->maxsize || size<=file->maxsize)) {
        H5FD_free_t *prev=NULL, *best=NULL;
        H5FD_free_t *cur = file->fl[mapped_type];
	int	found_aligned = 0;
	int	need_aligned;
	hsize_t head;

	need_aligned = file->alignment > 1 && size >= file->threshold;
        while (cur) {
            file->maxsize = MAX(file->maxsize, cur->size);
	    if (need_aligned) {
		if ((head = cur->addr % file->alignment) == 0) {
		    /* got aligned address*/
		    if (cur->size==size) {
			/* exact match */
			ret_value = cur->addr;

                        /*
                         * Make certain we don't hand out a block of raw data
                         * from the free list which overlaps with the metadata
                         * aggregation buffer (if it's turned on)
                         */
                        if(type==H5FD_MEM_DRAW &&
                                (file->feature_flags&H5FD_FEAT_ACCUMULATE_METADATA) &&
                                H5F_addr_overlap(ret_value,size,file->accum_loc,file->accum_size)) {
                            ret_value=HADDR_UNDEF;
                        } /* end if */
                        else {
                            if (prev)
                                prev->next = cur->next;
                            else
                                file->fl[mapped_type] = cur->next;
                            H5FL_FREE(H5FD_free_t,cur);
                            if (size==file->maxsize)
                                file->maxsize=0; /*unknown*/
                            HGOTO_DONE(ret_value);
                        } /* end else */
		    } /* end if */
		    if (cur->size>size) {
			if (!best || !found_aligned || cur->size<best->size) {
			    best = cur;
			    found_aligned = 1;
			} /* end if */
		    } /* end if */
		} /* end if */
                else {
		    /* non-aligned address.
		     * check to see if this block is big enough to skip
		     * to the next aligned address and is still big enough
		     * for the requested size.
		     * the extra cur->size>head is for preventing unsigned
		     * underflow.
		     * (this can be improved by checking for an exact match
		     * after excluding the head. Such match is as good as
		     * the found_aligned case above.)
		     */
		    head = file->alignment - head;	/* actual head size */
		    if (!found_aligned &&
                            (cur->size > head && cur->size-head >= size) &&
                            (!best || cur->size < best->size)) {
			best = cur;
		    } /* end if */
		} /* end else */
	    } /* end if */
            else {
		/* !need_aligned */
		if (cur->size==size) {
		    ret_value = cur->addr;

                    /*
                     * Make certain we don't hand out a block of raw data
                     * from the free list which overlaps with the metadata
                     * aggregation buffer (if it's turned on)
                     */
                    if(type==H5FD_MEM_DRAW &&
                            (file->feature_flags&H5FD_FEAT_ACCUMULATE_METADATA) &&
                            H5F_addr_overlap(ret_value,size,file->accum_loc,file->accum_size)) {
                        ret_value=HADDR_UNDEF;
                    } /* end if */
                    else {
                        if (prev)
                            prev->next = cur->next;
                        else
                            file->fl[mapped_type] = cur->next;
                        H5FL_FREE(H5FD_free_t,cur);
                        if (size==file->maxsize)
                            file->maxsize=0; /*unknown*/
                        HGOTO_DONE(ret_value);
                    } /* end else */
		} /* end if */
                else
                    if (cur->size>size && (!best || cur->size<best->size)) {
                        best = cur;
                    } /* end if */
	    } /* end else */
            prev = cur;
            cur = cur->next;
        } /* end while */

        /* Couldn't find exact match, use best fitting piece found */
        if (best) {
	    if (best->size==file->maxsize)
		file->maxsize=0; /*unknown*/
	    if (!need_aligned || found_aligned) {
		/* free only tail */
		ret_value = best->addr;

                /*
                 * Make certain we don't hand out a block of raw data
                 * from the free list which overlaps with the metadata
                 * aggregation buffer (if it's turned on)
                 */
                if(type==H5FD_MEM_DRAW &&
                        (file->feature_flags&H5FD_FEAT_ACCUMULATE_METADATA) &&
                        H5F_addr_overlap(ret_value,size,file->accum_loc,file->accum_size)) {
                    ret_value=HADDR_UNDEF;
                } /* end if */
                else {
                    best->addr += size;     /* Reduce size of block on free list */
                    best->size -= size;
                    HGOTO_DONE(ret_value);
                } /* end else */
	    } /* end if */
            else {
		/* Split into 3 pieces. */
                /* Keep the the head and tail in the freelist. */
		H5FD_free_t *tmp = NULL;

		head = file->alignment - (best->addr % file->alignment);
		ret_value = best->addr + head;

                /*
                 * Make certain we don't hand out a block of raw data
                 * from the free list which overlaps with the metadata
                 * aggregation buffer (if it's turned on)
                 */
                if(type==H5FD_MEM_DRAW &&
                        (file->feature_flags&H5FD_FEAT_ACCUMULATE_METADATA) &&
                        H5F_addr_overlap(ret_value,size,file->accum_loc,file->accum_size)) {
                    ret_value=HADDR_UNDEF;
                } /* end if */
                else {

                    /* Attempt to allocate memory for temporary node */ 
                    tmp = H5FL_ALLOC(H5FD_free_t,0);
#ifdef H5F_DEBUG
                    if (H5DEBUG(F)) {
                        HDfprintf(H5DEBUG(F),
                            "%s: 3 pieces, begin best->addr=%a, best->size=%Hd, "
                            "head=%Hd, size=%Hd\n",
                            FUNC, best->addr, best->size, head, size);
                    }
#endif
                    assert(tmp);		/* bark in debug mode */
                    if (tmp) {
                        if ((tmp->size = (best->size - head - size))) {
                            tmp->addr = best->addr + head + size;
                            tmp->next = best->next;
                            best->next = tmp;
                        } /* end if */
                        else {
                            /* no tail piece */
                            H5FL_FREE(H5FD_free_t,tmp);
                        } /* end else */
                    } /* end if */
                    else {
                        /* Cannot keep the tail piece.  Leak file memory. */
                        /* (Only happens if memory allocation fails) */
                    } /* end else */
                    best->size = head;
                    HGOTO_DONE(ret_value);
                } /* end else */
	    } /* end else */
        } /* end if */
    } /* end if */

#ifdef H5F_DEBUG
    if (H5DEBUG(F)) {
	fprintf(H5DEBUG(F), "%s: Could not allocate from freelists\n", FUNC);
    }
#endif

    /* Handle metadata differently from "raw" data */
    if(type!=H5FD_MEM_DRAW) {
        /*
         * If the metadata aggregation feature is enabled for this VFL driver,
         * allocate "generic" metadata space and sub-allocate out of that, if
         * possible.  Otherwise just allocate through H5FD_real_alloc()
         */
        /* Allocate all types of metadata out of the metadata block */
        if(file->feature_flags&H5FD_FEAT_AGGREGATE_METADATA) {
            /* Check if the space requested is larger than the space left in the block */
            if(size>file->cur_meta_block_size) {
                haddr_t new_meta;       /* Address for new metadata */

                /* Check if the block asked for is too large for a metadata block */
                if(size>=file->def_meta_block_size) {
                    /* Allocate more room for this new block the regular way */
                    new_meta=H5FD_real_alloc(file,type,size);

                    /* Check if the new metadata is at the end of the current metadata block */
                    if(file->eoma+file->cur_meta_block_size==new_meta) {
                        /* Treat the allocation request as if the current metadata block
                         * grew by the amount allocated and just update the eoma
                         * address.  Don't bother updating the cur_meta_block_size
                         * since it will just grow and shrink by the same amount.
                         */
                        ret_value=file->eoma;
                        file->eoma+=size;
                    } /* end if */
                    else {
                        /* Use the new metadata block for the space allocated */
                        ret_value=new_meta;
                    } /* end else */
                } /* end if */
                else {
                    /* Allocate another metadata block */
                    new_meta=H5FD_real_alloc(file,H5FD_MEM_DEFAULT,file->def_meta_block_size);

                    /* Check if the new metadata is at the end of the current metadata block */
                    if(file->eoma+file->cur_meta_block_size==new_meta) {
                        file->cur_meta_block_size+=file->def_meta_block_size;
                    } /* end if */
                    else {
                        /* Return the unused portion of the metadata block to a free list */
                        if(file->eoma!=0)
                            if(H5FD_free(file,H5FD_MEM_DEFAULT,file->eoma,file->cur_meta_block_size)<0)
                                HGOTO_ERROR(H5E_VFL, H5E_CANTFREE, HADDR_UNDEF, "can't free metadata block");

                        /* Point the metadata block at the newly allocated block */
                        file->eoma=new_meta;
                        file->cur_meta_block_size=file->def_meta_block_size;
                    } /* end else */


                    /* Allocate space out of the metadata block */
                    ret_value=file->eoma;
                    file->cur_meta_block_size-=size;
                    file->eoma+=size;
                } /* end else */
            } /* end if */
            else {
                /* Allocate space out of the metadata block */
                ret_value=file->eoma;
                file->cur_meta_block_size-=size;
                file->eoma+=size;
            } /* end else */
        } /* end if */
        else { /* Allocate data the regular way */
            ret_value=H5FD_real_alloc(file,type,size);
        } /* end else */
    } /* end if */
    else { /* Allocate "raw" data */
        /*
         * If the "small data" aggregation feature is enabled for this VFL driver,
         * allocate "small data" space and sub-allocate out of that, if
         * possible.  Otherwise just allocate through H5FD_real_alloc()
         */
        if(file->feature_flags&H5FD_FEAT_AGGREGATE_SMALLDATA) {
            /* Check if the space requested is larger than the space left in the block */
            if(size>file->cur_sdata_block_size) {
                haddr_t new_data;       /* Address for new raw data block */

                /* Check if the block asked for is too large for the "small data" block */
                if(size>=file->def_sdata_block_size) {
                    /* Allocate more room for this new block the regular way */
                    new_data=H5FD_real_alloc(file,type,size);

                    /* Check if the new raw data is at the end of the current "small data" block */
                    if(file->eosda+file->cur_sdata_block_size==new_data) {
                        /* Treat the allocation request as if the current "small data"
                         * block grew by the amount allocated and just update the
                         * eosda address.  Don't bother updating the
                         * cur_sdata_block_size since it will just grow and shrink by
                         * the same amount.
                         */
                        ret_value=file->eosda;
                        file->eosda+=size;
                    } /* end if */
                    else {
                        /* Use the new "small data" block for the space allocated */
                        ret_value=new_data;
                    } /* end else */
                } /* end if */
                else {
                    /* Allocate another "small data" block */
                    new_data=H5FD_real_alloc(file,type,file->def_sdata_block_size);

                    /* Check if the new raw data is at the end of the current "small data" block */
                    if(file->eosda+file->cur_sdata_block_size==new_data) {
                        file->cur_sdata_block_size+=file->def_sdata_block_size;
                    } /* end if */
                    else {
                        /* Return the unused portion of the "small data" block to a free list */
                        if(file->eosda!=0)
                            if(H5FD_free(file,H5FD_MEM_DRAW,file->eosda,file->cur_sdata_block_size)<0)
                                HGOTO_ERROR(H5E_VFL, H5E_CANTFREE, HADDR_UNDEF, "can't free 'small data' block");

                        /* Point the "small data" block at the newly allocated block */
                        file->eosda=new_data;
                        file->cur_sdata_block_size=file->def_sdata_block_size;
                    } /* end else */


                    /* Allocate space out of the "small data" block */
                    ret_value=file->eosda;
                    file->cur_sdata_block_size-=size;
                    file->eosda+=size;
                } /* end else */
            } /* end if */
            else {
                /* Allocate space out of the "small data" block */
                ret_value=file->eosda;
                file->cur_sdata_block_size-=size;
                file->eosda+=size;
            } /* end else */
        } /* end if */
        else { /* Allocate data the regular way */
            ret_value=H5FD_real_alloc(file,type,size);
        } /* end else */
    } /* end else */

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_real_alloc
 *
 * Purpose:	Double private version of H5FDalloc() :-)
 *
 * Return:	Success:	The format address of the new file memory.
 *
 *		Failure:	The undefined address HADDR_UNDEF
 *
 * Programmer:	Quincey Koziol
 *              Friday, August 25, 2000
 *
 * Modifications:
 *	Albert Cheng, 2001/05/01
 *	Implement the allocation by alignment/threshold.
 *
 *-------------------------------------------------------------------------
 */
static haddr_t
H5FD_real_alloc(H5FD_t *file, H5FD_mem_t type, hsize_t size)
{
    haddr_t	ret_value = HADDR_UNDEF;

    FUNC_ENTER_NOINIT(H5FD_real_alloc);

    /* Check args */
    assert(file && file->cls);
    assert(type>=0 && type<H5FD_MEM_NTYPES);
    assert(size>0);
    
    /*
     * Dispatch to driver `alloc' callback or extend the end-of-address
     * marker
     */
    if (file->cls->alloc) {
        ret_value = (file->cls->alloc)(file, type, size);
        if (HADDR_UNDEF==ret_value)
            HGOTO_ERROR(H5E_VFL, H5E_NOSPACE, HADDR_UNDEF, "driver allocation request failed");
    } else {
	hsize_t	wasted;
        haddr_t oldeoa=0;
	haddr_t eoa = (file->cls->get_eoa)(file);

#ifdef H5F_DEBUG
	if (file->alignment * file->threshold != 1 && H5DEBUG(F)) {
	    HDfprintf(H5DEBUG(F),
		"%s: alignment=%Hd, threshold=%Hd, size=%Hd, Begin eoa=%a\n",
		FUNC, file->alignment, file->threshold, size, eoa);
	}
#endif
	/* wasted is 0 if not exceeding threshold or eoa happens to be aligned*/
	wasted = (size>=file->threshold) ? (eoa % file->alignment) : 0;
	if (wasted){
	    wasted = file->alignment - wasted;	/* actual waste */
	    oldeoa = eoa;			/* save it for later freeing */
	    /* advance eoa to the next alignment by allocating the wasted */
	    if (H5F_addr_overflow(eoa, wasted) || eoa+wasted>file->maxaddr)
		HGOTO_ERROR(H5E_VFL, H5E_NOSPACE, HADDR_UNDEF, "file allocation request failed");
	    eoa += wasted;
	    if ((file->cls->set_eoa)(file, eoa)<0)
		HGOTO_ERROR(H5E_VFL, H5E_NOSPACE, HADDR_UNDEF, "file allocation request failed");
	}

	/* allocate the aligned memory */
        if (H5F_addr_overflow(eoa, size) || eoa+size>file->maxaddr)
            HGOTO_ERROR(H5E_VFL, H5E_NOSPACE, HADDR_UNDEF, "file allocation request failed");
        ret_value = eoa;
        eoa += size;
        if ((file->cls->set_eoa)(file, eoa)<0)
            HGOTO_ERROR(H5E_VFL, H5E_NOSPACE, HADDR_UNDEF, "file allocation request failed");

	/* Free the wasted memory */
	if (wasted)
	    H5FDfree(file, type, oldeoa, wasted);

#ifdef H5F_DEBUG
	if (file->alignment * file->threshold != 1 && H5DEBUG(F)) {
	    HDfprintf(H5DEBUG(F),
		"%s: ret_value=%a, wasted=%Hd, Ended eoa=%a\n",
		FUNC, ret_value, wasted, eoa);
	}
#endif
    }

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



/*-------------------------------------------------------------------------
 * Function:	H5FDfree
 *
 * Purpose:	Frees format addresses starting with ADDR and continuing for
 *		SIZE bytes in the file FILE. The type of space being freed is
 *		specified by TYPE, which is mapped to a free list as
 *		described for the H5FDalloc() function above.  If the request
 *		doesn't map to a free list then either the application `free'
 *		callback is invoked (if defined) or the memory is leaked.
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Wednesday, July 28, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FDfree(H5FD_t *file, H5FD_mem_t type, haddr_t addr, hsize_t size)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_API(H5FDfree, FAIL);
    H5TRACE4("e","xMtah",file,type,addr,size);
    
    /* Check args */
    if (!file || !file->cls)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid file pointer");
    if (type<0 || type>=H5FD_MEM_NTYPES)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid request type");

    /* Do the real work */
    if (H5FD_free(file, type, addr, size)<0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "file deallocation request failed");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_free
 *
 * Purpose:	Private version of H5FDfree()
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Wednesday, August  4, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FD_free(H5FD_t *file, H5FD_mem_t type, haddr_t addr, hsize_t size)
{
    H5FD_mem_t		mapped_type;
    herr_t      ret_value=SUCCEED;       /* Return value */
    
    FUNC_ENTER_NOAPI(H5FD_free, FAIL);

    /* Check args */
    assert(file && file->cls);
    assert(type>=0 && type<H5FD_MEM_NTYPES);
    if (!H5F_addr_defined(addr) || addr>file->maxaddr ||
            H5F_addr_overflow(addr, size) || addr+size>file->maxaddr)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid region");

    /* Allow 0-sized free's to occur without penalty */
    if(0==size)
        HGOTO_DONE(SUCCEED);

    /* Map request type to free list */
    if (H5FD_MEM_DEFAULT==file->cls->fl_map[type]) {
        mapped_type = type;
    } else {
        mapped_type = file->cls->fl_map[type];
    }

    /*
     * If the request maps to a free list then add memory to the free list
     * without ever telling the driver that it was freed.  Otherwise let the
     * driver deallocate the memory.
     */
    if (mapped_type>=0) {
        H5FD_free_t *last;          /* Last merged node */
        H5FD_free_t *last_prev=NULL;/* Pointer to node before merged node */
        H5FD_free_t *curr;          /* Current free block being inspected */
        H5FD_free_t *prev;          /* Previous free block being inspected */

        /* Adjust the metadata accumulator to remove the freed block, if it overlaps */
        if((file->feature_flags&H5FD_FEAT_ACCUMULATE_METADATA)
                && H5F_addr_overlap(addr,size,file->accum_loc,file->accum_size)) {
            size_t overlap_size;        /* Size of overlap with accumulator */

            /* Check for overlapping the beginning of the accumulator */
            if(H5F_addr_le(addr,file->accum_loc)) {
                /* Check for completely overlapping the accumulator */
                if(H5F_addr_ge(addr+size,file->accum_loc+file->accum_size)) {
                    /* Reset the entire accumulator */
                    file->accum_loc=HADDR_UNDEF;
                    file->accum_size=FALSE;
                    file->accum_dirty=FALSE;
                } /* end if */
                /* Block to free must end within the accumulator */
                else {
                    size_t new_accum_size;      /* Size of new accumulator buffer */

                    /* Calculate the size of the overlap with the accumulator, etc. */
                    overlap_size=(addr+size)-file->accum_loc;
                    new_accum_size=file->accum_size-overlap_size;

                    /* Move the accumulator buffer information to eliminate the freed block */
                    HDmemmove(file->meta_accum,file->meta_accum+overlap_size,new_accum_size);

                    /* Adjust the accumulator information */
                    file->accum_loc+=overlap_size;
                    file->accum_size=new_accum_size;
                } /* end else */
            } /* end if */
            /* Block to free must start within the accumulator */
            else {
                /* Calculate the size of the overlap with the accumulator */
                overlap_size=(file->accum_loc+file->accum_size)-addr;

                /* Block to free is in the middle of the accumulator */
                if(H5F_addr_lt(addr,file->accum_loc+file->accum_size)) {
                    haddr_t tail_addr;
                    size_t tail_size;

                    /* Calculate the address & size of the tail to write */
                    tail_addr=addr+size;
                    tail_size=(file->accum_loc+file->accum_size)-tail_addr;

                    /* Write out the part of the accumulator after the block to free */
                    if (H5FD_write(file, H5FD_MEM_DEFAULT, H5P_DEFAULT, tail_addr, tail_size, file->meta_accum+(tail_addr-file->accum_loc))<0)
                        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "file write request failed");
                } /* end if */

                /* Adjust the accumulator information */
                file->accum_size=file->accum_size-overlap_size;
            } /* end else */
        } /* end if */

        /* Scan through the existing blocks for the mapped type to see if we can extend one */
        curr=file->fl[mapped_type];
        last=prev=NULL;
        while(curr!=NULL) {
            /* Check if the block to free adjoins the start of the current block */
            if((addr+size)==curr->addr) {
                /* If we previously found & merged a node, eliminate it from the list & free it */
                if(last!=NULL) {
                    /* Check if there was a previous block in the list */
                    if(last_prev!=NULL)
                        /* Eliminate the merged block from the list */
                        last_prev->next=last->next;
                    /* No previous block, this must be the head of the list */
                    else
                        /* Eliminate the merged block from the list */
                        file->fl[mapped_type] = last->next;

                    /* Check for eliminating the block before the 'current' one */
                    if(last==prev)
                        prev=last_prev;

                    /* Free the memory for the merged block */
                    H5FL_FREE(H5FD_free_t,last);
                } /* end if */

                /* Adjust the address and size of the block found */
                curr->addr=addr;
                curr->size+=size;

                /* Adjust the information about to memory block to include the merged block */
                addr=curr->addr;
                size=curr->size;

                /* Update the information about the merged node */
                last=curr;
                last_prev=prev;
            } /* end if */
            else {
                /* Check if the block to free adjoins the end of the current block */
                if((curr->addr+curr->size)==addr) {
                    /* If we previously found & merged a node, eliminate it from the list & free it */
                    if(last!=NULL) {
                        /* Check if there was a previous block in the list */
                        if(last_prev!=NULL)
                            /* Eliminate the merged block from the list */
                            last_prev->next=last->next;
                        /* No previous block, this must be the head of the list */
                        else
                            /* Eliminate the merged block from the list */
                            file->fl[mapped_type] = last->next;

                        /* Check for eliminating the block before the 'current' one */
                        if(last==prev)
                            prev=last_prev;

                        /* Free the memory for the merged block */
                        H5FL_FREE(H5FD_free_t,last);
                    } /* end if */

                    /* Adjust the size of the block found */
                    curr->size+=size;

                    /* Adjust the information about to memory block to include the merged block */
                    addr=curr->addr;
                    size=curr->size;

                    /* Update the information about the merged node */
                    last=curr;
                    last_prev=prev;
                } /* end if */
            } /* end else */

            /* Advance to next node in list */
            prev=curr;
            curr=curr->next;
        } /* end while */

        /* Check if we adjusted an existing block */
        if(last!=NULL) {
            /* Move the node found to the front, if it wasn't already there */
            if(last_prev!=NULL) {
                last_prev->next=last->next;
                last->next = file->fl[mapped_type];
                file->fl[mapped_type] = last;
            } /* end if */
        } /* end if */
        else {
            /* Allocate a new node to hold the free block's information */
            if(NULL==(last = H5FL_ALLOC(H5FD_free_t,0)))
                HGOTO_ERROR(H5E_FILE, H5E_NOSPACE, FAIL, "can't allocate node for free space info");

            last->addr = addr;
            last->size = size;
            last->next = file->fl[mapped_type];
            file->fl[mapped_type] = last;
        } /* end else */

        /* Check if we increased the size of the largest block on the list */
        file->maxsize = MAX(file->maxsize, last->size);

        /* Check if this free block is at the end of file allocated space.  
         * Truncate it if this is true. */
        if(file->cls->get_eoa) {
            haddr_t     eoa;
            eoa = file->cls->get_eoa(file);
            if(eoa == (last->addr+last->size)) {
                if(file->cls->set_eoa(file, last->addr) < 0)
                    HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "set end of space allocation request failed");
                /* Remove this free block from the list */
                file->fl[mapped_type] = last->next;
                if(file->maxsize==last->size)
                    file->maxsize=0; /*unknown*/
                H5FL_FREE(H5FD_free_t, last);
            }
        }    
    } else if (file->cls->free) {
        if ((file->cls->free)(file, type, addr, size)<0)
            HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "driver free request failed");
    } else {
        /* leak memory */
    }

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDrealloc
 *
 * Purpose:	Changes the size of an allocated chunk of memory, possibly
 *		also changing its location in the file.
 *
 * Return:	Success:	New address of the block of memory, not
 *				necessarily the same as the original address.
 *
 *		Failure:	HADDR_UNDEF
 *
 * Programmer:	Robb Matzke
 *              Tuesday, August  3, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
haddr_t
H5FDrealloc(H5FD_t *file, H5FD_mem_t type, haddr_t old_addr, hsize_t old_size,
	    hsize_t new_size)
{
    haddr_t	ret_value=HADDR_UNDEF;

    FUNC_ENTER_API(H5FDrealloc, HADDR_UNDEF);
    H5TRACE5("a","xMtahh",file,type,old_addr,old_size,new_size);

    if (HADDR_UNDEF==(ret_value=H5FD_realloc(file, type, old_addr, old_size, new_size)))
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, HADDR_UNDEF, "file reallocation request failed");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_realloc
 *
 * Purpose:	Private version of H5FDrealloc()
 *
 * Return:	Success:	New address of the block of memory, not
 *				necessarily the same as the original address.
 *
 *		Failure:	HADDR_UNDEF
 *
 * Programmer:	Robb Matzke
 *              Wednesday, August  4, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
haddr_t
H5FD_realloc(H5FD_t *file, H5FD_mem_t type, haddr_t old_addr, hsize_t old_size,
	     hsize_t new_size)
{
    haddr_t	new_addr=old_addr;
    uint8_t	_buf[8192];
    uint8_t	*buf=_buf;
    haddr_t      ret_value;       /* Return value */
    
    FUNC_ENTER_NOAPI(H5FD_realloc, HADDR_UNDEF);

    if (new_size==old_size) {
        /*nothing to do*/
	
    } else if (0==old_size) {
        /* allocate memory */
        assert(!H5F_addr_defined(old_addr));
        if (HADDR_UNDEF==(new_addr=H5FDalloc(file, type, new_size)))
            HGOTO_ERROR(H5E_FILE, H5E_NOSPACE, HADDR_UNDEF, "file allocation failed");
    } else if (0==new_size) {
        /* free memory */
        assert(H5F_addr_defined(old_addr));
        H5FDfree(file, type, old_addr, old_size);
        new_addr = HADDR_UNDEF;
        
    } else if (new_size<old_size) {
        /* free the end of the block */
        H5FDfree(file, type, old_addr+old_size, old_size-new_size);
    } else {
        /* move memory to new location */
        /* Note!  This may fail if sizeof(hsize_t)>sizeof(size_t) and the
         * object on disk is too large to read into a memory buffer all at one
         * time.  This chunk of code would have to be re-written using a loop
         * to move pieces of the realloced data through a fixed size buffer, etc.
         * -QAK, 6/20/01
         */
        if (HADDR_UNDEF==(new_addr=H5FDalloc(file, type, new_size)))
            HGOTO_ERROR(H5E_FILE, H5E_NOSPACE, HADDR_UNDEF, "file allocation failed");
        H5_CHECK_OVERFLOW(old_size,hsize_t,size_t);
        if (old_size>sizeof(_buf) && NULL==(buf=H5MM_malloc((size_t)old_size))) {
            H5FDfree(file, type, new_addr, new_size);
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, HADDR_UNDEF, "memory allocation failed");
        }
        if (H5FDread(file, type, H5P_DEFAULT, old_addr, (size_t)old_size, buf)<0 ||
                H5FDwrite(file, type, H5P_DEFAULT, new_addr, (size_t)old_size, buf)<0) {
            H5FDfree(file, type, new_addr, new_size);
            H5MM_xfree(buf);
            HGOTO_ERROR(H5E_FILE, H5E_READERROR, HADDR_UNDEF, "unable to move file block");
        }
        
        if (buf!=_buf)
            H5MM_xfree(buf);
        H5FDfree(file, type, old_addr, old_size);
    }

    /* Set return value */
    ret_value=new_addr;

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDget_eoa
 *
 * Purpose:	Returns the address of the first byte after the last
 *		allocated memory in the file.
 *
 * Return:	Success:	First byte after allocated memory.
 *
 *		Failure:	HADDR_UNDEF
 *
 * Programmer:	Robb Matzke
 *              Friday, July 30, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
haddr_t
H5FDget_eoa(H5FD_t *file)
{
    haddr_t	ret_value;

    FUNC_ENTER_API(H5FDget_eoa, HADDR_UNDEF);
    H5TRACE1("a","x",file);

    /* Check args */
    if (!file || !file->cls)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, HADDR_UNDEF, "invalid file pointer");

    /* The real work */
    if (HADDR_UNDEF==(ret_value=H5FD_get_eoa(file)))
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, HADDR_UNDEF, "file get eoa request failed");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_get_eoa
 *
 * Purpose:	Private version of H5FDget_eoa()
 *
 * Return:	Success:	First byte after allocated memory.
 *
 *		Failure:	HADDR_UNDEF
 *
 * Programmer:	Robb Matzke
 *              Wednesday, August  4, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
haddr_t
H5FD_get_eoa(H5FD_t *file)
{
    haddr_t	ret_value;
    
    FUNC_ENTER_NOAPI(H5FD_get_eoa, HADDR_UNDEF);
    assert(file && file->cls);
    
    /* Dispatch to driver */
    if (HADDR_UNDEF==(ret_value=(file->cls->get_eoa)(file)))
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, HADDR_UNDEF, "driver get_eoa request failed");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDset_eoa
 *
 * Purpose:	Set the end-of-address marker for the file. The ADDR is the
 *		address of the first byte past the last allocated byte of the
 *		file. This function is called from two places:
 *
 *		    It is called after an existing file is opened in order to
 *		    "allocate" enough space to read the superblock and then
 *		    to "allocate" the entire hdf5 file based on the contents
 *		    of the superblock.
 *
 *		    It is called during file memory allocation if the
 *		    allocation request cannot be satisfied from the free list
 *		    and the driver didn't supply an allocation callback.
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative, no side effect
 *
 * Programmer:	Robb Matzke
 *              Friday, July 30, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FDset_eoa(H5FD_t *file, haddr_t addr)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_API(H5FDset_eoa, FAIL);
    H5TRACE2("e","xa",file,addr);

    /* Check args */
    if (!file || !file->cls)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid file pointer");
    if (!H5F_addr_defined(addr) || addr>file->maxaddr)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid end-of-address value");

    /* The real work */
    if (H5FD_set_eoa(file, addr)<0)
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "file set eoa request failed");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_set_eoa
 *
 * Purpose:	Private version of H5FDset_eoa()
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative, no side effect
 *
 * Programmer:	Robb Matzke
 *              Wednesday, August  4, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FD_set_eoa(H5FD_t *file, haddr_t addr)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI(H5FD_set_eoa, FAIL);

    assert(file && file->cls);
    assert(H5F_addr_defined(addr) && addr<=file->maxaddr);
    
    /* Dispatch to driver */
    if ((file->cls->set_eoa)(file, addr)<0)
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "driver set_eoa request failed");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDget_eof
 *
 * Purpose:	Returns the end-of-file address, which is the greater of the
 *		end-of-format address and the actual EOF marker. This
 *		function is called after an existing file is opened in order
 *		for the library to learn the true size of the underlying file
 *		and to determine whether the hdf5 data has been truncated.
 *
 *		It is also used when a file is first opened to learn whether
 *		the file is empty or not.
 *
 * 		It is permissible for the driver to return the maximum address
 *		for the file size if the file is not empty.
 *
 * Return:	Success:	The EOF address.
 *
 *		Failure:	HADDR_UNDEF
 *
 * Programmer:	Robb Matzke
 *              Thursday, July 29, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
haddr_t
H5FDget_eof(H5FD_t *file)
{
    haddr_t	ret_value;
    
    FUNC_ENTER_API(H5FDget_eof, HADDR_UNDEF);
    H5TRACE1("a","x",file);

    /* Check arguments */
    if (!file || !file->cls)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, HADDR_UNDEF, "invalid file pointer");

    /* The real work */
    if (HADDR_UNDEF==(ret_value=H5FD_get_eof(file)))
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, HADDR_UNDEF, "file get eof request failed");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_get_eof
 *
 * Purpose:	Private version of H5FDget_eof()
 *
 * Return:	Success:	The EOF address.
 *
 *		Failure:	HADDR_UNDEF
 *
 * Programmer:	Robb Matzke
 *              Wednesday, August  4, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
haddr_t
H5FD_get_eof(H5FD_t *file)
{
    haddr_t	ret_value;

    FUNC_ENTER_NOAPI(H5FD_get_eof, HADDR_UNDEF);

    assert(file && file->cls);
    
    /* Dispatch to driver */
    if (file->cls->get_eof) {
	if (HADDR_UNDEF==(ret_value=(file->cls->get_eof)(file)))
	    HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, HADDR_UNDEF, "driver get_eof request failed");
    } else {
	ret_value = file->maxaddr;
    }

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDread
 *
 * Purpose:	Reads SIZE bytes from FILE beginning at address ADDR
 *		according to the data transfer property list DXPL_ID (which may
 *		be the constant H5P_DEFAULT). The result is written into the
 *		buffer BUF.
 *
 * Return:	Success:	Non-negative. The read result is written into
 *				the BUF buffer which should be allocated by
 *				the caller.
 *
 *		Failure:	Negative. The contents of BUF is undefined.
 *
 * Programmer:	Robb Matzke
 *              Thursday, July 29, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FDread(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t size,
	 void *buf/*out*/)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_API(H5FDread, FAIL);
    H5TRACE6("e","xMtiazx",file,type,dxpl_id,addr,size,buf);

    /* Check args */
    if (!file || !file->cls)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid file pointer");

    /* Get the default dataset transfer property list if the user didn't provide one */
    if (H5P_DEFAULT == dxpl_id)
        dxpl_id= H5P_DATASET_XFER_DEFAULT;
    if (TRUE!=H5P_isa_class(dxpl_id,H5P_DATASET_XFER))
	HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a data transfer property list");
    if (!buf)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "null result buffer");

    /* Do the real work */
    if (H5FD_read(file, type, dxpl_id, addr, size, buf)<0)
	HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "file read request failed");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_read
 *
 * Purpose:	Private version of H5FDread()
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Wednesday, August  4, 1999
 *
 * Modifications:
 *	Albert Cheng, 2000-11-21
 *	Disable the code that does early return when size==0 for
 *	Parallel mode since a collective call would require the process
 *	to continue on with "nothing" to transfer.
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FD_read(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t size,
	  void *buf/*out*/)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI(H5FD_read, FAIL);

    assert(file && file->cls);
    assert(H5I_GENPROP_LST==H5I_get_type(dxpl_id));
    assert(TRUE==H5P_isa_class(dxpl_id,H5P_DATASET_XFER));
    assert(buf);

#ifndef H5_HAVE_PARALLEL
    /* Do not return early for Parallel mode since the I/O could be a */
    /* collective transfer. */
    /* The no-op case */
    if (0==size)
        HGOTO_DONE(SUCCEED);
#endif /* H5_HAVE_PARALLEL */

    /* Check if this information is in the metadata accumulator */
    if((file->feature_flags&H5FD_FEAT_ACCUMULATE_METADATA) && type!=H5FD_MEM_DRAW) {
        /* Current read overlaps with metadata accumulator */
        if(H5F_addr_overlap(addr,size,file->accum_loc,file->accum_size)) {
            unsigned char *read_buf=(unsigned char *)buf; /* Pointer to the buffer being read in */
            size_t amount_read;         /* Amount to read at a time */
#ifndef NDEBUG
            hsize_t tempamount_read;         /* Amount to read at a time */
#endif /* NDEBUG */
            hsize_t read_off;           /* Offset to read from */

            /* Double check that we aren't reading raw data */
            assert(type!=H5FD_MEM_DRAW);

            /* Read the part before the metadata accumulator */
            if(addr<file->accum_loc) {
                /* Set the amount to read */
                H5_ASSIGN_OVERFLOW(amount_read,file->accum_loc-addr,hsize_t,size_t);

                /* Dispatch to driver */
                if ((file->cls->read)(file, type, dxpl_id, addr, amount_read, read_buf)<0)
                    HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "driver read request failed");

                /* Adjust the buffer, address & size */
                read_buf+=amount_read;
                addr+=amount_read;
                size-=amount_read;
            } /* end if */

            /* Copy the part overlapping the metadata accumulator */
            if(size>0 && (addr>=file->accum_loc && addr<(file->accum_loc+file->accum_size))) {
                /* Set the offset to "read" from */
                read_off=addr-file->accum_loc;

                /* Set the amount to "read" */
#ifndef NDEBUG
                tempamount_read = file->accum_size-read_off;
                H5_CHECK_OVERFLOW(tempamount_read,hsize_t,size_t);
                amount_read = MIN(size, (size_t)tempamount_read);
#else /* NDEBUG */
                amount_read = MIN(size, (size_t)(file->accum_size-read_off));
#endif /* NDEBUG */

                /* Copy the data out of the buffer */
                HDmemcpy(read_buf,file->meta_accum+read_off,amount_read);

                /* Adjust the buffer, address & size */
                read_buf+=amount_read;
                addr+=amount_read;
                size-=amount_read;
            } /* end if */

            /* Read the part after the metadata accumulator */
            if(size>0 && addr>=(file->accum_loc+file->accum_size)) {
                /* Dispatch to driver */
                if ((file->cls->read)(file, type, dxpl_id, addr, size, read_buf)<0)
                    HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "driver read request failed");

                /* Adjust the buffer, address & size */
                read_buf+=size;
                addr+=size;
                size-=size;
            } /* end if */

            /* Make certain we've read it all */
            assert(size==0);
        } /* end if */
        /* Current read doesn't overlap with metadata accumulator, read it into accumulator */
        else {
            /* Only update the metadata accumulator if it is not dirty or if
             * we are allowed to write the accumulator out during reads (when
             * it is dirty)
             */
            if(file->feature_flags&H5FD_FEAT_ACCUMULATE_METADATA_READ || !file->accum_dirty) {
                /* Flush current contents, if dirty */
                if(file->accum_dirty) {
                    if ((file->cls->write)(file, H5FD_MEM_DEFAULT, dxpl_id, file->accum_loc, file->accum_size, file->meta_accum)<0)
                        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "driver write request failed");

                    /* Reset accumulator dirty flag */
                    file->accum_dirty=FALSE;
                } /* end if */

                /* Cache the new piece of metadata */
                /* Check if we need to resize the buffer */
                if(size>file->accum_buf_size) {
                    /* Grow the metadata accumulator buffer */
                    if ((file->meta_accum=H5FL_BLK_REALLOC(meta_accum,file->meta_accum,size))==NULL)
                        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate metadata accumulator buffer");

                    /* Note the new buffer size */
                    file->accum_buf_size=size;
                } /* end if */
                else {
                    /* Check if we should shrink the accumulator buffer */
                    if(size<(file->accum_buf_size/H5FD_ACCUM_THROTTLE) &&
                            file->accum_buf_size>H5FD_ACCUM_THRESHOLD) {
                        size_t new_size=(file->accum_buf_size/H5FD_ACCUM_THROTTLE); /* New size of accumulator buffer */

                        /* Shrink the accumulator buffer */
                        if ((file->meta_accum=H5FL_BLK_REALLOC(meta_accum,file->meta_accum,new_size))==NULL)
                            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate metadata accumulator buffer");

                        /* Note the new buffer size */
                        file->accum_buf_size=new_size;
                    } /* end if */
                } /* end else */

                /* Update accumulator information */
                file->accum_loc=addr;
                file->accum_size=size;
                file->accum_dirty=FALSE;

                /* Read into accumulator */
                if ((file->cls->read)(file, H5FD_MEM_DEFAULT, dxpl_id, file->accum_loc, file->accum_size, file->meta_accum)<0)
                    HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "driver read request failed");

                /* Copy into buffer */
                HDmemcpy(buf,file->meta_accum,size);
            } /* end if */
            else {
                /* Dispatch to driver */
                if ((file->cls->read)(file, type, dxpl_id, addr, size, buf)<0)
                    HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "driver read request failed");
            } /* end else */
        } /* end else */
    } /* end if */
    else {
        /* Dispatch to driver */
        if ((file->cls->read)(file, type, dxpl_id, addr, size, buf)<0)
            HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "driver read request failed");
    } /* end else */

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDwrite
 *
 * Purpose:	Writes SIZE bytes to FILE beginning at address ADDR according
 *		to the data transfer property list DXPL_ID (which may be the
 *		constant H5P_DEFAULT). The bytes to be written come from the
 *		buffer BUF.
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Thursday, July 29, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FDwrite(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t size,
	  const void *buf)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_API(H5FDwrite, FAIL);
    H5TRACE6("e","xMtiazx",file,type,dxpl_id,addr,size,buf);

    /* Check args */
    if (!file || !file->cls)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid file pointer");
    /* Get the default dataset transfer property list if the user didn't provide one */
    if (H5P_DEFAULT == dxpl_id)
        dxpl_id= H5P_DATASET_XFER_DEFAULT;
    if (TRUE!=H5P_isa_class(dxpl_id,H5P_DATASET_XFER))
	HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a data transfer property list");
    if (!buf)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "null buffer");

    /* The real work */
    if (H5FD_write(file, type, dxpl_id, addr, size, buf)<0)
	HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "file write request failed");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_write
 *
 * Purpose:	Private version of H5FDwrite()
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Wednesday, August  4, 1999
 *
 * Modifications:
 *	Albert Cheng, 2000-11-21
 *	Disable the code that does early return when size==0 for
 *	Parallel mode since a collective call would require the process
 *	to continue on with "nothing" to transfer.
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FD_write(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t size,
	   const void *buf)
{
    size_t new_size;    /* New size of the accumulator buffer */
    size_t old_offset;  /* Offset of old data within the accumulator buffer */
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI(H5FD_write, FAIL);

    assert(file && file->cls);
    assert(H5I_GENPROP_LST==H5I_get_type(dxpl_id));
    assert(TRUE==H5P_isa_class(dxpl_id,H5P_DATASET_XFER));
    assert(buf);
    
#ifndef H5_HAVE_PARALLEL
    /* Do not return early for Parallel mode since the I/O could be a */
    /* collective transfer. */
    /* The no-op case */
    if (0==size)
        HGOTO_DONE(SUCCEED);
#endif /* H5_HAVE_PARALLEL */

    /* Check for accumulating metadata */
    if((file->feature_flags&H5FD_FEAT_ACCUMULATE_METADATA) && type!=H5FD_MEM_DRAW) {
        /* Check if there is already metadata in the accumulator */
        if(file->accum_size>0) {
            /* Check if the piece of metadata being written adjoins or is inside the metadata accumulator */
            if((addr>=file->accum_loc && addr<=(file->accum_loc+file->accum_size))
                || ((addr+size)>file->accum_loc && (addr+size)<=(file->accum_loc+file->accum_size))
                || (addr<file->accum_loc && (addr+size)>file->accum_loc)) {

                /* Check if the new metadata adjoins the beginning of the current accumulator */
                if((addr+size)==file->accum_loc) {
                    /* Check if we need more buffer space */
                    if((size+file->accum_size)>file->accum_buf_size) {
                        /* Adjust the buffer size, by doubling it */
                        file->accum_buf_size = MAX(file->accum_buf_size*2,size+file->accum_size);

                        /* Reallocate the metadata accumulator buffer */
                        if ((file->meta_accum=H5FL_BLK_REALLOC(meta_accum,file->meta_accum,file->accum_buf_size))==NULL)
                            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate metadata accumulator buffer");
                    } /* end if */

                    /* Move the existing metadata to the proper location */
                    HDmemmove(file->meta_accum+size,file->meta_accum,file->accum_size);

                    /* Copy the new metadata at the front */
                    HDmemcpy(file->meta_accum,buf,size);

                    /* Set the new size & location of the metadata accumulator */
                    file->accum_loc=addr;
                    file->accum_size=file->accum_size+size;

                    /* Mark it as written to */
                    file->accum_dirty=TRUE;
                } /* end if */
                /* Check if the new metadata adjoins the end of the current accumulator */
                else if(addr==(file->accum_loc+file->accum_size)) {
                    /* Check if we need more buffer space */
                    if((size+file->accum_size)>file->accum_buf_size) {
                        /* Adjust the buffer size, by doubling it */
                        file->accum_buf_size = MAX(file->accum_buf_size*2,size+file->accum_size);

                        /* Reallocate the metadata accumulator buffer */
                        if ((file->meta_accum=H5FL_BLK_REALLOC(meta_accum,file->meta_accum,file->accum_buf_size))==NULL)
                            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate metadata accumulator buffer");
                    } /* end if */

                    /* Copy the new metadata to the end */
                    HDmemcpy(file->meta_accum+file->accum_size,buf,size);

                    /* Set the new size of the metadata accumulator */
                    file->accum_size=file->accum_size+size;

                    /* Mark it as written to */
                    file->accum_dirty=TRUE;
                } /* end if */
                /* Check if the new metadata is entirely within the current accumulator */
                else if(addr>=file->accum_loc && (addr+size)<=(file->accum_loc+file->accum_size)) {
                    /* Copy the new metadata to the proper location within the accumulator */
                    HDmemcpy(file->meta_accum+(addr-file->accum_loc),buf,size);

                    /* Mark it as written to */
                    file->accum_dirty=TRUE;
                } /* end if */
                /* Check if the new metadata overlaps the beginning of the current accumulator */
                else if(addr<file->accum_loc && (addr+size)<=(file->accum_loc+file->accum_size)) {
                    /* Calculate the new accumulator size, based on the amount of overlap */
                    H5_ASSIGN_OVERFLOW(new_size,(file->accum_loc-addr)+file->accum_size,hsize_t,size_t);

                    /* Check if we need more buffer space */
                    if(new_size>file->accum_buf_size) {
                        /* Adjust the buffer size, by doubling it */
                        file->accum_buf_size = MAX(file->accum_buf_size*2,new_size);

                        /* Reallocate the metadata accumulator buffer */
                        if ((file->meta_accum=H5FL_BLK_REALLOC(meta_accum,file->meta_accum,file->accum_buf_size))==NULL)
                            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate metadata accumulator buffer");
                    } /* end if */

                    /* Calculate the proper offset of the existing metadata */
                    H5_ASSIGN_OVERFLOW(old_offset,(addr+size)-file->accum_loc,hsize_t,size_t);

                    /* Move the existing metadata to the proper location */
                    HDmemmove(file->meta_accum+size,file->meta_accum+old_offset,(file->accum_size-old_offset));

                    /* Copy the new metadata at the front */
                    HDmemcpy(file->meta_accum,buf,size);

                    /* Set the new size & location of the metadata accumulator */
                    file->accum_loc=addr;
                    file->accum_size=new_size;

                    /* Mark it as written to */
                    file->accum_dirty=TRUE;
                } /* end if */
                /* Check if the new metadata overlaps the end of the current accumulator */
                else if(addr>=file->accum_loc && (addr+size)>(file->accum_loc+file->accum_size)) {
                    /* Calculate the new accumulator size, based on the amount of overlap */
                    H5_ASSIGN_OVERFLOW(new_size,(addr-file->accum_loc)+size,hsize_t,size_t);

                    /* Check if we need more buffer space */
                    if(new_size>file->accum_buf_size) {
                        /* Adjust the buffer size, by doubling it */
                        file->accum_buf_size = MAX(file->accum_buf_size*2,new_size);

                        /* Reallocate the metadata accumulator buffer */
                        if ((file->meta_accum=H5FL_BLK_REALLOC(meta_accum,file->meta_accum,file->accum_buf_size))==NULL)
                            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate metadata accumulator buffer");
                    } /* end if */

                    /* Copy the new metadata to the end */
                    HDmemcpy(file->meta_accum+(addr-file->accum_loc),buf,size);

                    /* Set the new size & location of the metadata accumulator */
                    file->accum_loc=addr;
                    file->accum_size=new_size;

                    /* Mark it as written to */
                    file->accum_dirty=TRUE;
                } /* end if */
                else {
                    assert(0 && "New metadata overlapped both beginning and end of existing metadata accumulator!");
                } /* end else */
            } /* end if */
            /* New piece of metadata doesn't adjoin or overlap the existing accumulator */
            else {
                /* Write out the existing metadata accumulator, with dispatch to driver */
                if(file->accum_dirty) {
                    if ((file->cls->write)(file, H5FD_MEM_DEFAULT, dxpl_id, file->accum_loc, file->accum_size, file->meta_accum)<0)
                        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "driver write request failed");
                    /* Reset accumulator dirty flag */
                    file->accum_dirty=FALSE;
                } /* end if */

                /* Cache the new piece of metadata */
                /* Check if we need to resize the buffer */
                if(size>file->accum_buf_size) {
                    /* Grow the metadata accumulator buffer */
                    if ((file->meta_accum=H5FL_BLK_REALLOC(meta_accum,file->meta_accum,size))==NULL)
                        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate metadata accumulator buffer");

                    /* Note the new buffer size */
                    file->accum_buf_size=size;
                } /* end if */
                else {
                    /* Check if we should shrink the accumulator buffer */
                    if(size<(file->accum_buf_size/H5FD_ACCUM_THROTTLE) &&
                            file->accum_buf_size>H5FD_ACCUM_THRESHOLD) {
                        size_t tmp_size=(file->accum_buf_size/H5FD_ACCUM_THROTTLE); /* New size of accumulator buffer */

                        /* Shrink the accumulator buffer */
                        if ((file->meta_accum=H5FL_BLK_REALLOC(meta_accum,file->meta_accum,tmp_size))==NULL)
                            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate metadata accumulator buffer");

                        /* Note the new buffer size */
                        file->accum_buf_size=tmp_size;
                    } /* end if */
                } /* end else */

                /* Update the metadata accumulator information */
                file->accum_loc=addr;
                file->accum_size=size;
                file->accum_dirty=TRUE;

                /* Store the piece of metadata in the accumulator */
                HDmemcpy(file->meta_accum,buf,size);
            } /* end else */
        } /* end if */
        /* No metadata in the accumulator, grab this piece and keep it */
        else {
            /* Check if we need to reallocate the buffer */
            if(size>file->accum_buf_size) {
                /* Reallocate the metadata accumulator buffer */
                if ((file->meta_accum=H5FL_BLK_REALLOC(meta_accum,file->meta_accum,size))==NULL)
                    HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate metadata accumulator buffer");

                /* Note the new buffer size */
                file->accum_buf_size=size;
            } /* end if */

            /* Update the metadata accumulator information */
            file->accum_loc=addr;
            file->accum_size=size;
            file->accum_dirty=TRUE;

            /* Store the piece of metadata in the accumulator */
            HDmemcpy(file->meta_accum,buf,size);
        } /* end else */
    } /* end if */
    else {
        /* Dispatch to driver */
        if ((file->cls->write)(file, type, dxpl_id, addr, size, buf)<0)
            HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "driver write request failed");
    } /* end else */

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FDflush
 *
 * Purpose:	Notify driver to flush all cached data.  If the driver has no
 *		flush method then nothing happens.
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Thursday, July 29, 1999
 *
 * Modifications:
 *              Quincey Koziol, May 20, 2002
 *              Added 'closing' parameter
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FDflush(H5FD_t *file, unsigned closing)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_API(H5FDflush, FAIL);
    H5TRACE2("e","xIu",file,closing);

    /* Check args */
    if (!file || !file->cls)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid file pointer");

    /* Do the real work */
    if (H5FD_flush(file,closing)<0)
	HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "file flush request failed");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_flush
 *
 * Purpose:	Private version of H5FDflush()
 *
 * Return:	Success:	Non-negative
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Wednesday, August  4, 1999
 *
 * Modifications:
 *              Quincey Koziol, May 20, 2002
 *              Added 'closing' parameter
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FD_flush(H5FD_t *file, unsigned closing)
{
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI(H5FD_flush, FAIL);

    assert(file && file->cls);

    /* Check if we need to flush out the metadata accumulator */
    if((file->feature_flags&H5FD_FEAT_ACCUMULATE_METADATA) && file->accum_dirty && file->accum_size>0) {
        /* Flush the metadata contents */
        /* Not certain if the type and dxpl should be the way they are... -QAK */
        if ((file->cls->write)(file, H5FD_MEM_DEFAULT, H5P_DATASET_XFER_DEFAULT, file->accum_loc, file->accum_size, file->meta_accum)<0)
            HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "driver write request failed");

        /* Reset the dirty flag */
        file->accum_dirty=FALSE;
    } /* end if */

    if (file->cls->flush && (file->cls->flush)(file,closing)<0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "driver flush request failed");

done:
    FUNC_LEAVE(ret_value);
}


/*-------------------------------------------------------------------------
 * Function:	H5FD_get_fileno
 *
 * Purpose:	Quick and dirty routine to retrieve the file's 'fileno' value
 *          (Mainly added to stop non-file routines from poking about in the
 *          H5FD_t data structure)
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Quincey Koziol <koziol@ncsa.uiuc.edu>
 *		March 27, 2002
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5FD_get_fileno(const H5FD_t *file, unsigned long *filenum)
{
    herr_t ret_value=SUCCEED;   /* Return value */

    FUNC_ENTER_NOAPI(H5FD_get_fileno, FAIL);

    assert(file);
    assert(filenum);

    /* Retrieve the file's serial number */
    HDmemcpy(filenum,file->fileno,sizeof(file->fileno));

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


/*--------------------------------------------------------------------------
 * Function:    H5FDget_vfd_handle
 *
 * Purpose:     Returns a pointer to the file handle of low-level virtual 
 *              file driver.
 *
 * Return:      Non-negative if succeed; negative otherwise.
 *
 * Programmer:  Raymond Lu
 *              Sep. 16, 2002
 *
 * Modifications:
 *
 *--------------------------------------------------------------------------
 */
herr_t H5FDget_vfd_handle(H5FD_t *file, hid_t fapl, void** file_handle)
{
    herr_t              ret_value;

    FUNC_ENTER_API(H5FDget_vfd_handle, FAIL);
    
    /* Check arguments */
    assert(file);
    assert(file_handle);
    ret_value=H5FD_get_vfd_handle(file, fapl, file_handle);

done:
    FUNC_LEAVE(ret_value);
}


/*--------------------------------------------------------------------------
 * Function:    H5FD_get_vfd_handle
 *
 * Purpose:     Retrieve the file handle for file driver.
 *
 * Return:      Non-negative if succeed; negative if fails.
 * 
 * Programmer:  Raymond Lu
 *              Sep. 16, 2002
 *
 * Modifications:
 *
 *--------------------------------------------------------------------------
 */
herr_t H5FD_get_vfd_handle(H5FD_t *file, hid_t fapl, void** file_handle)
{
    herr_t ret_value = SUCCEED;
    
    FUNC_ENTER_NOAPI(H5FD_get_vfd_handle, FAIL);

    assert(file_handle);
    if(file->cls->get_handle && ((ret_value=file->cls->get_handle(file, fapl, file_handle)) < 0))
        HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "can't get file handle for file driver");        

done:
    FUNC_LEAVE(ret_value);
}