/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the COPYING file, which can be found at the root of the source code       *
 * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.  *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/* 
 *
 * Purpose: v2 B-tree indexing for chunked datasets with > 1 unlimited dimensions.
 *   Each dataset chunk in the b-tree is identified by its dimensional offset.
 *
 */

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

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


/***********/
/* Headers */
/***********/
#include "H5private.h"          /* Generic Functions                    */
#include "H5Dpkg.h"		/* Datasets				*/
#include "H5FLprivate.h"	/* Free Lists                           */
#include "H5MFprivate.h"     	/* File space management                */
#include "H5MMprivate.h"	/* Memory management			*/
#include "H5VMprivate.h"	/* Vector and array functions		*/


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


/******************/
/* Local Typedefs */
/******************/
/* User data for creating callback context */
typedef struct H5D_bt2_ctx_ud_t {
    const H5F_t *f;             /* Pointer to file info */
    uint32_t chunk_size;        /* Size of chunk (bytes; for filtered object) */
    unsigned ndims;		/* Number of dimensions */
    uint32_t *dim;		/* Size of chunk in elements */
} H5D_bt2_ctx_ud_t;

/* The callback context */
typedef struct H5D_bt2_ctx_t {
    uint32_t chunk_size;        /* Size of chunk (bytes; constant for unfiltered object) */
    size_t sizeof_addr;       	/* Size of file addresses in the file (bytes) */
    size_t chunk_size_len;      /* Size of chunk sizes in the file (bytes) */
    unsigned ndims;		/* Number of dimensions in chunk */
    uint32_t *dim;		/* Size of chunk in elements */
} H5D_bt2_ctx_t;

/* Callback info for iteration over chunks in v2 B-tree */
typedef struct H5D_bt2_it_ud_t {
    H5D_chunk_cb_func_t cb;     /* Callback routine for the chunk */
    void *udata;                /* User data for the chunk's callback routine */
} H5D_bt2_it_ud_t;

/* User data for compare callback */
typedef struct H5D_bt2_ud_t {
    H5D_chunk_rec_t rec;    	/* The record to search for */
    unsigned ndims;	        /* Number of dimensions for the chunked dataset */
} H5D_bt2_ud_t;


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

/* Shared v2 B-tree methods for indexing filtered and non-filtered chunked datasets */
static void *H5D__bt2_crt_context(void *udata);
static herr_t H5D__bt2_dst_context(void *ctx);
static herr_t H5D__bt2_store(void *native, const void *udata);
static herr_t H5D__bt2_compare(const void *rec1, const void *rec2, int *result);

/* v2 B-tree class for indexing non-filtered chunked datasets */
static herr_t H5D__bt2_unfilt_encode(uint8_t *raw, const void *native, void *ctx);
static herr_t H5D__bt2_unfilt_decode(const uint8_t *raw, void *native, void *ctx);
static herr_t H5D__bt2_unfilt_debug(FILE *stream, int indent, int fwidth,
    const void *record, const void *u_ctx);

/* v2 B-tree class for indexing filtered chunked datasets */
static herr_t H5D__bt2_filt_encode(uint8_t *raw, const void *native, void *ctx);
static herr_t H5D__bt2_filt_decode(const uint8_t *raw, void *native, void *ctx);
static herr_t H5D__bt2_filt_debug(FILE *stream, int indent, int fwidth,
    const void *record, const void *u_ctx);

/* Helper routine */
static herr_t H5D__bt2_idx_open(const H5D_chk_idx_info_t *idx_info);
static herr_t H5D__btree2_idx_depend(const H5D_chk_idx_info_t *idx_info);

/* Callback for H5B2_iterate() which is called in H5D__bt2_idx_iterate() */
static int H5D__bt2_idx_iterate_cb(const void *_record, void *_udata);

/* Callback for H5B2_find() which is called in H5D__bt2_idx_get_addr() */
static herr_t H5D__bt2_found_cb(const void *nrecord, void *op_data);

/*  
 * Callback for H5B2_remove() and H5B2_delete() which is called
 * in H5D__bt2_idx_remove() and H5D__bt2_idx_delete().
 */
static herr_t H5D__bt2_remove_cb(const void *nrecord, void *_udata);

/* Callback for H5B2_modify() which is called in H5D__bt2_idx_insert() */
static herr_t H5D__bt2_mod_cb(void *_record, void *_op_data, hbool_t *changed);

/* Chunked layout indexing callbacks for v2 B-tree indexing */
static herr_t H5D__bt2_idx_init(const H5D_chk_idx_info_t *idx_info,
    const H5S_t *space, haddr_t dset_ohdr_addr);
static herr_t H5D__bt2_idx_create(const H5D_chk_idx_info_t *idx_info);
static hbool_t H5D__bt2_idx_is_space_alloc(const H5O_storage_chunk_t *storage);
static herr_t H5D__bt2_idx_insert(const H5D_chk_idx_info_t *idx_info,
    H5D_chunk_ud_t *udata, const H5D_t *dset);
static herr_t H5D__bt2_idx_get_addr(const H5D_chk_idx_info_t *idx_info,
    H5D_chunk_ud_t *udata);
static int H5D__bt2_idx_iterate(const H5D_chk_idx_info_t *idx_info,
    H5D_chunk_cb_func_t chunk_cb, void *chunk_udata);
static herr_t H5D__bt2_idx_remove(const H5D_chk_idx_info_t *idx_info,
    H5D_chunk_common_ud_t *udata);
static herr_t H5D__bt2_idx_delete(const H5D_chk_idx_info_t *idx_info);
static herr_t H5D__bt2_idx_copy_setup(const H5D_chk_idx_info_t *idx_info_src,
    const H5D_chk_idx_info_t *idx_info_dst);
static herr_t H5D__bt2_idx_copy_shutdown(H5O_storage_chunk_t *storage_src,
    H5O_storage_chunk_t *storage_dst);
static herr_t H5D__bt2_idx_size(const H5D_chk_idx_info_t *idx_info, hsize_t *size);
static herr_t H5D__bt2_idx_reset(H5O_storage_chunk_t *storage, hbool_t reset_addr);
static herr_t H5D__bt2_idx_dump(const H5O_storage_chunk_t *storage,
    FILE *stream);
static herr_t H5D__bt2_idx_dest(const H5D_chk_idx_info_t *idx_info);


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

