/*-------------------------------------------------------------------------
 * Function:	H5HF_man_iter_start_offset
 *
 * Purpose:	Initialize a block iterator to a particular location, given
 *              an offset in the heap
 *
 * Return:	SUCCEED/FAIL
 *
 * Programmer:	Quincey Koziol
 *		[email protected]
 *		Apr 24 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5HF_man_iter_start_offset(H5HF_hdr_t *hdr, hid_t dxpl_id,
    H5HF_block_iter_t *biter, hsize_t offset)
{
    H5HF_indirect_t *iblock;        /* Indirect block for location context */
    haddr_t iblock_addr;            /* Address of indirect block */
    unsigned iblock_nrows;          /* # of rows in indirect block */
    H5HF_indirect_t *iblock_parent; /* Parent indirect block of location context */
    unsigned iblock_par_entry;      /* Entry within parent indirect block */
    hsize_t curr_offset;        /* Current offset, as adjusted */
    unsigned row;               /* Current row we are on */
    unsigned col;               /* Column in row */
    hbool_t root_block = TRUE;  /* Flag to indicate the current block is the root indirect block */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5HF_man_iter_start_offset)

    /*
     * Check arguments.
     */
    HDassert(biter);
    HDassert(!biter->ready);

    /* Check for empty heap */
    HDassert(offset >= hdr->man_dtable.cparam.start_block_size);

    /* Allocate level structure */
    if(NULL == (biter->curr = H5FL_MALLOC(H5HF_block_loc_t)))
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for direct block free list section")