/* Chunked dataset I/O ops for v2 B-tree indexing */
const H5D_chunk_ops_t H5D_COPS_BT2[1] = {{
    TRUE,                               /* Fixed array indices support SWMR access */
    H5D__bt2_idx_init,                  /* init */
    H5D__bt2_idx_create,                /* create */
    H5D__bt2_idx_is_space_alloc,        /* is_space_alloc */
    H5D__bt2_idx_insert,                /* insert */
    H5D__bt2_idx_get_addr,              /* get_addr */
    NULL,                               /* resize */
    H5D__bt2_idx_iterate,               /* iterate */
    H5D__bt2_idx_remove,                /* remove */
    H5D__bt2_idx_delete,                /* delete */
    H5D__bt2_idx_copy_setup,            /* copy_setup */
    H5D__bt2_idx_copy_shutdown,         /* copy_shutdown */
    H5D__bt2_idx_size,                  /* size */
    H5D__bt2_idx_reset,                 /* reset */
    H5D__bt2_idx_dump,                  /* dump */
    H5D__bt2_idx_dest                   /* destroy */
}};


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

/* v2 B-tree class for indexing non-filtered chunked datasets */
const H5B2_class_t H5D_BT2[1] = {{  	/* B-tree class information */
    H5B2_CDSET_ID,             	/* Type of B-tree */
    "H5B2_CDSET_ID",           	/* Name of B-tree class */
    sizeof(H5D_chunk_rec_t),	/* Size of native record */
    H5D__bt2_crt_context,      	/* Create client callback context */
    H5D__bt2_dst_context,       /* Destroy client callback context */
    H5D__bt2_store,    		/* Record storage callback */
    H5D__bt2_compare,   	/* Record comparison callback */
    H5D__bt2_unfilt_encode,    	/* Record encoding callback */
    H5D__bt2_unfilt_decode,    	/* Record decoding callback */
    H5D__bt2_unfilt_debug   	/* Record debugging callback */
}};

/* v2 B-tree class for indexing filtered chunked datasets */
const H5B2_class_t H5D_BT2_FILT[1] = {{	/* B-tree class information */
    H5B2_CDSET_FILT_ID, 	/* Type of B-tree */
    "H5B2_CDSET_FILT_ID",      	/* Name of B-tree class */
    sizeof(H5D_chunk_rec_t), 	/* Size of native record */
    H5D__bt2_crt_context,      	/* Create client callback context */
    H5D__bt2_dst_context,      	/* Destroy client callback context */
    H5D__bt2_store,             /* Record storage callback */
    H5D__bt2_compare,           /* Record comparison callback */
    H5D__bt2_filt_encode,       /* Record encoding callback */
    H5D__bt2_filt_decode,       /* Record decoding callback */
    H5D__bt2_filt_debug         /* Record debugging callback */
}};


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

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

/* Declare a free list to manage the page elements */
H5FL_ARR_DEFINE_STATIC(uint32_t, H5O_LAYOUT_NDIMS);



/*-------------------------------------------------------------------------
 * Function:    H5D__bt2_crt_context
 *
 * Purpose:     Create client callback context
 *
 * Return:      Success:        non-NULL
 *              Failure:        NULL
 *
 * Programmer:  Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static void *
H5D__bt2_crt_context(void *_udata)
{
    H5D_bt2_ctx_ud_t *udata = (H5D_bt2_ctx_ud_t *)_udata; /* User data for building callback context */
    H5D_bt2_ctx_t *ctx;   	/* Callback context structure */
    uint32_t *my_dim = NULL;    /* Pointer to copy of chunk dimension size */
    void *ret_value = NULL;     /* Return value */

    FUNC_ENTER_STATIC

    /* Sanity check */
    HDassert(udata);
    HDassert(udata->f);
    HDassert(udata->ndims > 0 && udata->ndims < H5O_LAYOUT_NDIMS);

    /* Allocate callback context */
    if(NULL == (ctx = H5FL_MALLOC(H5D_bt2_ctx_t)))
        HGOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, NULL, "can't allocate callback context")

    /* Determine the size of addresses and set the chunk size and # of dimensions for the dataset */
    ctx->sizeof_addr = H5F_SIZEOF_ADDR(udata->f);
    ctx->chunk_size = udata->chunk_size;
    ctx->ndims = udata->ndims;

    /* Set up the "local" information for this dataset's chunk dimension sizes */
    if(NULL == (my_dim = (uint32_t *)H5FL_ARR_MALLOC(uint32_t, H5O_LAYOUT_NDIMS)))
        HGOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, NULL, "can't allocate chunk dims")
    H5MM_memcpy(my_dim, udata->dim, H5O_LAYOUT_NDIMS * sizeof(uint32_t));
    ctx->dim = my_dim;

    /* 
     * Compute the size required for encoding the size of a chunk,
     * allowing for an extra byte, in case the filter makes the chunk larger.
     */
    ctx->chunk_size_len = 1 + ((H5VM_log2_gen((uint64_t)udata->chunk_size) + 8) / 8);
    if(ctx->chunk_size_len > 8)
        ctx->chunk_size_len = 8;

    /* Set return value */
    ret_value = ctx;

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


/*-------------------------------------------------------------------------
 * Function:    H5D__bt2_dst_context
 *
 * Purpose:     Destroy client callback context
 *
 * Return:      Success:        non-negative
 *              Failure:        negative
 *
 * Programmer:  Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_dst_context(void *_ctx)
{
    H5D_bt2_ctx_t *ctx = (H5D_bt2_ctx_t *)_ctx;       /* Callback context structure */

    FUNC_ENTER_STATIC_NOERR

    /* Sanity check */
    HDassert(ctx);

    /* Free array for chunk dimension sizes */
    if(ctx->dim)
	(void)H5FL_ARR_FREE(uint32_t, ctx->dim); 
    /* Release callback context */
    ctx = H5FL_FREE(H5D_bt2_ctx_t, ctx);

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5D__bt2_dst_context() */


/*-------------------------------------------------------------------------
 * Function:    H5D__bt2_store
 *
 * Purpose:     Store native information into record for v2 B-tree
 *		(non-filtered)
 *
 * Return:      Success:        non-negative
 *              Failure:        negative
 *
 * Programmer:  Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_store(void *record, const void *_udata)
{
    const H5D_bt2_ud_t *udata = (const H5D_bt2_ud_t *)_udata;	/* User data */

    FUNC_ENTER_STATIC_NOERR

    *(H5D_chunk_rec_t *)record = udata->rec;

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5D__bt2_store() */


/*-------------------------------------------------------------------------
 * Function:    H5D__bt2_compare
 *
 * Purpose:     Compare two native information records, according to some key
 *		(non-filtered)
 *
 * Return:      <0 if rec1 < rec2
 *              =0 if rec1 == rec2
 *              >0 if rec1 > rec2
 *
 * Programmer:  Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_compare(const void *_udata, const void *_rec2, int *result)
{
    const H5D_bt2_ud_t *udata = (const H5D_bt2_ud_t *)_udata;	/* User data */
    const H5D_chunk_rec_t *rec1 = &(udata->rec);	/* The search record */
    const H5D_chunk_rec_t *rec2 = (const H5D_chunk_rec_t *)_rec2;	/* The native record */
    herr_t ret_value = SUCCEED; 	/* Return value */

    FUNC_ENTER_STATIC_NOERR

    /* Sanity checks */
    HDassert(rec1);
    HDassert(rec2);

    /* Compare the offsets but ignore the other fields */
    *result = H5VM_vector_cmp_u(udata->ndims, rec1->scaled, rec2->scaled);

    FUNC_LEAVE_NOAPI(ret_value)
} /* H5D__bt2_compare() */


/*-------------------------------------------------------------------------
 * Function:    H5D__bt2_unfilt_encode
 *
 * Purpose:     Encode native information into raw form for storing on disk
 *		(non-filtered)
 *
 * Return:      Success:        non-negative
 *              Failure:        negative
 *
 * Programmer:  Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_unfilt_encode(uint8_t *raw, const void *_record, void *_ctx)
{
    H5D_bt2_ctx_t *ctx = (H5D_bt2_ctx_t *)_ctx;	/* Callback context structure */
    const H5D_chunk_rec_t *record = (const H5D_chunk_rec_t *)_record; /* The native record */
    unsigned u;			/* Local index variable */

    FUNC_ENTER_STATIC_NOERR

    /* Sanity check */
    HDassert(ctx);

    /* Encode the record's fields */
    H5F_addr_encode_len(ctx->sizeof_addr, &raw, record->chunk_addr);
    /* (Don't encode the chunk size & filter mask for non-filtered B-tree records) */
    for(u = 0; u < ctx->ndims; u++)
	UINT64ENCODE(raw, record->scaled[u]);

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5D__bt2_unfilt_encode() */