/*
1:  <Scan down block offsets for dtable rows until find a row >= offset>
    <Set current location's row, col, entry & size>
    <If row < max_direct_rows>
        <Done>
    <Else - row > max_direct_rows>
        <Create new block level>
        <Link new block level into iterator>
        <Adjust offset for block offset for row>
        <Make new block level the current context>
        <Goto 1>

*/
    do {
        hbool_t did_protect;            /* Whether we protected the indirect block or not */

        /* Walk down the rows in the doubling table until we've found the correct row for the next block */
        for(row = 0; row < hdr->man_dtable.max_root_rows; row++)
            if((offset >= hdr->man_dtable.row_block_off[row]) &&
                    (offset < hdr->man_dtable.row_block_off[row] +
                        (hdr->man_dtable.cparam.width * hdr->man_dtable.row_block_size[row])))
                break;

        /* Adjust offset by row offset */
        curr_offset = offset - hdr->man_dtable.row_block_off[row];

        /* Compute column */
        H5_CHECK_OVERFLOW((curr_offset / hdr->man_dtable.row_block_size[row]), hsize_t, unsigned);
        col = (unsigned)(curr_offset / hdr->man_dtable.row_block_size[row]);

        /* Set the current level's context */
        biter->curr->row = row;
        biter->curr->col = col;
        biter->curr->entry = (row * hdr->man_dtable.cparam.width) + col;

        /* Get the context indirect block's information */
        if(root_block) {
            iblock_addr = hdr->man_dtable.table_addr;
            iblock_nrows = hdr->man_dtable.curr_root_rows;
            iblock_parent = NULL;
            iblock_par_entry = 0;

            /* The root block can't go up further... */
            biter->curr->up = NULL;

            /* Next time through the loop will not be with the root indirect block */
            root_block = FALSE;
        } /* end if */
        else {
            hsize_t child_size;     /* Size of new indirect block to create */

            /* Retrieve the parent information from the previous context location */
            iblock_parent = biter->curr->up->context;
            iblock_par_entry = biter->curr->up->entry;

            /* Look up the address of context indirect block */
            iblock_addr = iblock_parent->ents[iblock_par_entry].addr;

            /* Compute # of rows in context indirect block */
            child_size = hdr->man_dtable.row_block_size[biter->curr->up->row];
            iblock_nrows = (H5V_log2_gen(child_size) - hdr->man_dtable.first_row_bits) + 1;
        } /* end else */

        /* Load indirect block for this context location */
        if(NULL == (iblock = H5HF_man_iblock_protect(hdr, dxpl_id, iblock_addr, iblock_nrows, iblock_parent, iblock_par_entry, FALSE, H5AC_WRITE, &did_protect)))
            HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to protect fractal heap indirect block")

        /* Make indirect block the context for the current location */
        biter->curr->context = iblock;

        /* Hold the indirect block with the location */
        if(H5HF_iblock_incr(biter->curr->context) < 0)
            HGOTO_ERROR(H5E_HEAP, H5E_CANTINC, FAIL, "can't increment reference count on shared indirect block")

        /* Release the current indirect block */
        if(H5HF_man_iblock_unprotect(iblock, dxpl_id, H5AC__NO_FLAGS_SET, did_protect) < 0)
            HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release fractal heap indirect block")
        iblock = NULL;

        /* See if the location falls in a direct block row */
        /* Or, if the offset has just filled up a direct or indirect block */
        if(curr_offset == (col * hdr->man_dtable.row_block_size[row]) || row < hdr->man_dtable.max_direct_rows) {
            HDassert(curr_offset - (col * hdr->man_dtable.row_block_size[row]) == 0);
            break;      /* Done now */
        } /* end if */
        /* Indirect block row */
        else {
            H5HF_block_loc_t *new_loc;      /* Pointer to new block location */

            /* Allocate level structure */
            if(NULL == (new_loc = H5FL_MALLOC(H5HF_block_loc_t)))
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for direct block free list section")

            /* Link new level into iterator */
            new_loc->up = biter->curr;

            /* Adjust offset for new level */
            offset = curr_offset - (col * hdr->man_dtable.row_block_size[row]);

            /* Make new block the current context */
            biter->curr = new_loc;
        } /* end else */
    } while(1);       /* Breaks out in middle */

    /* Set flag to indicate block iterator finished initializing */
    biter->ready = TRUE;

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5HF_man_iter_start_offset() */
/*-------------------------------------------------------------------------
 * Function:	H5F_accum_read
 *
 * Purpose:	Attempts to read some data from the metadata accumulator for
 *              a file into a buffer.
 *
 * Note:	We can't change (or add to) the metadata accumulator, because
 *		this might be a speculative read and could possibly read raw
 *		data into the metadata accumulator.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Quincey Koziol
 *		[email protected]
 *		Jan 10 2008
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5F_accum_read(const H5F_t *f, hid_t dxpl_id, H5FD_mem_t type, haddr_t addr,
    size_t size, void *buf/*out*/)
{
    herr_t      ret_value = SUCCEED;    /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    HDassert(f);
    HDassert(f->shared);
    HDassert(buf);

    /* Check if this information is in the metadata accumulator */
    if((f->shared->feature_flags & H5FD_FEAT_ACCUMULATE_METADATA) && type != H5FD_MEM_DRAW) {
        if(size < H5F_ACCUM_MAX_SIZE) {
            /* Sanity check */
            HDassert(!f->shared->accum.buf || (f->shared->accum.alloc_size >= f->shared->accum.size));

            /* Current read adjoins or overlaps with metadata accumulator */
            if(H5F_addr_overlap(addr, size, f->shared->accum.loc, f->shared->accum.size)
                    || ((addr + size) == f->shared->accum.loc)
                    || (f->shared->accum.loc + f->shared->accum.size) == addr) {
                size_t amount_before;       /* Amount to read before current accumulator */
                haddr_t new_addr;           /* New address of the accumulator buffer */
                size_t new_size;            /* New size of the accumulator buffer */

                /* Compute new values for accumulator */
                new_addr = MIN(addr, f->shared->accum.loc);
                new_size = (size_t)(MAX((addr + size), (f->shared->accum.loc + f->shared->accum.size))
                        - new_addr);

                /* Check if we need more buffer space */
                if(new_size > f->shared->accum.alloc_size) {
                    size_t new_alloc_size;        /* New size of accumulator */

                    /* Adjust the buffer size to be a power of 2 that is large enough to hold data */
                    new_alloc_size = (size_t)1 << (1 + H5V_log2_gen((uint64_t)(new_size - 1)));

                    /* Reallocate the metadata accumulator buffer */
                    if(NULL == (f->shared->accum.buf = H5FL_BLK_REALLOC(meta_accum, f->shared->accum.buf, new_alloc_size)))
                        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate metadata accumulator buffer")

                    /* Note the new buffer size */
                    f->shared->accum.alloc_size = new_alloc_size;
#ifdef H5_CLEAR_MEMORY
    HDmemset(f->shared->accum.buf + f->shared->accum.size, 0, (f->shared->accum.alloc_size - f->shared->accum.size));
#endif /* H5_CLEAR_MEMORY */
                } /* end if */

                /* Read the part before the metadata accumulator */
                if(addr < f->shared->accum.loc) {
                    /* Set the amount to read */
                    H5_ASSIGN_OVERFLOW(amount_before, (f->shared->accum.loc - addr), hsize_t, size_t);

                    /* Make room for the metadata to read in */
                    HDmemmove(f->shared->accum.buf + amount_before, f->shared->accum.buf, f->shared->accum.size);

                    /* Adjust dirty region tracking info, if present */
                    if(f->shared->accum.dirty)
                        f->shared->accum.dirty_off += amount_before;

                    /* Dispatch to driver */
                    if(H5FD_read(f->shared->lf, dxpl_id, type, addr, amount_before, f->shared->accum.buf) < 0)
                        HGOTO_ERROR(H5E_IO, H5E_READERROR, FAIL, "driver read request failed")
                } /* end if */
                else