/*-------------------------------------------------------------------------
 * Function:    H5D__bt2_unfilt_decode
 *
 * Purpose:     Decode raw disk form of record into native form
 *		(non-filtered)
 *
 * Return:      Success:        non-negative
 *              Failure:        negative
 *
 * Programmer:  Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_unfilt_decode(const uint8_t *raw, void *_record, void *_ctx)
{
    H5D_bt2_ctx_t *ctx = (H5D_bt2_ctx_t *)_ctx;       	/* Callback context structure */
    H5D_chunk_rec_t *record = (H5D_chunk_rec_t *)_record;	/* The native record */
    unsigned	u;		/* Local index variable */

    FUNC_ENTER_STATIC_NOERR

    /* Sanity check */
    HDassert(ctx);

    /* Decode the record's fields */
    H5F_addr_decode_len(ctx->sizeof_addr, &raw, &record->chunk_addr);
    record->nbytes = ctx->chunk_size;
    record->filter_mask = 0;
    for(u = 0; u < ctx->ndims; u++)
	UINT64DECODE(raw, record->scaled[u]);

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5D__bt2_unfilt_decode() */


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_unfilt_debug
 *
 * Purpose:	Debug native form of record (non-filtered)
 *
 * Return:	Success:	non-negative
 *		Failure:	negative
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_unfilt_debug(FILE *stream, int indent, int fwidth,
    const void *_record, const void *_ctx)
{
    const H5D_chunk_rec_t *record = (const H5D_chunk_rec_t *)_record; /* The native record */
    const H5D_bt2_ctx_t *ctx = (const H5D_bt2_ctx_t *)_ctx; 	  /* Callback context */
    unsigned u;		/* Local index variable */

    FUNC_ENTER_STATIC_NOERR

    /* Sanity checks */
    HDassert(record);
    HDassert(ctx->chunk_size == record->nbytes);
    HDassert(0 == record->filter_mask);

    HDfprintf(stream, "%*s%-*s %a\n", indent, "", fwidth, "Chunk address:", record->chunk_addr);

    HDfprintf(stream, "%*s%-*s {", indent, "", fwidth, "Logical offset:");
    for(u = 0; u < ctx->ndims; u++)
        HDfprintf(stream, "%s%Hd", u?", ":"", record->scaled[u] * ctx->dim[u]);
    HDfputs("}\n", stream);

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5D__bt2_unfilt_debug() */


/*-------------------------------------------------------------------------
 * Function:    H5D__bt2_filt_encode
 *
 * Purpose:     Encode native information into raw form for storing on disk
 *		(filtered)
 *
 * Return:      Success:        non-negative
 *              Failure:        negative
 *
 * Programmer:  Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_filt_encode(uint8_t *raw, const void *_record, void *_ctx)
{
    H5D_bt2_ctx_t *ctx = (H5D_bt2_ctx_t *)_ctx;	/* Callback context structure */
    const H5D_chunk_rec_t *record = (const H5D_chunk_rec_t *)_record;  /* The native record */
    unsigned u;         /* Local index variable */

    FUNC_ENTER_STATIC_NOERR

    /* Sanity check */
    HDassert(ctx);
    HDassert(record);
    HDassert(H5F_addr_defined(record->chunk_addr));
    HDassert(0 != record->nbytes);

    /* Encode the record's fields */
    H5F_addr_encode_len(ctx->sizeof_addr, &raw, record->chunk_addr);
    UINT64ENCODE_VAR(raw, record->nbytes, ctx->chunk_size_len);
    UINT32ENCODE(raw, record->filter_mask);
    for(u = 0; u < ctx->ndims; u++)
	UINT64ENCODE(raw, record->scaled[u]);

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5D__bt2_filt_encode() */


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_filt_decode
 *
 * Purpose:	Decode raw disk form of record into native form
 *		(filtered)
 *
 * Return:	Success:	non-negative
 *		Failure:	negative
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_filt_decode(const uint8_t *raw, void *_record, void *_ctx)
{
    H5D_bt2_ctx_t *ctx = (H5D_bt2_ctx_t *)_ctx;       		/* Callback context structure */
    H5D_chunk_rec_t *record = (H5D_chunk_rec_t *)_record;	/* The native record */
    unsigned u;         /* Local index variable */

    FUNC_ENTER_STATIC_NOERR

    /* Sanity check */
    HDassert(ctx);
    HDassert(record);

    /* Decode the record's fields */
    H5F_addr_decode_len(ctx->sizeof_addr, &raw, &record->chunk_addr);
    UINT64DECODE_VAR(raw, record->nbytes, ctx->chunk_size_len);
    UINT32DECODE(raw, record->filter_mask);
    for(u = 0; u < ctx->ndims; u++)
	UINT64DECODE(raw, record->scaled[u]);

    /* Sanity checks */
    HDassert(H5F_addr_defined(record->chunk_addr));
    HDassert(0 != record->nbytes);

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5D__bt2_filt_decode() */


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_filt_debug
 *
 * Purpose:	Debug native form of record (filtered)
 *
 * Return:	Success:	non-negative
 *		Failure:	negative
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_filt_debug(FILE *stream, int indent, int fwidth,
    const void *_record, const void *_ctx)
{
    const H5D_chunk_rec_t *record = (const H5D_chunk_rec_t *)_record;   /* The native record */
    const H5D_bt2_ctx_t *ctx = (const H5D_bt2_ctx_t *)_ctx; 	/* Callback context */
    unsigned u;		/* Local index variable */
 
    FUNC_ENTER_STATIC_NOERR

    /* Sanity checks */
    HDassert(record);
    HDassert(H5F_addr_defined(record->chunk_addr));
    HDassert(0 != record->nbytes);

    HDfprintf(stream, "%*s%-*s %a\n", indent, "", fwidth, "Chunk address:", record->chunk_addr);
    HDfprintf(stream, "%*s%-*s %u bytes\n", indent, "", fwidth, "Chunk size:", (unsigned)record->nbytes);
    HDfprintf(stream, "%*s%-*s 0x%08x\n", indent, "", fwidth, "Filter mask:", record->filter_mask);

    HDfprintf(stream, "%*s%-*s {", indent, "", fwidth, "Logical offset:");
    for(u = 0; u < ctx->ndims; u++)
        HDfprintf(stream, "%s%Hd", u?", ":"", record->scaled[u] * ctx->dim[u]);
    HDfputs("}\n", stream);

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5D__bt2_filt_debug() */


/*-------------------------------------------------------------------------
 * Function:    H5D__bt2_idx_init
 *
 * Purpose:     Initialize the indexing information for a dataset.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 * Programmer:  Neil Fortner
 *              Wednesday, May 23, 2012
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_idx_init(const H5D_chk_idx_info_t H5_ATTR_UNUSED *idx_info,
    const H5S_t H5_ATTR_UNUSED *space, haddr_t dset_ohdr_addr)
{
    FUNC_ENTER_STATIC_NOERR

    /* Check args */
    HDassert(H5F_addr_defined(dset_ohdr_addr));

    idx_info->storage->u.btree2.dset_ohdr_addr = dset_ohdr_addr;

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


/*-------------------------------------------------------------------------
 * Function:	H5D__btree2_idx_depend
 *
 * Purpose:	Create flush dependency between v2 B-tree and dataset's
 *              object header.
 *
 * Return:	Success:	non-negative
 *		Failure:	negative
 *
 * Programmer:	Quincey Koziol
 *		Friday, December 18, 2015
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__btree2_idx_depend(const H5D_chk_idx_info_t *idx_info)
{
    H5O_t *oh = NULL;                   /* Object header */
    H5O_loc_t oloc;                     /* Temporary object header location for dataset */
    H5AC_proxy_entry_t *oh_proxy;       /* Dataset's object header proxy */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_STATIC

    /* Check args */
    HDassert(idx_info);
    HDassert(idx_info->f);
    HDassert(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE);
    HDassert(idx_info->pline);
    HDassert(idx_info->layout);
    HDassert(H5D_CHUNK_IDX_BT2 == idx_info->layout->idx_type);
    HDassert(idx_info->storage);
    HDassert(H5D_CHUNK_IDX_BT2 == idx_info->storage->idx_type);
    HDassert(H5F_addr_defined(idx_info->storage->idx_addr));
    HDassert(idx_info->storage->u.btree2.bt2);

    /* Set up object header location for dataset */
    H5O_loc_reset(&oloc);
    oloc.file = idx_info->f;
    oloc.addr = idx_info->storage->u.btree.dset_ohdr_addr;

    /* Get header */
    if(NULL == (oh = H5O_protect(&oloc, H5AC__READ_ONLY_FLAG, TRUE)))
        HGOTO_ERROR(H5E_DATASET, H5E_CANTPROTECT, FAIL, "unable to protect object header")

    /* Retrieve the dataset's object header proxy */
    if(NULL == (oh_proxy = H5O_get_proxy(oh)))
        HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to get dataset object header proxy")

    /* Make the v2 B-tree a child flush dependency of the dataset's object header proxy */
    if(H5B2_depend(idx_info->storage->u.btree2.bt2, oh_proxy) < 0)
        HGOTO_ERROR(H5E_DATASET, H5E_CANTDEPEND, FAIL, "unable to create flush dependency on object header proxy")

done:
    /* Release the object header from the cache */
    if(oh && H5O_unprotect(&oloc, oh, H5AC__NO_FLAGS_SET) < 0)
        HDONE_ERROR(H5E_DATASET, H5E_CANTUNPROTECT, FAIL, "unable to release object header")

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_open()
 *
 * Purpose:	Opens an existing v2 B-tree.
 *
 * Note:	This information is passively initialized from each index
 *              operation callback because those abstract chunk index operations
 *              are designed to work with the v2 B-tree chunk indices also,
 *              which don't require an 'open' for the data structure.
 *
 * Return:	Success:	non-negative
 *		Failure:	negative
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_idx_open(const H5D_chk_idx_info_t *idx_info)
{
    H5D_bt2_ctx_ud_t u_ctx;	/* user data for creating context */
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_STATIC

    /* Check args */
    HDassert(idx_info);
    HDassert(idx_info->f);
    HDassert(idx_info->pline);
    HDassert(idx_info->layout);
    HDassert(H5D_CHUNK_IDX_BT2 == idx_info->layout->idx_type);
    HDassert(idx_info->storage);
    HDassert(H5F_addr_defined(idx_info->storage->idx_addr));
    HDassert(NULL == idx_info->storage->u.btree2.bt2);

    /* Set up the user data */
    u_ctx.f = idx_info->f;
    u_ctx.ndims = idx_info->layout->ndims - 1;
    u_ctx.chunk_size = idx_info->layout->size;
    u_ctx.dim = idx_info->layout->dim;

    /* Open v2 B-tree for the chunk index */
    if(NULL == (idx_info->storage->u.btree2.bt2 = H5B2_open(idx_info->f, idx_info->storage->idx_addr, &u_ctx)))
	HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "can't open v2 B-tree for tracking chunked dataset")

    /* Check for SWMR writes to the file */
    if(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)
        if(H5D__btree2_idx_depend(idx_info) < 0)
            HGOTO_ERROR(H5E_DATASET, H5E_CANTDEPEND, FAIL, "unable to create flush dependency on object header")

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_create 
 *
 * Purpose:	Create the v2 B-tree for tracking dataset chunks 
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_idx_create(const H5D_chk_idx_info_t *idx_info)
{
    H5B2_create_t bt2_cparam;           /* v2 B-tree creation parameters */
    H5D_bt2_ctx_ud_t u_ctx;		/* data for context call */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_STATIC

    /* Check args */
    HDassert(idx_info);
    HDassert(idx_info->f);
    HDassert(idx_info->pline);
    HDassert(idx_info->layout);
    HDassert(idx_info->storage);
    HDassert(!H5F_addr_defined(idx_info->storage->idx_addr));

    bt2_cparam.rrec_size = H5F_SIZEOF_ADDR(idx_info->f)	/* Address of chunk */
		  + (idx_info->layout->ndims - 1) * 8;	/* # of dimensions x 64-bit chunk offsets */

    /* General parameters */
    if(idx_info->pline->nused > 0) {
	unsigned chunk_size_len;        /* Size of encoded chunk size */

        /* 
	 * Compute the size required for encoding the size of a chunk,
         * allowing for an extra byte, in case the filter makes the chunk larger.
         */
        chunk_size_len = 1 + ((H5VM_log2_gen((uint64_t)idx_info->layout->size) + 8) / 8);
        if(chunk_size_len > 8)
            chunk_size_len = 8;

	bt2_cparam.rrec_size  += chunk_size_len	+ 4;	/* Size of encoded chunk size & filter mask */
	bt2_cparam.cls = H5D_BT2_FILT;
    } /* end if */
    else
	bt2_cparam.cls = H5D_BT2;

    bt2_cparam.node_size = idx_info->layout->u.btree2.cparam.node_size;
    bt2_cparam.split_percent = idx_info->layout->u.btree2.cparam.split_percent;
    bt2_cparam.merge_percent = idx_info->layout->u.btree2.cparam.merge_percent;

    u_ctx.f = idx_info->f;
    u_ctx.ndims = idx_info->layout->ndims - 1;
    u_ctx.chunk_size = idx_info->layout->size;
    u_ctx.dim = idx_info->layout->dim;

    /* Create the v2 B-tree for the chunked dataset */
    if(NULL == (idx_info->storage->u.btree2.bt2 = H5B2_create(idx_info->f, &bt2_cparam, &u_ctx)))
        HGOTO_ERROR(H5E_DATASET, H5E_CANTCREATE, FAIL, "can't create v2 B-tree for tracking chunked dataset")

    /* Retrieve the v2 B-tree's address in the file */
    if(H5B2_get_addr(idx_info->storage->u.btree2.bt2, &(idx_info->storage->idx_addr)) < 0)
        HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get v2 B-tree address for tracking chunked dataset")

    /* Check for SWMR writes to the file */
    if(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)
        if(H5D__btree2_idx_depend(idx_info) < 0)
            HGOTO_ERROR(H5E_DATASET, H5E_CANTDEPEND, FAIL, "unable to create flush dependency on object header")

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_is_space_alloc
 *
 * Purpose:	Query if space is allocated for index method
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static hbool_t
H5D__bt2_idx_is_space_alloc(const H5O_storage_chunk_t *storage)
{
    FUNC_ENTER_STATIC_NOERR

    /* Check args */
    HDassert(storage);

    FUNC_LEAVE_NOAPI((hbool_t)H5F_addr_defined(storage->idx_addr))
} /* end H5D__bt2_idx_is_space_alloc() */


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_mod_cb
 *
 * Purpose:	Modify record for dataset chunk when it is found in a v2 B-tree.
 * 		This is the callback for H5B2_modify() which is called in 
 *		H5D__bt2_idx_insert().
 *
 * Return:	Success:	non-negative
 *		Failure:	negative
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_mod_cb(void *_record, void *_op_data, hbool_t *changed)
{
    H5D_bt2_ud_t *op_data = (H5D_bt2_ud_t *)_op_data;       /* User data for v2 B-tree calls */
    H5D_chunk_rec_t *record = (H5D_chunk_rec_t *)_record;   /* Chunk record */

    FUNC_ENTER_STATIC_NOERR

/* Sanity check */
#ifndef NDEBUG
{
    unsigned u;                 /* Local index variable */

    for(u = 0; u < op_data->ndims; u++)
        HDassert(record->scaled[u] == op_data->rec.scaled[u]);
}
#endif /* NDEBUG */

    /* Modify record */
    *record = op_data->rec;

    /* Note that the record changed */
    *changed = TRUE;

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_insert
 *
 * Purpose:	Insert chunk address into the indexing structure.
 *		A non-filtered chunk: 
 *		  Should not exist
 *		  Allocate the chunk and pass chunk address back up
 *		A filtered chunk:
 *		  If it was not found, create the chunk and pass chunk address back up
 *		  If it was found but its size changed, reallocate the chunk and pass chunk address back up
 *		  If it was found but its size was the same, pass chunk address back up
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_idx_insert(const H5D_chk_idx_info_t *idx_info, H5D_chunk_ud_t *udata,
    const H5D_t H5_ATTR_UNUSED *dset)
{
    H5B2_t *bt2;                        /* v2 B-tree handle for indexing chunks */
    H5D_bt2_ud_t bt2_udata;             /* User data for v2 B-tree calls */
    unsigned u;				/* Local index variable */
    herr_t ret_value = SUCCEED;		/* Return value */

    FUNC_ENTER_STATIC

    /* Sanity checks */
    HDassert(idx_info);
    HDassert(idx_info->f);
    HDassert(idx_info->pline);
    HDassert(idx_info->layout);
    HDassert(idx_info->storage);
    HDassert(H5F_addr_defined(idx_info->storage->idx_addr));
    HDassert(udata);
    HDassert(H5F_addr_defined(udata->chunk_block.offset));

    /* Check if the v2 B-tree is open yet */
    if(NULL == idx_info->storage->u.btree2.bt2) {
	/* Open existing v2 B-tree */
        if(H5D__bt2_idx_open(idx_info) < 0)
            HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "can't open v2 B-tree")
    } /* end if */
    else  /* Patch the top level file pointer contained in bt2 if needed */
	if(H5B2_patch_file(idx_info->storage->u.btree2.bt2, idx_info->f) < 0)
            HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "can't patch v2 B-tree file pointer")

    /* Set convenience pointer to v2 B-tree structure */
    bt2 = idx_info->storage->u.btree2.bt2;

    /* Set up callback info */
    bt2_udata.ndims = idx_info->layout->ndims - 1;
    bt2_udata.rec.chunk_addr = udata->chunk_block.offset;
    if(idx_info->pline->nused > 0) { /* filtered chunk */
        H5_CHECKED_ASSIGN(bt2_udata.rec.nbytes, uint32_t, udata->chunk_block.length, hsize_t);
        bt2_udata.rec.filter_mask = udata->filter_mask;
    } /* end if */
    else { /* non-filtered chunk */
        bt2_udata.rec.nbytes = idx_info->layout->size;
        bt2_udata.rec.filter_mask = 0;
    } /* end else */
    for(u = 0; u < (idx_info->layout->ndims - 1); u++)
        bt2_udata.rec.scaled[u] = udata->common.scaled[u];

    /* Update record for v2 B-tree (could be insert or modify) */
    if(H5B2_update(bt2, &bt2_udata, H5D__bt2_mod_cb, &bt2_udata) < 0)
        HGOTO_ERROR(H5E_DATASET, H5E_CANTUPDATE, FAIL, "unable to update record in v2 B-tree")

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_found_cb
 *
 * Purpose:	Retrieve record for dataset chunk when it is found in a v2 B-tree.
 * 		This is the callback for H5B2_find() which is called in 
 *		H5D__bt2_idx_get_addr() and H5D__bt2_idx_insert().
 *
 * Return:	Success:	non-negative
 *		Failure:	negative
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_found_cb(const void *nrecord, void *op_data)
{
    FUNC_ENTER_STATIC_NOERR

    *(H5D_chunk_rec_t *)op_data = *(const H5D_chunk_rec_t *)nrecord;

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5D__bt2_found_cb() */


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_get_addr
 *
 * Purpose:	Get the file address of a chunk if file space has been
 *		assigned.  Save the retrieved information in the udata
 *		supplied.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_idx_get_addr(const H5D_chk_idx_info_t *idx_info, H5D_chunk_ud_t *udata)
{
    H5B2_t 	*bt2;                   /* v2 B-tree handle for indexing chunks */
    H5D_bt2_ud_t bt2_udata;             /* User data for v2 B-tree calls */
    H5D_chunk_rec_t found_rec;  	/* Record found from searching for object */
    unsigned	u;			/* Local index variable */
    herr_t	ret_value = SUCCEED;	/* Return value */

    FUNC_ENTER_STATIC

    /* Sanity checks */
    HDassert(idx_info);
    HDassert(idx_info->f);
    HDassert(idx_info->pline);
    HDassert(idx_info->layout);
    HDassert(idx_info->layout->ndims > 0);
    HDassert(idx_info->storage);
    HDassert(H5F_addr_defined(idx_info->storage->idx_addr));
    HDassert(udata);

    /* Check if the v2 B-tree is open yet */
    if(NULL == idx_info->storage->u.btree2.bt2) {
	/* Open existing v2 B-tree */
        if(H5D__bt2_idx_open(idx_info) < 0)
            HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "can't open v2 B-tree")
    } /* end if */
    else  /* Patch the top level file pointer contained in bt2 if needed */
	if(H5B2_patch_file(idx_info->storage->u.btree2.bt2, idx_info->f) < 0)
            HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "can't patch v2 B-tree file pointer")

    /* Set convenience pointer to v2 B-tree structure */
    bt2 = idx_info->storage->u.btree2.bt2;

    /* Clear the found record */
    found_rec.chunk_addr = HADDR_UNDEF;
    found_rec.nbytes = 0;
    found_rec.filter_mask = 0;

    /* Prepare user data for compare callback */
    bt2_udata.rec.chunk_addr = HADDR_UNDEF;
    bt2_udata.ndims = idx_info->layout->ndims - 1;

    /* Set the chunk offset to be searched for */
    for(u = 0; u < (idx_info->layout->ndims - 1); u++)
        bt2_udata.rec.scaled[u] = udata->common.scaled[u];

    /* Go get chunk information from v2 B-tree */
    if(H5B2_find(bt2, &bt2_udata, H5D__bt2_found_cb, &found_rec) < 0)
        HGOTO_ERROR(H5E_HEAP, H5E_NOTFOUND, FAIL, "can't find object in v2 B-tree")

    /* Set common info for the chunk */
    udata->chunk_block.offset = found_rec.chunk_addr;

    /* Check for setting other info */
    if(H5F_addr_defined(udata->chunk_block.offset)) {
        /* Sanity check */
        HDassert(0 != found_rec.nbytes);

        /* Set other info for the chunk */
        if(idx_info->pline->nused > 0) { /* filtered chunk */
            udata->chunk_block.length = found_rec.nbytes;
            udata->filter_mask = found_rec.filter_mask;
        } /* end if */
        else { /* non-filtered chunk */
            udata->chunk_block.length = idx_info->layout->size;
            udata->filter_mask = 0;
        } /* end else */
    } /* end if */
    else {
        udata->chunk_block.length = 0;
        udata->filter_mask = 0;
    } /* end else */

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_iterate_cb
 *
 * Purpose:	Translate the B-tree specific chunk record into a generic
 *              form and make the callback to the generic chunk callback
 *              routine.
 * 		This is the callback for H5B2_iterate() which is called in 
 *		H5D__bt2_idx_iterate().
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static int
H5D__bt2_idx_iterate_cb(const void *_record, void *_udata)
{
    H5D_bt2_it_ud_t *udata = (H5D_bt2_it_ud_t *)_udata; /* User data */
    const H5D_chunk_rec_t *record = (const H5D_chunk_rec_t *)_record;   /* Native record */
    int ret_value = -1;         /* Return value */

    FUNC_ENTER_STATIC_NOERR

    /* Make "generic chunk" callback */
    if((ret_value = (udata->cb)(record, udata->udata)) < 0)
        HERROR(H5E_DATASET, H5E_CALLBACK, "failure in generic chunk iterator callback");

    FUNC_LEAVE_NOAPI(ret_value)
} /* H5D__bt2_idx_iterate_cb() */


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_iterate
 *
 * Purpose:	Iterate over the chunks in an index, making a callback
 *              for each one.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static int
H5D__bt2_idx_iterate(const H5D_chk_idx_info_t *idx_info,
    H5D_chunk_cb_func_t chunk_cb, void *chunk_udata)
{
    H5B2_t *bt2;	        /* v2 B-tree handle for indexing chunks */
    H5D_bt2_it_ud_t udata; 	/* User data for B-tree iterator callback */
    int ret_value = FAIL;	/* Return value */

    FUNC_ENTER_STATIC

    /* Sanity checks */
    HDassert(idx_info);
    HDassert(idx_info->f);
    HDassert(idx_info->pline);
    HDassert(idx_info->layout);
    HDassert(idx_info->storage);
    HDassert(H5F_addr_defined(idx_info->storage->idx_addr));
    HDassert(chunk_cb);
    HDassert(chunk_udata);

    /* Check if the v2 B-tree is open yet */
    if(NULL == idx_info->storage->u.btree2.bt2) {
	/* Open existing v2 B-tree */
        if(H5D__bt2_idx_open(idx_info) < 0)
            HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "can't open v2 B-tree")
    } /* end if */
    else  /* Patch the top level file pointer contained in bt2 if needed */
	if(H5B2_patch_file(idx_info->storage->u.btree2.bt2, idx_info->f) < 0)
            HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "can't patch v2 B-tree file pointer")

    /* Set convenience pointer to v2 B-tree structure */
    bt2 = idx_info->storage->u.btree2.bt2;

    /* Prepare user data for iterate callback */
    udata.cb = chunk_cb;
    udata.udata = chunk_udata;

    /* Iterate over the records in the v2 B-tree */
    if((ret_value = H5B2_iterate(bt2, H5D__bt2_idx_iterate_cb, &udata)) < 0)
        HERROR(H5E_DATASET, H5E_BADITER, "unable to iterate over chunk v2 B-tree");

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_remove_cb()
 *
 * Purpose:	Free space for 'dataset chunk' object as v2 B-tree
 *             	is being deleted or v2 B-tree node is removed.
 * 		This is the callback for H5B2_remove() and H5B2_delete() which 
 *		which are called in H5D__bt2_idx_remove() and H5D__bt2_idx_delete().
 *
 * Return:	Success:	non-negative
 *		Failure:	negative
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_remove_cb(const void *_record, void *_udata)
{
    const H5D_chunk_rec_t *record = (const H5D_chunk_rec_t *)_record;	/* The native record */
    H5F_t *f = (H5F_t *)_udata;         /* User data for removal callback */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_STATIC

    /* Sanity checks */
    HDassert(f);

    /* Free the space in the file for the object being removed */
    H5_CHECK_OVERFLOW(record->nbytes, uint32_t, hsize_t);
    if(H5MF_xfree(f, H5FD_MEM_DRAW, record->chunk_addr, (hsize_t)record->nbytes) < 0)
        HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to free chunk")

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_remove
 *
 * Purpose:	Remove chunk from index.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_idx_remove(const H5D_chk_idx_info_t *idx_info, H5D_chunk_common_ud_t *udata)
{
    H5B2_t 	*bt2;                   /* v2 B-tree handle for indexing chunks */
    H5D_bt2_ud_t bt2_udata;             /* User data for v2 B-tree find call */
    unsigned 	u;			/* Local index variable */
    herr_t	ret_value = SUCCEED;	/* Return value */

    FUNC_ENTER_STATIC

    /* Sanity checks */
    HDassert(idx_info);
    HDassert(idx_info->f);
    HDassert(idx_info->pline);
    HDassert(idx_info->layout);
    HDassert(idx_info->storage);
    HDassert(H5F_addr_defined(idx_info->storage->idx_addr));
    HDassert(udata);

    /* Check if the v2 B-tree is open yet */
    if(NULL == idx_info->storage->u.btree2.bt2) {
	/* Open existing v2 B-tree */
        if(H5D__bt2_idx_open(idx_info) < 0)
            HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "can't open v2 B-tree")
    } /* end if */
    else  /* Patch the top level file pointer contained in bt2 if needed */
	if(H5B2_patch_file(idx_info->storage->u.btree2.bt2, idx_info->f) < 0)
            HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "can't patch v2 B-tree file pointer")

    /* Set convenience pointer to v2 B-tree structure */
    bt2 = idx_info->storage->u.btree2.bt2;

    /* Prepare user data for compare callback */
    bt2_udata.ndims = idx_info->layout->ndims - 1;

    /* Initialize the record to search for */
    for(u = 0; u < (idx_info->layout->ndims - 1); u++)
        bt2_udata.rec.scaled[u] = udata->scaled[u];

    /* Remove the record for the "dataset chunk" object from the v2 B-tree */
    /* (space in the file for the object is freed in the 'remove' callback) */
    if(H5B2_remove(bt2, &bt2_udata, (H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE) ? NULL : H5D__bt2_remove_cb, idx_info->f) < 0)
        HGOTO_ERROR(H5E_DATASET, H5E_CANTREMOVE, FAIL, "can't remove object from B-tree")

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_delete
 *
 * Purpose:	Delete index and raw data storage for entire dataset
 *              (i.e. all chunks)
 *
 * Return:	Success:	Non-negative
 *		Failure:	negative
 *
 * Programmer:	Vailin Choi; June 2010
 *
 * Modifications:
 *	Vailin Choi; March 2011
 *	Initialize size of an unfiltered chunk.
 *	This is a fix for for the assertion failure in:
 *	[src/H5FSsection.c:968: H5FS_sect_link_size: Assertion `bin < sinfo->nbins' failed.]
 *	which is uncovered by test_unlink_chunked_dataset() in test/unlink.c
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_idx_delete(const H5D_chk_idx_info_t *idx_info)
{
    H5B2_remove_t remove_op;		/* The removal callback */
    H5D_bt2_ctx_ud_t u_ctx;		/* data for context call */
    herr_t ret_value = SUCCEED;     	/* Return value */

    FUNC_ENTER_STATIC

    /* Sanity checks */
    HDassert(idx_info);
    HDassert(idx_info->f);
    HDassert(idx_info->pline);
    HDassert(idx_info->layout);
    HDassert(idx_info->storage);

    /* Check if the index data structure has been allocated */
    if(H5F_addr_defined(idx_info->storage->idx_addr)) {
	/* Set up user data for creating context */
	u_ctx.f = idx_info->f;
	u_ctx.ndims = idx_info->layout->ndims - 1;
	u_ctx.chunk_size = idx_info->layout->size;
	u_ctx.dim = idx_info->layout->dim;

	/* Set remove operation.  Do not remove chunks in SWMR_WRITE mode */
        if(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)
            remove_op = NULL;
        else
            remove_op = H5D__bt2_remove_cb;

	/* Delete the v2 B-tree */
	/*(space in the file for each object is freed in the 'remove' callback) */
	if(H5B2_delete(idx_info->f, idx_info->storage->idx_addr, &u_ctx, remove_op, idx_info->f) < 0)
	    HGOTO_ERROR(H5E_DATASET, H5E_CANTDELETE, FAIL, "can't delete v2 B-tree")

	idx_info->storage->idx_addr = HADDR_UNDEF;
    } /* end if */

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_copy_setup
 *
 * Purpose:	Set up any necessary information for copying chunks
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_idx_copy_setup(const H5D_chk_idx_info_t *idx_info_src,
    const H5D_chk_idx_info_t *idx_info_dst)
{
    herr_t      ret_value = SUCCEED;        /* Return value */

    FUNC_ENTER_STATIC

    /* Source file */
    HDassert(idx_info_src);
    HDassert(idx_info_src->f);
    HDassert(idx_info_src->pline);
    HDassert(idx_info_src->layout);
    HDassert(idx_info_src->storage);

    /* Destination file */
    HDassert(idx_info_dst);
    HDassert(idx_info_dst->f);
    HDassert(idx_info_dst->pline);
    HDassert(idx_info_dst->layout);
    HDassert(idx_info_dst->storage);
    HDassert(!H5F_addr_defined(idx_info_dst->storage->idx_addr));

    /* Check if the source v2 B-tree is open yet */
    if(NULL == idx_info_src->storage->u.btree2.bt2)
        if(H5D__bt2_idx_open(idx_info_src) < 0)
            HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "can't open v2 B-tree")

    /* Set copied metadata tag */
    H5_BEGIN_TAG(H5AC__COPIED_TAG);

    /* Create v2 B-tree that describes the chunked dataset in the destination file */
    if(H5D__bt2_idx_create(idx_info_dst) < 0)
        HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to initialize chunked storage")
    HDassert(H5F_addr_defined(idx_info_dst->storage->idx_addr));

    /* Reset metadata tag */
    H5_END_TAG

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_copy_shutdown
 *
 * Purpose:	Shutdown any information from copying chunks
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_idx_copy_shutdown(H5O_storage_chunk_t *storage_src,
    H5O_storage_chunk_t *storage_dst)
{
    herr_t      ret_value = SUCCEED;       /* Return value */

    FUNC_ENTER_STATIC

    /* Check args */
    HDassert(storage_src);
    HDassert(storage_src->u.btree2.bt2);
    HDassert(storage_dst);
    HDassert(storage_dst->u.btree2.bt2);

    /* Close v2 B-tree for source file */
    if(H5B2_close(storage_src->u.btree2.bt2) < 0)
        HGOTO_ERROR(H5E_DATASET, H5E_CANTCLOSEOBJ, FAIL, "unable to close v2 B-tree")
    storage_src->u.btree2.bt2 = NULL;

    /* Close v2 B-tree for destination file */
    if(H5B2_close(storage_dst->u.btree2.bt2) < 0)
        HGOTO_ERROR(H5E_DATASET, H5E_CANTCLOSEOBJ, FAIL, "unable to close v2 B-tree")
    storage_dst->u.btree2.bt2 = NULL;

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


/*-------------------------------------------------------------------------
 * Function:    H5D__bt2_idx_size
 *
 * Purpose:     Retrieve the amount of index storage for chunked dataset
 *
 * Return:      Success:        Non-negative
 *              Failure:        negative
 *
 * Programmer:  Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_idx_size(const H5D_chk_idx_info_t *idx_info, hsize_t *index_size)
{
    H5B2_t *bt2_cdset = NULL;		/* Pointer to v2 B-tree structure */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_STATIC

    /* Check args */
    HDassert(idx_info);
    HDassert(idx_info->f);
    HDassert(idx_info->pline);
    HDassert(idx_info->layout);
    HDassert(idx_info->storage);
    HDassert(H5F_addr_defined(idx_info->storage->idx_addr));
    HDassert(index_size);

    /* Open v2 B-tree */
    if(H5D__bt2_idx_open(idx_info) < 0)
	HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "can't open v2 B-tree")

    /* Set convenience pointer to v2 B-tree structure */
    bt2_cdset = idx_info->storage->u.btree2.bt2;

    /* Get v2 B-tree size for indexing chunked dataset */
    if(H5B2_size(bt2_cdset, index_size) < 0)
	HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "can't retrieve v2 B-tree storage info for chunked dataset")

done:
    /* Close v2 B-tree index */
    if(bt2_cdset && H5B2_close(bt2_cdset) < 0)
        HDONE_ERROR(H5E_SYM, H5E_CLOSEERROR, FAIL, "can't close v2 B-tree for tracking chunked dataset")
    idx_info->storage->u.btree2.bt2 = NULL;

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_reset
 *
 * Purpose:	Reset indexing information.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_idx_reset(H5O_storage_chunk_t *storage, hbool_t reset_addr)
{
    FUNC_ENTER_STATIC_NOERR

    /* Sanity checks */
    HDassert(storage);

    /* Reset index info */
    if(reset_addr)
	storage->idx_addr = HADDR_UNDEF;
    storage->u.btree2.bt2 = NULL;

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_dump
 *
 * Purpose:	Dump indexing information to a stream.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_idx_dump(const H5O_storage_chunk_t *storage, FILE *stream)
{
    FUNC_ENTER_STATIC_NOERR

    /* Sanity checks */
    HDassert(storage);
    HDassert(stream);

    HDfprintf(stream, "    Address: %a\n", storage->idx_addr);

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


/*-------------------------------------------------------------------------
 * Function:	H5D__bt2_idx_dest
 *
 * Purpose:	Release indexing information in memory.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Vailin Choi; June 2010
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5D__bt2_idx_dest(const H5D_chk_idx_info_t *idx_info)
{
    herr_t      ret_value = SUCCEED;       /* Return value */

    FUNC_ENTER_STATIC

    /* Check args */
    HDassert(idx_info);
    HDassert(idx_info->f);
    HDassert(idx_info->storage);

    /* Check if the v2-btree is open */
    if(idx_info->storage->u.btree2.bt2) {

	/* Patch the top level file pointer contained in bt2 if needed */
	if(H5B2_patch_file(idx_info->storage->u.btree2.bt2, idx_info->f) < 0)
            HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "can't patch v2 B-tree file pointer")

        /* Close v2 B-tree */
        if(H5B2_close(idx_info->storage->u.btree2.bt2) < 0)
            HGOTO_ERROR(H5E_DATASET, H5E_CANTCLOSEOBJ, FAIL, "can't close v2 B-tree")
        idx_info->storage->u.btree2.bt2 = NULL;
    } /* end if */

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