Coverage Report

Created: 2025-10-10 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hdf5/src/H5HFiblock.c
Line
Count
Source
1
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
 * Copyright by The HDF Group.                                               *
3
 * All rights reserved.                                                      *
4
 *                                                                           *
5
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
6
 * terms governing use, modification, and redistribution, is contained in    *
7
 * the LICENSE file, which can be found at the root of the source code       *
8
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
9
 * If you do not have access to either file, you may request a copy from     *
10
 * help@hdfgroup.org.                                                        *
11
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12
13
/*-------------------------------------------------------------------------
14
 *
15
 * Created:   H5HFiblock.c
16
 *
17
 * Purpose:   Indirect block routines for fractal heaps.
18
 *
19
 *-------------------------------------------------------------------------
20
 */
21
22
/****************/
23
/* Module Setup */
24
/****************/
25
26
#include "H5HFmodule.h" /* This source code file is part of the H5HF module */
27
28
/***********/
29
/* Headers */
30
/***********/
31
#include "H5private.h"   /* Generic Functions     */
32
#include "H5Eprivate.h"  /* Error handling        */
33
#include "H5Fprivate.h"  /* File access       */
34
#include "H5FLprivate.h" /* Free Lists                               */
35
#include "H5HFpkg.h"     /* Fractal heaps     */
36
#include "H5MFprivate.h" /* File memory management    */
37
#include "H5VMprivate.h" /* Vectors and arrays      */
38
39
/****************/
40
/* Local Macros */
41
/****************/
42
43
/******************/
44
/* Local Typedefs */
45
/******************/
46
47
/********************/
48
/* Package Typedefs */
49
/********************/
50
51
/********************/
52
/* Local Prototypes */
53
/********************/
54
static herr_t H5HF__iblock_pin(H5HF_indirect_t *iblock);
55
static herr_t H5HF__iblock_unpin(H5HF_indirect_t *iblock);
56
static herr_t H5HF__man_iblock_root_halve(H5HF_indirect_t *root_iblock);
57
static herr_t H5HF__man_iblock_root_revert(H5HF_indirect_t *root_iblock);
58
59
/*********************/
60
/* Package Variables */
61
/*********************/
62
63
/* Declare a free list to manage the H5HF_indirect_t struct */
64
H5FL_DEFINE(H5HF_indirect_t);
65
66
/* Declare a free list to manage the H5HF_indirect_ent_t sequence information */
67
H5FL_SEQ_DEFINE(H5HF_indirect_ent_t);
68
69
/* Declare a free list to manage the H5HF_indirect_filt_ent_t sequence information */
70
H5FL_SEQ_DEFINE(H5HF_indirect_filt_ent_t);
71
72
/* Declare a free list to manage the H5HF_indirect_t * sequence information */
73
H5FL_SEQ_DEFINE(H5HF_indirect_ptr_t);
74
75
/*****************************/
76
/* Library Private Variables */
77
/*****************************/
78
79
/*******************/
80
/* Local Variables */
81
/*******************/
82
83
/*-------------------------------------------------------------------------
84
 * Function:  H5HF__iblock_pin
85
 *
86
 * Purpose: Pin an indirect block in memory
87
 *
88
 * Return:  Non-negative on success/Negative on failure
89
 *
90
 *-------------------------------------------------------------------------
91
 */
92
static herr_t
93
H5HF__iblock_pin(H5HF_indirect_t *iblock)
94
0
{
95
0
    herr_t ret_value = SUCCEED; /* Return value */
96
97
0
    FUNC_ENTER_PACKAGE
98
99
    /* Sanity checks */
100
0
    assert(iblock);
101
102
    /* Mark block as un-evictable */
103
0
    if (H5AC_pin_protected_entry(iblock) < 0)
104
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPIN, FAIL, "unable to pin fractal heap indirect block");
105
106
    /* If this indirect block has a parent, update it's child iblock pointer */
107
0
    if (iblock->parent) {
108
0
        H5HF_indirect_t *par_iblock = iblock->parent; /* Parent indirect block */
109
0
        unsigned         indir_idx;                   /* Index in parent's child iblock pointer array */
110
111
        /* Sanity check */
112
0
        assert(par_iblock->child_iblocks);
113
0
        assert(iblock->par_entry >=
114
0
               (iblock->hdr->man_dtable.max_direct_rows * iblock->hdr->man_dtable.cparam.width));
115
116
        /* Compute index in parent's child iblock pointer array */
117
0
        indir_idx = iblock->par_entry -
118
0
                    (iblock->hdr->man_dtable.max_direct_rows * iblock->hdr->man_dtable.cparam.width);
119
120
        /* Set pointer to pinned indirect block in parent */
121
0
        assert(par_iblock->child_iblocks[indir_idx] == NULL);
122
0
        par_iblock->child_iblocks[indir_idx] = iblock;
123
0
    } /* end if */
124
0
    else {
125
        /* Check for pinning the root indirect block */
126
0
        if (iblock->block_off == 0) {
127
            /* Sanity check - shouldn't be recursively pinning root indirect block */
128
0
            assert(0 == (iblock->hdr->root_iblock_flags & H5HF_ROOT_IBLOCK_PINNED));
129
130
            /* Check if we should set the root iblock pointer */
131
0
            if (0 == iblock->hdr->root_iblock_flags) {
132
0
                assert(NULL == iblock->hdr->root_iblock);
133
0
                iblock->hdr->root_iblock = iblock;
134
0
            } /* end if */
135
136
            /* Indicate that the root indirect block is pinned */
137
0
            iblock->hdr->root_iblock_flags |= H5HF_ROOT_IBLOCK_PINNED;
138
0
        } /* end if */
139
0
    }     /* end if */
140
141
0
done:
142
0
    FUNC_LEAVE_NOAPI(ret_value)
143
0
} /* end H5HF__iblock_pin() */
144
145
/*-------------------------------------------------------------------------
146
 * Function:  H5HF__iblock_unpin
147
 *
148
 * Purpose: Unpin an indirect block in the metadata cache
149
 *
150
 * Return:  Non-negative on success/Negative on failure
151
 *
152
 *-------------------------------------------------------------------------
153
 */
154
static herr_t
155
H5HF__iblock_unpin(H5HF_indirect_t *iblock)
156
0
{
157
0
    herr_t ret_value = SUCCEED; /* Return value */
158
159
0
    FUNC_ENTER_PACKAGE
160
161
    /* Sanity check */
162
0
    assert(iblock);
163
164
    /* Mark block as evictable again */
165
0
    if (H5AC_unpin_entry(iblock) < 0)
166
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPIN, FAIL, "unable to unpin fractal heap indirect block");
167
168
0
done:
169
0
    FUNC_LEAVE_NOAPI(ret_value)
170
0
} /* end H5HF__iblock_unpin() */
171
172
/*-------------------------------------------------------------------------
173
 * Function:  H5HF__iblock_incr
174
 *
175
 * Purpose: Increment reference count on shared indirect block
176
 *
177
 * Return:  Non-negative on success/Negative on failure
178
 *
179
 *-------------------------------------------------------------------------
180
 */
181
herr_t
182
H5HF__iblock_incr(H5HF_indirect_t *iblock)
183
0
{
184
0
    herr_t ret_value = SUCCEED; /* Return value */
185
186
0
    FUNC_ENTER_PACKAGE
187
188
    /* Sanity checks */
189
0
    assert(iblock);
190
0
    assert(iblock->block_off == 0 || iblock->parent);
191
192
    /* Mark block as un-evictable when a child block is depending on it */
193
0
    if (iblock->rc == 0)
194
0
        if (H5HF__iblock_pin(iblock) < 0)
195
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTPIN, FAIL, "unable to pin fractal heap indirect block");
196
197
    /* Increment reference count on shared indirect block */
198
0
    iblock->rc++;
199
200
0
done:
201
0
    FUNC_LEAVE_NOAPI(ret_value)
202
0
} /* end H5HF__iblock_incr() */
203
204
/*-------------------------------------------------------------------------
205
 * Function:  H5HF__iblock_decr
206
 *
207
 * Purpose: Decrement reference count on shared indirect block
208
 *
209
 * Return:  Non-negative on success/Negative on failure
210
 *
211
 *-------------------------------------------------------------------------
212
 */
213
herr_t
214
H5HF__iblock_decr(H5HF_indirect_t *iblock)
215
0
{
216
0
    herr_t ret_value = SUCCEED; /* Return value */
217
218
0
    FUNC_ENTER_PACKAGE
219
220
    /* Sanity check */
221
0
    assert(iblock);
222
223
    /* Decrement reference count on shared indirect block */
224
0
    iblock->rc--;
225
226
    /* Check for last reference to block */
227
0
    if (iblock->rc == 0) {
228
229
        /* If this indirect block has a parent, reset it's child iblock pointer */
230
0
        if (iblock->parent) {
231
0
            H5HF_indirect_t *par_iblock = iblock->parent; /* Parent indirect block */
232
0
            unsigned         indir_idx;                   /* Index in parent's child iblock pointer array */
233
234
            /* Sanity check */
235
0
            assert(par_iblock->child_iblocks);
236
0
            assert(iblock->par_entry >=
237
0
                   (iblock->hdr->man_dtable.max_direct_rows * iblock->hdr->man_dtable.cparam.width));
238
239
            /* Compute index in parent's child iblock pointer array */
240
0
            indir_idx = iblock->par_entry -
241
0
                        (iblock->hdr->man_dtable.max_direct_rows * iblock->hdr->man_dtable.cparam.width);
242
243
            /* Reset pointer to pinned child indirect block in parent */
244
0
            assert(par_iblock->child_iblocks[indir_idx]);
245
0
            par_iblock->child_iblocks[indir_idx] = NULL;
246
0
        } /* end if */
247
0
        else {
248
            /* Check for root indirect block */
249
0
            if (iblock->block_off == 0) {
250
                /* Sanity check - shouldn't be recursively unpinning root indirect block */
251
0
                assert(iblock->hdr->root_iblock_flags & H5HF_ROOT_IBLOCK_PINNED);
252
253
                /* Check if we should reset the root iblock pointer */
254
0
                if (H5HF_ROOT_IBLOCK_PINNED == iblock->hdr->root_iblock_flags) {
255
0
                    assert(NULL != iblock->hdr->root_iblock);
256
0
                    iblock->hdr->root_iblock = NULL;
257
0
                } /* end if */
258
259
                /* Indicate that the root indirect block is unpinned */
260
0
                iblock->hdr->root_iblock_flags &= (unsigned)(~(H5HF_ROOT_IBLOCK_PINNED));
261
0
            } /* end if */
262
0
        }     /* end else */
263
264
        /* Check if the block is still in the cache */
265
0
        if (!iblock->removed_from_cache) {
266
            /* Unpin the indirect block, making it evictable again */
267
0
            if (H5HF__iblock_unpin(iblock) < 0)
268
0
                HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPIN, FAIL, "unable to unpin fractal heap indirect block");
269
0
        } /* end if */
270
0
        else {
271
            /* Destroy the indirect block */
272
0
            if (H5HF__man_iblock_dest(iblock) < 0)
273
0
                HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "unable to destroy fractal heap indirect block");
274
0
        } /* end else */
275
0
    }     /* end if */
276
277
0
done:
278
0
    FUNC_LEAVE_NOAPI(ret_value)
279
0
} /* end H5HF__iblock_decr() */
280
281
/*-------------------------------------------------------------------------
282
 * Function:  H5HF__iblock_dirty
283
 *
284
 * Purpose: Mark indirect block as dirty
285
 *
286
 * Return:  Non-negative on success/Negative on failure
287
 *
288
 *-------------------------------------------------------------------------
289
 */
290
herr_t
291
H5HF__iblock_dirty(H5HF_indirect_t *iblock)
292
0
{
293
0
    herr_t ret_value = SUCCEED; /* Return value */
294
295
0
    FUNC_ENTER_PACKAGE
296
297
    /* Sanity check */
298
0
    assert(iblock);
299
300
    /* Mark indirect block as dirty in cache */
301
0
    if (H5AC_mark_entry_dirty(iblock) < 0)
302
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTMARKDIRTY, FAIL, "unable to mark fractal heap indirect block as dirty");
303
304
0
done:
305
0
    FUNC_LEAVE_NOAPI(ret_value)
306
0
} /* end H5HF__iblock_dirty() */
307
308
/*-------------------------------------------------------------------------
309
 * Function:  H5HF__man_iblock_root_create
310
 *
311
 * Purpose: Create root indirect block
312
 *
313
 * Return:  SUCCEED/FAIL
314
 *
315
 *-------------------------------------------------------------------------
316
 */
317
herr_t
318
H5HF__man_iblock_root_create(H5HF_hdr_t *hdr, size_t min_dblock_size)
319
0
{
320
0
    H5HF_indirect_t *iblock;              /* Pointer to indirect block */
321
0
    haddr_t          iblock_addr;         /* Indirect block's address */
322
0
    hsize_t          acc_dblock_free;     /* Accumulated free space in direct blocks */
323
0
    bool             have_direct_block;   /* Flag to indicate a direct block already exists */
324
0
    bool             did_protect;         /* Whether we protected the indirect block or not */
325
0
    unsigned         nrows;               /* Number of rows for root indirect block */
326
0
    unsigned         u;                   /* Local index variable */
327
0
    herr_t           ret_value = SUCCEED; /* Return value */
328
329
0
    FUNC_ENTER_PACKAGE
330
331
    /* Check for allocating entire root indirect block initially */
332
0
    if (hdr->man_dtable.cparam.start_root_rows == 0)
333
0
        nrows = hdr->man_dtable.max_root_rows;
334
0
    else {
335
0
        unsigned rows_needed;   /* Number of rows needed to get to direct block size */
336
0
        unsigned block_row_off; /* Row offset from larger block sizes */
337
338
0
        nrows = hdr->man_dtable.cparam.start_root_rows;
339
340
0
        block_row_off = H5VM_log2_of2((uint32_t)min_dblock_size) -
341
0
                        H5VM_log2_of2((uint32_t)hdr->man_dtable.cparam.start_block_size);
342
0
        if (block_row_off > 0)
343
0
            block_row_off++; /* Account for the pair of initial rows of the initial block size */
344
0
        rows_needed = 1 + block_row_off;
345
0
        if (nrows < rows_needed)
346
0
            nrows = rows_needed;
347
0
    } /* end else */
348
349
    /* Allocate root indirect block */
350
0
    if (H5HF__man_iblock_create(hdr, NULL, 0, nrows, hdr->man_dtable.max_root_rows, &iblock_addr) < 0)
351
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTALLOC, FAIL, "can't allocate fractal heap indirect block");
352
353
    /* Move current direct block (used as root) into new indirect block */
354
355
    /* Lock new indirect block */
356
0
    if (NULL == (iblock = H5HF__man_iblock_protect(hdr, iblock_addr, nrows, NULL, 0, false,
357
0
                                                   H5AC__NO_FLAGS_SET, &did_protect)))
358
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to protect fractal heap indirect block");
359
360
    /* Check if there's already a direct block as root) */
361
0
    have_direct_block = H5_addr_defined(hdr->man_dtable.table_addr);
362
0
    if (have_direct_block) {
363
0
        H5HF_direct_t *dblock; /* Pointer to direct block to query */
364
365
        /* Lock first (root) direct block */
366
0
        if (NULL == (dblock = H5HF__man_dblock_protect(hdr, hdr->man_dtable.table_addr,
367
0
                                                       hdr->man_dtable.cparam.start_block_size, NULL, 0,
368
0
                                                       H5AC__NO_FLAGS_SET)))
369
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to protect fractal heap direct block");
370
371
        /* Attach direct block to new root indirect block */
372
0
        dblock->parent    = iblock;
373
0
        dblock->par_entry = 0;
374
375
        /* Destroy flush dependency between direct block and header */
376
0
        if (H5AC_destroy_flush_dependency(dblock->fd_parent, dblock) < 0)
377
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency");
378
0
        dblock->fd_parent = NULL;
379
380
        /* Create flush dependency between direct block and new root indirect block */
381
0
        if (H5AC_create_flush_dependency(iblock, dblock) < 0)
382
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTDEPEND, FAIL, "unable to create flush dependency");
383
0
        dblock->fd_parent = iblock;
384
385
0
        if (H5HF__man_iblock_attach(iblock, 0, hdr->man_dtable.table_addr) < 0)
386
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTATTACH, FAIL,
387
0
                        "can't attach root direct block to parent indirect block");
388
389
        /* Check for I/O filters on this heap */
390
0
        if (hdr->filter_len > 0) {
391
            /* Set the pipeline filter information from the header */
392
0
            iblock->filt_ents[0].size        = hdr->pline_root_direct_size;
393
0
            iblock->filt_ents[0].filter_mask = hdr->pline_root_direct_filter_mask;
394
395
            /* Reset the header's pipeline information */
396
0
            hdr->pline_root_direct_size        = 0;
397
0
            hdr->pline_root_direct_filter_mask = 0;
398
0
        } /* end if */
399
400
        /* Scan free space sections to set any 'parent' pointers to new indirect block */
401
0
        if (H5HF__space_create_root(hdr, iblock) < 0)
402
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTSET, FAIL,
403
0
                        "can't set free space section info to new root indirect block");
404
405
        /* Unlock first (previously the root) direct block */
406
0
        if (H5AC_unprotect(hdr->f, H5AC_FHEAP_DBLOCK, hdr->man_dtable.table_addr, dblock,
407
0
                           H5AC__NO_FLAGS_SET) < 0)
408
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release fractal heap direct block");
409
0
        dblock = NULL;
410
0
    } /* end if */
411
412
    /* Start iterator at correct location */
413
0
    if (H5HF__hdr_start_iter(hdr, iblock,
414
0
                             (hsize_t)(have_direct_block ? hdr->man_dtable.cparam.start_block_size : 0),
415
0
                             have_direct_block) < 0)
416
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTINIT, FAIL, "can't initialize block iterator");
417
418
    /* Check for skipping over direct blocks, in order to get to large enough block */
419
0
    if (min_dblock_size > hdr->man_dtable.cparam.start_block_size)
420
        /* Add skipped blocks to heap's free space */
421
0
        if (H5HF__hdr_skip_blocks(hdr, iblock, have_direct_block,
422
0
                                  ((nrows - 1) * hdr->man_dtable.cparam.width) - have_direct_block) < 0)
423
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTDEC, FAIL, "can't add skipped blocks to heap's free space");
424
425
    /* Mark indirect block as modified */
426
0
    if (H5HF__iblock_dirty(iblock) < 0)
427
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTDIRTY, FAIL, "can't mark indirect block as dirty");
428
429
    /* Unprotect root indirect block (it's pinned by the iterator though) */
430
0
    if (H5HF__man_iblock_unprotect(iblock, H5AC__DIRTIED_FLAG, did_protect) < 0)
431
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release fractal heap indirect block");
432
0
    iblock = NULL;
433
434
    /* Point heap header at new indirect block */
435
0
    hdr->man_dtable.curr_root_rows = nrows;
436
0
    hdr->man_dtable.table_addr     = iblock_addr;
437
438
    /* Compute free space in direct blocks referenced from entries in root indirect block */
439
0
    acc_dblock_free = 0;
440
0
    for (u = 0; u < nrows; u++)
441
0
        acc_dblock_free += hdr->man_dtable.row_tot_dblock_free[u] * hdr->man_dtable.cparam.width;
442
443
    /* Account for potential initial direct block */
444
0
    if (have_direct_block)
445
0
        acc_dblock_free -= hdr->man_dtable.row_tot_dblock_free[0];
446
447
    /* Extend heap to cover new root indirect block */
448
0
    if (H5HF__hdr_adjust_heap(hdr, hdr->man_dtable.row_block_off[nrows], (hssize_t)acc_dblock_free) < 0)
449
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTEXTEND, FAIL, "can't increase space to cover root direct block");
450
451
0
done:
452
0
    FUNC_LEAVE_NOAPI(ret_value)
453
0
} /* end H5HF__man_iblock_root_create() */
454
455
/*-------------------------------------------------------------------------
456
 * Function:  H5HF__man_iblock_root_double
457
 *
458
 * Purpose: Double size of root indirect block
459
 *
460
 * Return:  SUCCEED/FAIL
461
 *
462
 *-------------------------------------------------------------------------
463
 */
464
herr_t
465
H5HF__man_iblock_root_double(H5HF_hdr_t *hdr, size_t min_dblock_size)
466
0
{
467
0
    H5HF_indirect_t *iblock;          /* Pointer to root indirect block */
468
0
    haddr_t          new_addr;        /* New address of indirect block */
469
0
    hsize_t          acc_dblock_free; /* Accumulated free space in direct blocks */
470
0
    hsize_t          next_size;       /* The previous value of the "next size" for the new block iterator */
471
0
    hsize_t          old_iblock_size; /* Old size of indirect block */
472
0
    unsigned         next_row;        /* The next row to allocate block in */
473
0
    unsigned         next_entry;      /* The previous value of the "next entry" for the new block iterator */
474
0
    unsigned         new_next_entry = 0; /* The new value of the "next entry" for the new block iterator */
475
0
    unsigned         min_nrows      = 0; /* Min. # of direct rows */
476
0
    unsigned         old_nrows;          /* Old # of rows */
477
0
    unsigned         new_nrows;          /* New # of rows */
478
0
    bool             skip_direct_rows = false; /* Whether we are skipping direct rows */
479
0
    size_t           u;                        /* Local index variable */
480
0
    herr_t           ret_value = SUCCEED;      /* Return value */
481
482
0
    FUNC_ENTER_PACKAGE
483
484
    /* Get "new block" iterator information */
485
0
    if (H5HF__man_iter_curr(&hdr->next_block, &next_row, NULL, &next_entry, &iblock) < 0)
486
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "unable to retrieve current block iterator location");
487
0
    next_size = hdr->man_dtable.row_block_size[next_row];
488
489
    /* Make certain the iterator is at the root indirect block */
490
0
    assert(iblock->parent == NULL);
491
0
    assert(iblock->block_off == 0);
492
493
    /* Keep this for later */
494
0
    old_nrows = iblock->nrows;
495
496
    /* Check for skipping over direct block rows */
497
0
    if (iblock->nrows < hdr->man_dtable.max_direct_rows && min_dblock_size > next_size) {
498
        /* Sanity check */
499
0
        assert(min_dblock_size > hdr->man_dtable.cparam.start_block_size);
500
501
        /* Set flag */
502
0
        skip_direct_rows = true;
503
504
        /* Make certain we allocate at least the required row for the block requested */
505
0
        min_nrows = 1 + H5HF__dtable_size_to_row(&hdr->man_dtable, min_dblock_size);
506
507
        /* Set the information for the next block, of the appropriate size */
508
0
        new_next_entry = (min_nrows - 1) * hdr->man_dtable.cparam.width;
509
0
    } /* end if */
510
511
    /* Compute new # of rows in indirect block */
512
0
    new_nrows = MAX(min_nrows, MIN(2 * iblock->nrows, iblock->max_rows));
513
514
    /* Check if the indirect block is NOT currently allocated in temp. file space */
515
    /* (temp. file space does not need to be freed) */
516
0
    if (!H5F_IS_TMP_ADDR(hdr->f, iblock->addr))
517
        /* Free previous indirect block disk space */
518
0
        if (H5MF_xfree(hdr->f, H5FD_MEM_FHEAP_IBLOCK, iblock->addr, (hsize_t)iblock->size) < 0)
519
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL,
520
0
                        "unable to free fractal heap indirect block file space");
521
522
    /* Compute size of buffer needed for new indirect block */
523
0
    iblock->nrows   = new_nrows;
524
0
    old_iblock_size = iblock->size;
525
0
    iblock->size    = H5HF_MAN_INDIRECT_SIZE(hdr, iblock->nrows);
526
527
    /* Allocate [temporary] space for the new indirect block on disk */
528
0
    if (H5F_USE_TMP_SPACE(hdr->f)) {
529
0
        if (HADDR_UNDEF == (new_addr = H5MF_alloc_tmp(hdr->f, (hsize_t)iblock->size)))
530
0
            HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL,
531
0
                        "file allocation failed for fractal heap indirect block");
532
0
    } /* end if */
533
0
    else {
534
0
        if (HADDR_UNDEF == (new_addr = H5MF_alloc(hdr->f, H5FD_MEM_FHEAP_IBLOCK, (hsize_t)iblock->size)))
535
0
            HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL,
536
0
                        "file allocation failed for fractal heap indirect block");
537
0
    } /* end else */
538
539
    /* Resize pinned indirect block in the cache, if its changed size */
540
0
    if (old_iblock_size != iblock->size) {
541
0
        if (H5AC_resize_entry(iblock, (size_t)iblock->size) < 0)
542
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTRESIZE, FAIL, "unable to resize fractal heap indirect block");
543
0
    } /* end if */
544
545
    /* Move object in cache, if it actually was relocated */
546
0
    if (H5_addr_ne(iblock->addr, new_addr)) {
547
0
        if (H5AC_move_entry(hdr->f, H5AC_FHEAP_IBLOCK, iblock->addr, new_addr) < 0)
548
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTMOVE, FAIL, "unable to move fractal heap root indirect block");
549
0
        iblock->addr = new_addr;
550
0
    } /* end if */
551
552
    /* Re-allocate child block entry array */
553
0
    if (NULL == (iblock->ents = H5FL_SEQ_REALLOC(H5HF_indirect_ent_t, iblock->ents,
554
0
                                                 (size_t)(iblock->nrows * hdr->man_dtable.cparam.width))))
555
0
        HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL, "memory allocation failed for direct entries");
556
557
    /* Check for skipping over rows and add free section for skipped rows */
558
0
    if (skip_direct_rows)
559
        /* Add skipped blocks to heap's free space */
560
0
        if (H5HF__hdr_skip_blocks(hdr, iblock, next_entry, (new_next_entry - next_entry)) < 0)
561
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTDEC, FAIL, "can't add skipped blocks to heap's free space");
562
563
    /* Initialize new direct block entries in rows added */
564
0
    acc_dblock_free = 0;
565
0
    for (u = (old_nrows * hdr->man_dtable.cparam.width); u < (iblock->nrows * hdr->man_dtable.cparam.width);
566
0
         u++) {
567
0
        unsigned row = (unsigned)(u / hdr->man_dtable.cparam.width); /* Row for current entry */
568
569
0
        iblock->ents[u].addr = HADDR_UNDEF;
570
0
        acc_dblock_free += hdr->man_dtable.row_tot_dblock_free[row];
571
0
    } /* end for */
572
573
    /* Check for needing to re-allocate filtered entry array */
574
0
    if (hdr->filter_len > 0 && old_nrows < hdr->man_dtable.max_direct_rows) {
575
0
        unsigned dir_rows; /* Number of direct rows in this indirect block */
576
577
        /* Compute the number of direct rows for this indirect block */
578
0
        dir_rows = MIN(iblock->nrows, hdr->man_dtable.max_direct_rows);
579
0
        assert(dir_rows > old_nrows);
580
581
        /* Re-allocate filtered direct block entry array */
582
0
        if (NULL == (iblock->filt_ents = H5FL_SEQ_REALLOC(H5HF_indirect_filt_ent_t, iblock->filt_ents,
583
0
                                                          (size_t)(dir_rows * hdr->man_dtable.cparam.width))))
584
0
            HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL, "memory allocation failed for filtered direct entries");
585
586
        /* Initialize new entries allocated */
587
0
        for (u = (old_nrows * hdr->man_dtable.cparam.width); u < (dir_rows * hdr->man_dtable.cparam.width);
588
0
             u++) {
589
0
            iblock->filt_ents[u].size        = 0;
590
0
            iblock->filt_ents[u].filter_mask = 0;
591
0
        } /* end for */
592
0
    }     /* end if */
593
594
    /* Check for needing to re-allocate child iblock pointer array */
595
0
    if (iblock->nrows > hdr->man_dtable.max_direct_rows) {
596
0
        unsigned indir_rows;     /* Number of indirect rows in this indirect block */
597
0
        unsigned old_indir_rows; /* Previous number of indirect rows in this indirect block */
598
599
        /* Compute the number of direct rows for this indirect block */
600
0
        indir_rows = iblock->nrows - hdr->man_dtable.max_direct_rows;
601
602
        /* Re-allocate child indirect block array */
603
0
        if (NULL ==
604
0
            (iblock->child_iblocks = H5FL_SEQ_REALLOC(H5HF_indirect_ptr_t, iblock->child_iblocks,
605
0
                                                      (size_t)(indir_rows * hdr->man_dtable.cparam.width))))
606
0
            HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL, "memory allocation failed for filtered direct entries");
607
608
        /* Compute the previous # of indirect rows in this block */
609
0
        if (old_nrows < hdr->man_dtable.max_direct_rows)
610
0
            old_indir_rows = 0;
611
0
        else
612
0
            old_indir_rows = old_nrows - hdr->man_dtable.max_direct_rows;
613
614
        /* Initialize new entries allocated */
615
0
        for (u = (old_indir_rows * hdr->man_dtable.cparam.width);
616
0
             u < (indir_rows * hdr->man_dtable.cparam.width); u++)
617
0
            iblock->child_iblocks[u] = NULL;
618
0
    } /* end if */
619
620
    /* Mark indirect block as dirty */
621
0
    if (H5HF__iblock_dirty(iblock) < 0)
622
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTDIRTY, FAIL, "can't mark indirect block as dirty");
623
624
    /* Update other shared header info */
625
0
    hdr->man_dtable.curr_root_rows = new_nrows;
626
0
    hdr->man_dtable.table_addr     = new_addr;
627
628
    /* Extend heap to cover new root indirect block */
629
0
    if (H5HF__hdr_adjust_heap(hdr, 2 * hdr->man_dtable.row_block_off[new_nrows - 1],
630
0
                              (hssize_t)acc_dblock_free) < 0)
631
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTEXTEND, FAIL, "can't increase space to cover root direct block");
632
633
0
done:
634
0
    FUNC_LEAVE_NOAPI(ret_value)
635
0
} /* end H5HF__man_iblock_root_double() */
636
637
/*-------------------------------------------------------------------------
638
 * Function:  H5HF__man_iblock_root_halve
639
 *
640
 * Purpose: Halve size of root indirect block
641
 *
642
 * Return:  SUCCEED/FAIL
643
 *
644
 *-------------------------------------------------------------------------
645
 */
646
static herr_t
647
H5HF__man_iblock_root_halve(H5HF_indirect_t *iblock)
648
0
{
649
0
    H5HF_hdr_t *hdr = iblock->hdr;   /* Pointer to heap header */
650
0
    haddr_t     new_addr;            /* New address of indirect block */
651
0
    hsize_t     acc_dblock_free;     /* Accumulated free space in direct blocks */
652
0
    hsize_t     old_size;            /* Old size of indirect block */
653
0
    unsigned    max_child_row;       /* Row for max. child entry */
654
0
    unsigned    old_nrows;           /* Old # of rows */
655
0
    unsigned    new_nrows;           /* New # of rows */
656
0
    unsigned    u;                   /* Local index variable */
657
0
    herr_t      ret_value = SUCCEED; /* Return value */
658
659
0
    FUNC_ENTER_PACKAGE
660
661
    /* Sanity check */
662
0
    assert(iblock);
663
0
    assert(iblock->parent == NULL);
664
0
    assert(hdr);
665
666
    /* Compute maximum row used by child of indirect block */
667
0
    max_child_row = iblock->max_child / hdr->man_dtable.cparam.width;
668
669
    /* Compute new # of rows in root indirect block */
670
0
    new_nrows = (unsigned)1 << (1 + H5VM_log2_gen((uint64_t)max_child_row));
671
672
    /* Check if the indirect block is NOT currently allocated in temp. file space */
673
    /* (temp. file space does not need to be freed) */
674
0
    if (!H5F_IS_TMP_ADDR(hdr->f, iblock->addr))
675
        /* Free previous indirect block disk space */
676
0
        if (H5MF_xfree(hdr->f, H5FD_MEM_FHEAP_IBLOCK, iblock->addr, (hsize_t)iblock->size) < 0)
677
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL,
678
0
                        "unable to free fractal heap indirect block file space");
679
680
    /* Compute free space in rows to delete */
681
0
    acc_dblock_free = 0;
682
0
    for (u = new_nrows; u < iblock->nrows; u++)
683
0
        acc_dblock_free += hdr->man_dtable.row_tot_dblock_free[u] * hdr->man_dtable.cparam.width;
684
685
    /* Compute size of buffer needed for new indirect block */
686
0
    old_nrows     = iblock->nrows;
687
0
    iblock->nrows = new_nrows;
688
0
    old_size      = iblock->size;
689
0
    iblock->size  = H5HF_MAN_INDIRECT_SIZE(hdr, iblock->nrows);
690
691
    /* Allocate [temporary] space for the new indirect block on disk */
692
0
    if (H5F_USE_TMP_SPACE(hdr->f)) {
693
0
        if (HADDR_UNDEF == (new_addr = H5MF_alloc_tmp(hdr->f, (hsize_t)iblock->size)))
694
0
            HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL,
695
0
                        "file allocation failed for fractal heap indirect block");
696
0
    } /* end if */
697
0
    else {
698
0
        if (HADDR_UNDEF == (new_addr = H5MF_alloc(hdr->f, H5FD_MEM_FHEAP_IBLOCK, (hsize_t)iblock->size)))
699
0
            HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL,
700
0
                        "file allocation failed for fractal heap indirect block");
701
0
    } /* end else */
702
703
    /* Resize pinned indirect block in the cache, if it has changed size */
704
0
    if (old_size != iblock->size) {
705
0
        if (H5AC_resize_entry(iblock, (size_t)iblock->size) < 0)
706
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTRESIZE, FAIL, "unable to resize fractal heap indirect block");
707
0
    } /* end if */
708
709
    /* Move object in cache, if it actually was relocated */
710
0
    if (H5_addr_ne(iblock->addr, new_addr)) {
711
0
        if (H5AC_move_entry(hdr->f, H5AC_FHEAP_IBLOCK, iblock->addr, new_addr) < 0)
712
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTSPLIT, FAIL, "unable to move fractal heap root indirect block");
713
0
        iblock->addr = new_addr;
714
0
    } /* end if */
715
716
    /* Re-allocate child block entry array */
717
0
    if (NULL == (iblock->ents = H5FL_SEQ_REALLOC(H5HF_indirect_ent_t, iblock->ents,
718
0
                                                 (size_t)(iblock->nrows * hdr->man_dtable.cparam.width))))
719
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for direct entries");
720
721
    /* Check for needing to re-allocate filtered entry array */
722
0
    if (hdr->filter_len > 0 && new_nrows < hdr->man_dtable.max_direct_rows) {
723
        /* Re-allocate filtered direct block entry array */
724
0
        if (NULL ==
725
0
            (iblock->filt_ents = H5FL_SEQ_REALLOC(H5HF_indirect_filt_ent_t, iblock->filt_ents,
726
0
                                                  (size_t)(iblock->nrows * hdr->man_dtable.cparam.width))))
727
0
            HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL, "memory allocation failed for filtered direct entries");
728
0
    } /* end if */
729
730
    /* Check for needing to re-allocate child iblock pointer array */
731
0
    if (old_nrows > hdr->man_dtable.max_direct_rows) {
732
        /* Check for shrinking away child iblock pointer array */
733
0
        if (iblock->nrows > hdr->man_dtable.max_direct_rows) {
734
0
            unsigned indir_rows; /* Number of indirect rows in this indirect block */
735
736
            /* Compute the number of direct rows for this indirect block */
737
0
            indir_rows = iblock->nrows - hdr->man_dtable.max_direct_rows;
738
739
            /* Re-allocate child indirect block array */
740
0
            if (NULL == (iblock->child_iblocks =
741
0
                             H5FL_SEQ_REALLOC(H5HF_indirect_ptr_t, iblock->child_iblocks,
742
0
                                              (size_t)(indir_rows * hdr->man_dtable.cparam.width))))
743
0
                HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL,
744
0
                            "memory allocation failed for filtered direct entries");
745
0
        } /* end if */
746
0
        else
747
0
            iblock->child_iblocks =
748
0
                (H5HF_indirect_ptr_t *)H5FL_SEQ_FREE(H5HF_indirect_ptr_t, iblock->child_iblocks);
749
0
    } /* end if */
750
751
    /* Mark indirect block as dirty */
752
0
    if (H5HF__iblock_dirty(iblock) < 0)
753
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTDIRTY, FAIL, "can't mark indirect block as dirty");
754
755
    /* Update other shared header info */
756
0
    hdr->man_dtable.curr_root_rows = new_nrows;
757
0
    hdr->man_dtable.table_addr     = new_addr;
758
759
    /* Shrink heap to only cover new root indirect block */
760
0
    if (H5HF__hdr_adjust_heap(hdr, 2 * hdr->man_dtable.row_block_off[new_nrows - 1],
761
0
                              -(hssize_t)acc_dblock_free) < 0)
762
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTSHRINK, FAIL, "can't reduce space to cover root direct block");
763
764
0
done:
765
0
    FUNC_LEAVE_NOAPI(ret_value)
766
0
} /* end H5HF__man_iblock_root_halve() */
767
768
/*-------------------------------------------------------------------------
769
 * Function:  H5HF__man_iblock_root_revert
770
 *
771
 * Purpose: Revert root indirect block back to root direct block
772
 *
773
 * Note:  Any sections left pointing to the  old root indirect block
774
 *              will be cleaned up by the free space manager
775
 *
776
 * Return:  SUCCEED/FAIL
777
 *
778
 *-------------------------------------------------------------------------
779
 */
780
static herr_t
781
H5HF__man_iblock_root_revert(H5HF_indirect_t *root_iblock)
782
0
{
783
0
    H5HF_hdr_t    *hdr;                 /* Pointer to heap's header */
784
0
    H5HF_direct_t *dblock = NULL;       /* Pointer to new root indirect block */
785
0
    haddr_t        dblock_addr;         /* Direct block's address in the file */
786
0
    size_t         dblock_size;         /* Direct block's size */
787
0
    herr_t         ret_value = SUCCEED; /* Return value */
788
789
0
    FUNC_ENTER_PACKAGE
790
791
    /*
792
     * Check arguments.
793
     */
794
0
    assert(root_iblock);
795
796
    /* Set up local convenience variables */
797
0
    hdr         = root_iblock->hdr;
798
0
    dblock_addr = root_iblock->ents[0].addr;
799
0
    dblock_size = hdr->man_dtable.cparam.start_block_size;
800
801
    /* Get pointer to last direct block */
802
0
    if (NULL == (dblock = H5HF__man_dblock_protect(hdr, dblock_addr, dblock_size, root_iblock, 0,
803
0
                                                   H5AC__NO_FLAGS_SET)))
804
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to protect fractal heap direct block");
805
0
    assert(dblock->parent == root_iblock);
806
0
    assert(dblock->par_entry == 0);
807
808
    /* Check for I/O filters on this heap */
809
0
    if (hdr->filter_len > 0) {
810
        /* Set the header's pipeline information from the indirect block */
811
0
        hdr->pline_root_direct_size        = root_iblock->filt_ents[0].size;
812
0
        hdr->pline_root_direct_filter_mask = root_iblock->filt_ents[0].filter_mask;
813
0
    } /* end if */
814
815
    /* Destroy flush dependency between old root iblock and new root direct block */
816
0
    if (H5AC_destroy_flush_dependency(dblock->fd_parent, dblock) < 0)
817
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency");
818
0
    dblock->fd_parent = NULL;
819
820
    /* Detach direct block from parent */
821
0
    if (H5HF__man_iblock_detach(dblock->parent, 0) < 0)
822
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTATTACH, FAIL, "can't detach direct block from parent indirect block");
823
0
    dblock->parent    = NULL;
824
0
    dblock->par_entry = 0;
825
826
    /* Create flush dependency between header and new root direct block */
827
0
    if (H5AC_create_flush_dependency(hdr, dblock) < 0)
828
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTDEPEND, FAIL, "unable to create flush dependency");
829
0
    dblock->fd_parent = hdr;
830
831
    /* Point root at direct block */
832
0
    hdr->man_dtable.curr_root_rows = 0;
833
0
    hdr->man_dtable.table_addr     = dblock_addr;
834
835
    /* Reset 'next block' iterator */
836
0
    if (H5HF__hdr_reset_iter(hdr, (hsize_t)dblock_size) < 0)
837
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTRELEASE, FAIL, "can't reset block iterator");
838
839
    /* Extend heap to just cover first direct block */
840
0
    if (H5HF__hdr_adjust_heap(hdr, (hsize_t)hdr->man_dtable.cparam.start_block_size,
841
0
                              (hssize_t)hdr->man_dtable.row_tot_dblock_free[0]) < 0)
842
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTEXTEND, FAIL, "can't increase space to cover root direct block");
843
844
    /* Scan free space sections to reset any 'parent' pointers */
845
0
    if (H5HF__space_revert_root(hdr) < 0)
846
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTRESET, FAIL, "can't reset free space section info");
847
848
0
done:
849
0
    if (dblock && H5AC_unprotect(hdr->f, H5AC_FHEAP_DBLOCK, dblock_addr, dblock, H5AC__NO_FLAGS_SET) < 0)
850
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release fractal heap direct block");
851
852
0
    FUNC_LEAVE_NOAPI(ret_value)
853
0
} /* end H5HF__man_iblock_root_revert() */
854
855
/*-------------------------------------------------------------------------
856
 * Function:  H5HF__man_iblock_alloc_row
857
 *
858
 * Purpose: Allocate a "single" section for an object, out of a
859
 *              "row" section.
860
 *
861
 * Note:  Creates necessary direct & indirect blocks
862
 *
863
 * Return:  Non-negative on success/Negative on failure
864
 *
865
 *-------------------------------------------------------------------------
866
 */
867
herr_t
868
H5HF__man_iblock_alloc_row(H5HF_hdr_t *hdr, H5HF_free_section_t **sec_node)
869
0
{
870
0
    H5HF_indirect_t     *iblock       = NULL;      /* Pointer to indirect block */
871
0
    H5HF_free_section_t *old_sec_node = *sec_node; /* Pointer to old indirect section node */
872
0
    unsigned             dblock_entry;             /* Entry for direct block */
873
0
    bool                 iblock_held = false;      /* Flag to indicate that indirect block is held */
874
0
    herr_t               ret_value   = SUCCEED;    /* Return value */
875
876
0
    FUNC_ENTER_PACKAGE
877
878
    /*
879
     * Check arguments.
880
     */
881
0
    assert(hdr);
882
0
    assert(sec_node && old_sec_node);
883
0
    assert(old_sec_node->u.row.row < hdr->man_dtable.max_direct_rows);
884
885
    /* Check for serialized row section, or serialized / deleted indirect
886
     * section under it. */
887
0
    if (old_sec_node->sect_info.state == H5FS_SECT_SERIALIZED ||
888
0
        (H5FS_SECT_SERIALIZED == old_sec_node->u.row.under->sect_info.state) ||
889
0
        (true == old_sec_node->u.row.under->u.indirect.u.iblock->removed_from_cache))
890
        /* Revive row and / or indirect section */
891
0
        if (H5HF__sect_row_revive(hdr, old_sec_node) < 0)
892
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTREVIVE, FAIL, "can't revive indirect section");
893
894
    /* Get a pointer to the indirect block covering the section */
895
0
    if (NULL == (iblock = H5HF__sect_row_get_iblock(old_sec_node)))
896
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't retrieve indirect block for row section");
897
898
    /* Hold indirect block in memory, until direct block can point to it */
899
0
    if (H5HF__iblock_incr(iblock) < 0)
900
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTINC, FAIL, "can't increment reference count on shared indirect block");
901
0
    iblock_held = true;
902
903
    /* Reduce (& possibly re-add) 'row' section */
904
0
    if (H5HF__sect_row_reduce(hdr, old_sec_node, &dblock_entry) < 0)
905
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTSHRINK, FAIL, "can't reduce row section node");
906
907
    /* Create direct block & single section */
908
0
    if (H5HF__man_dblock_create(hdr, iblock, dblock_entry, NULL, sec_node) < 0)
909
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTALLOC, FAIL, "can't allocate fractal heap direct block");
910
911
0
done:
912
    /* Release hold on indirect block */
913
0
    if (iblock_held)
914
0
        if (H5HF__iblock_decr(iblock) < 0)
915
0
            HDONE_ERROR(H5E_HEAP, H5E_CANTDEC, FAIL,
916
0
                        "can't decrement reference count on shared indirect block")
917
918
0
    FUNC_LEAVE_NOAPI(ret_value)
919
0
} /* end H5HF__man_iblock_alloc_row() */
920
921
/*-------------------------------------------------------------------------
922
 * Function:  H5HF__man_iblock_create
923
 *
924
 * Purpose: Allocate & initialize a managed indirect block
925
 *
926
 * Return:  SUCCEED/FAIL
927
 *
928
 *-------------------------------------------------------------------------
929
 */
930
herr_t
931
H5HF__man_iblock_create(H5HF_hdr_t *hdr, H5HF_indirect_t *par_iblock, unsigned par_entry, unsigned nrows,
932
                        unsigned max_rows, haddr_t *addr_p)
933
0
{
934
0
    H5HF_indirect_t *iblock = NULL;       /* Pointer to indirect block */
935
0
    size_t           u;                   /* Local index variable */
936
0
    herr_t           ret_value = SUCCEED; /* Return value */
937
938
0
    FUNC_ENTER_PACKAGE
939
940
    /*
941
     * Check arguments.
942
     */
943
0
    assert(hdr);
944
0
    assert(nrows > 0);
945
0
    assert(addr_p);
946
947
    /*
948
     * Allocate file and memory data structures.
949
     */
950
0
    if (NULL == (iblock = H5FL_MALLOC(H5HF_indirect_t)))
951
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
952
0
                    "memory allocation failed for fractal heap indirect block");
953
954
    /* Reset the metadata cache info for the heap header */
955
0
    memset(&iblock->cache_info, 0, sizeof(H5AC_info_t));
956
957
    /* Share common heap information */
958
0
    iblock->hdr = hdr;
959
0
    if (H5HF__hdr_incr(hdr) < 0)
960
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTINC, FAIL, "can't increment reference count on shared heap header");
961
962
    /* Set info for indirect block */
963
0
    iblock->rc                 = 0;
964
0
    iblock->nrows              = nrows;
965
0
    iblock->max_rows           = max_rows;
966
0
    iblock->removed_from_cache = false;
967
968
    /* Compute size of buffer needed for indirect block */
969
0
    iblock->size = H5HF_MAN_INDIRECT_SIZE(hdr, iblock->nrows);
970
971
    /* Allocate child block entry array */
972
0
    if (NULL == (iblock->ents = H5FL_SEQ_MALLOC(H5HF_indirect_ent_t,
973
0
                                                (size_t)(iblock->nrows * hdr->man_dtable.cparam.width))))
974
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for block entries");
975
976
    /* Initialize indirect block entry tables */
977
0
    for (u = 0; u < (iblock->nrows * hdr->man_dtable.cparam.width); u++)
978
0
        iblock->ents[u].addr = HADDR_UNDEF;
979
980
    /* Check for I/O filters to apply to this heap */
981
0
    if (hdr->filter_len > 0) {
982
0
        unsigned dir_rows; /* Number of direct rows in this indirect block */
983
984
        /* Compute the number of direct rows for this indirect block */
985
0
        dir_rows = MIN(iblock->nrows, hdr->man_dtable.max_direct_rows);
986
987
        /* Allocate & initialize indirect block filtered entry array */
988
0
        if (NULL == (iblock->filt_ents = H5FL_SEQ_CALLOC(H5HF_indirect_filt_ent_t,
989
0
                                                         (size_t)(dir_rows * hdr->man_dtable.cparam.width))))
990
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for block entries");
991
0
    } /* end if */
992
0
    else
993
0
        iblock->filt_ents = NULL;
994
995
    /* Check if we have any indirect block children */
996
0
    if (iblock->nrows > hdr->man_dtable.max_direct_rows) {
997
0
        unsigned indir_rows; /* Number of indirect rows in this indirect block */
998
999
        /* Compute the number of indirect rows for this indirect block */
1000
0
        indir_rows = iblock->nrows - hdr->man_dtable.max_direct_rows;
1001
1002
        /* Allocate & initialize child indirect block pointer array */
1003
0
        if (NULL == (iblock->child_iblocks = H5FL_SEQ_CALLOC(
1004
0
                         H5HF_indirect_ptr_t, (size_t)(indir_rows * hdr->man_dtable.cparam.width))))
1005
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for block entries");
1006
0
    } /* end if */
1007
0
    else
1008
0
        iblock->child_iblocks = NULL;
1009
1010
    /* Allocate [temporary] space for the indirect block on disk */
1011
0
    if (H5F_USE_TMP_SPACE(hdr->f)) {
1012
0
        if (HADDR_UNDEF == (*addr_p = H5MF_alloc_tmp(hdr->f, (hsize_t)iblock->size)))
1013
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
1014
0
                        "file allocation failed for fractal heap indirect block");
1015
0
    } /* end if */
1016
0
    else {
1017
0
        if (HADDR_UNDEF == (*addr_p = H5MF_alloc(hdr->f, H5FD_MEM_FHEAP_IBLOCK, (hsize_t)iblock->size)))
1018
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
1019
0
                        "file allocation failed for fractal heap indirect block");
1020
0
    } /* end else */
1021
0
    iblock->addr = *addr_p;
1022
1023
    /* Attach to parent indirect block, if there is one */
1024
0
    iblock->parent    = par_iblock;
1025
0
    iblock->par_entry = par_entry;
1026
0
    if (iblock->parent) {
1027
        /* Attach new block to parent */
1028
0
        if (H5HF__man_iblock_attach(iblock->parent, par_entry, *addr_p) < 0)
1029
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTATTACH, FAIL,
1030
0
                        "can't attach indirect block to parent indirect block");
1031
1032
        /* Compute the indirect block's offset in the heap's address space */
1033
        /* (based on parent's block offset) */
1034
0
        iblock->block_off = par_iblock->block_off;
1035
0
        iblock->block_off += hdr->man_dtable.row_block_off[par_entry / hdr->man_dtable.cparam.width];
1036
0
        iblock->block_off += hdr->man_dtable.row_block_size[par_entry / hdr->man_dtable.cparam.width] *
1037
0
                             (par_entry % hdr->man_dtable.cparam.width);
1038
1039
        /* Set indirect block parent as flush dependency parent */
1040
0
        iblock->fd_parent = par_iblock;
1041
0
    } /* end if */
1042
0
    else {
1043
0
        iblock->block_off = 0; /* Must be the root indirect block... */
1044
1045
        /* Set heap header as flush dependency parent */
1046
0
        iblock->fd_parent = hdr;
1047
0
    } /* end else */
1048
1049
    /* Update indirect block's statistics */
1050
0
    iblock->nchildren = 0;
1051
0
    iblock->max_child = 0;
1052
1053
    /* Cache the new indirect block */
1054
0
    if (H5AC_insert_entry(hdr->f, H5AC_FHEAP_IBLOCK, *addr_p, iblock, H5AC__NO_FLAGS_SET) < 0)
1055
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTINIT, FAIL, "can't add fractal heap indirect block to cache");
1056
1057
0
done:
1058
0
    if (ret_value < 0)
1059
0
        if (iblock)
1060
0
            if (H5HF__man_iblock_dest(iblock) < 0)
1061
0
                HDONE_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "unable to destroy fractal heap indirect block");
1062
1063
0
    FUNC_LEAVE_NOAPI(ret_value)
1064
0
} /* end H5HF__man_iblock_create() */
1065
1066
/*-------------------------------------------------------------------------
1067
 * Function:  H5HF__man_iblock_protect
1068
 *
1069
 * Purpose: Convenience wrapper around H5AC_protect on an indirect block
1070
 *
1071
 * Return:  Pointer to indirect block on success, NULL on failure
1072
 *
1073
 *-------------------------------------------------------------------------
1074
 */
1075
H5HF_indirect_t *
1076
H5HF__man_iblock_protect(H5HF_hdr_t *hdr, haddr_t iblock_addr, unsigned iblock_nrows,
1077
                         H5HF_indirect_t *par_iblock, unsigned par_entry, bool must_protect, unsigned flags,
1078
                         bool *did_protect)
1079
0
{
1080
0
    H5HF_parent_t    par_info;               /* Parent info for loading block */
1081
0
    H5HF_indirect_t *iblock         = NULL;  /* Indirect block from cache */
1082
0
    bool             should_protect = false; /* Whether we should protect the indirect block or not */
1083
0
    H5HF_indirect_t *ret_value      = NULL;  /* Return value */
1084
1085
0
    FUNC_ENTER_PACKAGE
1086
1087
    /*
1088
     * Check arguments.
1089
     */
1090
0
    assert(hdr);
1091
0
    assert(H5_addr_defined(iblock_addr));
1092
0
    assert(iblock_nrows > 0);
1093
0
    assert(did_protect);
1094
1095
    /* only H5AC__READ_ONLY_FLAG may appear in flags */
1096
0
    assert((flags & (unsigned)(~H5AC__READ_ONLY_FLAG)) == 0);
1097
1098
    /* Check if we are allowed to use existing pinned iblock pointer */
1099
0
    if (!must_protect) {
1100
        /* Check for this block already being pinned */
1101
0
        if (par_iblock) {
1102
0
            unsigned indir_idx; /* Index in parent's child iblock pointer array */
1103
1104
            /* Sanity check */
1105
0
            assert(par_iblock->child_iblocks);
1106
0
            assert(par_entry >= (hdr->man_dtable.max_direct_rows * hdr->man_dtable.cparam.width));
1107
1108
            /* Compute index in parent's child iblock pointer array */
1109
0
            indir_idx = par_entry - (hdr->man_dtable.max_direct_rows * hdr->man_dtable.cparam.width);
1110
1111
            /* Check for pointer to pinned indirect block in parent */
1112
0
            if (par_iblock->child_iblocks[indir_idx])
1113
0
                iblock = par_iblock->child_iblocks[indir_idx];
1114
0
            else
1115
0
                should_protect = true;
1116
0
        } /* end if */
1117
0
        else {
1118
            /* Check for root indirect block */
1119
0
            if (H5_addr_eq(iblock_addr, hdr->man_dtable.table_addr)) {
1120
                /* Check for valid pointer to pinned indirect block in root */
1121
0
                if (H5HF_ROOT_IBLOCK_PINNED == hdr->root_iblock_flags) {
1122
                    /* Sanity check */
1123
0
                    assert(NULL != hdr->root_iblock);
1124
1125
                    /* Return the pointer to the pinned root indirect block */
1126
0
                    iblock = hdr->root_iblock;
1127
0
                } /* end if */
1128
0
                else {
1129
                    /* Sanity check */
1130
0
                    assert(NULL == hdr->root_iblock);
1131
1132
0
                    should_protect = true;
1133
0
                } /* end else */
1134
0
            }     /* end if */
1135
0
            else
1136
0
                should_protect = true;
1137
0
        } /* end else */
1138
0
    }     /* end if */
1139
1140
    /* Check for protecting indirect block */
1141
0
    if (must_protect || should_protect) {
1142
0
        H5HF_iblock_cache_ud_t cache_udata; /* User-data for callback */
1143
1144
        /* Set up parent info */
1145
0
        par_info.hdr    = hdr;
1146
0
        par_info.iblock = par_iblock;
1147
0
        par_info.entry  = par_entry;
1148
1149
        /* Set up user data for protect call */
1150
0
        cache_udata.f        = hdr->f;
1151
0
        cache_udata.par_info = &par_info;
1152
0
        cache_udata.nrows    = &iblock_nrows;
1153
1154
        /* Protect the indirect block */
1155
0
        if (NULL == (iblock = (H5HF_indirect_t *)H5AC_protect(hdr->f, H5AC_FHEAP_IBLOCK, iblock_addr,
1156
0
                                                              &cache_udata, flags)))
1157
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, NULL, "unable to protect fractal heap indirect block");
1158
1159
        /* Set the indirect block's address */
1160
0
        iblock->addr = iblock_addr;
1161
1162
        /* Check for root indirect block */
1163
0
        if (iblock->block_off == 0) {
1164
            /* Sanity check - shouldn't be recursively protecting root indirect block */
1165
0
            assert(0 == (hdr->root_iblock_flags & H5HF_ROOT_IBLOCK_PROTECTED));
1166
1167
            /* Check if we should set the root iblock pointer */
1168
0
            if (0 == hdr->root_iblock_flags) {
1169
0
                assert(NULL == hdr->root_iblock);
1170
0
                hdr->root_iblock = iblock;
1171
0
            } /* end if */
1172
1173
            /* Indicate that the root indirect block is protected */
1174
0
            hdr->root_iblock_flags |= H5HF_ROOT_IBLOCK_PROTECTED;
1175
0
        } /* end if */
1176
1177
        /* Indicate that the indirect block was protected */
1178
0
        *did_protect = true;
1179
0
    } /* end if */
1180
0
    else
1181
        /* Indicate that the indirect block was _not_ protected */
1182
0
        *did_protect = false;
1183
1184
    /* Set the return value */
1185
0
    ret_value = iblock;
1186
1187
0
done:
1188
0
    FUNC_LEAVE_NOAPI(ret_value)
1189
0
} /* end H5HF__man_iblock_protect() */
1190
1191
/*-------------------------------------------------------------------------
1192
 * Function:  H5HF__man_iblock_unprotect
1193
 *
1194
 * Purpose: Convenience wrapper around H5AC_unprotect on an indirect block
1195
 *
1196
 * Return:  SUCCEED/FAIL
1197
 *
1198
 *-------------------------------------------------------------------------
1199
 */
1200
herr_t
1201
H5HF__man_iblock_unprotect(H5HF_indirect_t *iblock, unsigned cache_flags, bool did_protect)
1202
0
{
1203
0
    herr_t ret_value = SUCCEED; /* Return value */
1204
1205
0
    FUNC_ENTER_PACKAGE
1206
1207
    /*
1208
     * Check arguments.
1209
     */
1210
0
    assert(iblock);
1211
1212
    /* Check if we previously protected this indirect block */
1213
    /* (as opposed to using an existing pointer to a pinned child indirect block) */
1214
0
    if (did_protect) {
1215
        /* Check for root indirect block */
1216
0
        if (iblock->block_off == 0) {
1217
            /* Sanity check - shouldn't be recursively unprotecting root indirect block */
1218
0
            assert(iblock->hdr->root_iblock_flags & H5HF_ROOT_IBLOCK_PROTECTED);
1219
1220
            /* Check if we should reset the root iblock pointer */
1221
0
            if (H5HF_ROOT_IBLOCK_PROTECTED == iblock->hdr->root_iblock_flags) {
1222
0
                assert(NULL != iblock->hdr->root_iblock);
1223
0
                iblock->hdr->root_iblock = NULL;
1224
0
            } /* end if */
1225
1226
            /* Indicate that the root indirect block is unprotected */
1227
0
            iblock->hdr->root_iblock_flags &= (unsigned)(~(H5HF_ROOT_IBLOCK_PROTECTED));
1228
0
        } /* end if */
1229
1230
        /* Unprotect the indirect block */
1231
0
        if (H5AC_unprotect(iblock->hdr->f, H5AC_FHEAP_IBLOCK, iblock->addr, iblock, cache_flags) < 0)
1232
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release fractal heap indirect block");
1233
0
    } /* end if */
1234
1235
0
done:
1236
0
    FUNC_LEAVE_NOAPI(ret_value)
1237
0
} /* end H5HF__man_iblock_unprotect() */
1238
1239
/*-------------------------------------------------------------------------
1240
 * Function:  H5HF__man_iblock_attach
1241
 *
1242
 * Purpose: Attach a child block (direct or indirect) to an indirect block
1243
 *
1244
 * Return:  SUCCEED/FAIL
1245
 *
1246
 *-------------------------------------------------------------------------
1247
 */
1248
herr_t
1249
H5HF__man_iblock_attach(H5HF_indirect_t *iblock, unsigned entry, haddr_t child_addr)
1250
0
{
1251
0
    herr_t ret_value = SUCCEED; /* Return value */
1252
1253
0
    FUNC_ENTER_PACKAGE
1254
1255
    /*
1256
     * Check arguments.
1257
     */
1258
0
    assert(iblock);
1259
0
    assert(H5_addr_defined(child_addr));
1260
0
    assert(!H5_addr_defined(iblock->ents[entry].addr));
1261
1262
    /* Increment the reference count on this indirect block */
1263
0
    if (H5HF__iblock_incr(iblock) < 0)
1264
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTINC, FAIL, "can't increment reference count on shared indirect block");
1265
1266
    /* Point at the child block */
1267
0
    iblock->ents[entry].addr = child_addr;
1268
1269
    /* Check for I/O filters on this heap */
1270
0
    if (iblock->hdr->filter_len > 0) {
1271
0
        unsigned row; /* Row for entry */
1272
1273
        /* Sanity check */
1274
0
        assert(iblock->filt_ents);
1275
1276
        /* Compute row for entry */
1277
0
        row = entry / iblock->hdr->man_dtable.cparam.width;
1278
1279
        /* If this is a direct block, set its initial size */
1280
0
        if (row < iblock->hdr->man_dtable.max_direct_rows)
1281
0
            iblock->filt_ents[entry].size = iblock->hdr->man_dtable.row_block_size[row];
1282
0
    } /* end if */
1283
1284
    /* Check for max. entry used */
1285
0
    if (entry > iblock->max_child)
1286
0
        iblock->max_child = entry;
1287
1288
    /* Increment the # of child blocks */
1289
0
    iblock->nchildren++;
1290
1291
    /* Mark indirect block as modified */
1292
0
    if (H5HF__iblock_dirty(iblock) < 0)
1293
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTDIRTY, FAIL, "can't mark indirect block as dirty");
1294
1295
0
done:
1296
0
    FUNC_LEAVE_NOAPI(ret_value)
1297
0
} /* end H5HF__man_iblock_attach() */
1298
1299
/*-------------------------------------------------------------------------
1300
 * Function:  H5HF__man_iblock_detach
1301
 *
1302
 * Purpose: Detach a child block (direct or indirect) from an indirect block
1303
 *
1304
 * Return:  SUCCEED/FAIL
1305
 *
1306
 *-------------------------------------------------------------------------
1307
 */
1308
herr_t
1309
H5HF__man_iblock_detach(H5HF_indirect_t *iblock, unsigned entry)
1310
0
{
1311
0
    H5HF_hdr_t      *hdr;                 /* Fractal heap header */
1312
0
    H5HF_indirect_t *del_iblock = NULL;   /* Pointer to protected indirect block, when deleting */
1313
0
    unsigned         row;                 /* Row for entry */
1314
0
    herr_t           ret_value = SUCCEED; /* Return value */
1315
1316
0
    FUNC_ENTER_PACKAGE
1317
1318
    /*
1319
     * Check arguments.
1320
     */
1321
0
    assert(iblock);
1322
0
    assert(iblock->nchildren);
1323
1324
    /* Set up convenience variables */
1325
0
    hdr = iblock->hdr;
1326
1327
    /* Reset address of entry */
1328
0
    iblock->ents[entry].addr = HADDR_UNDEF;
1329
1330
    /* Compute row for entry */
1331
0
    row = entry / hdr->man_dtable.cparam.width;
1332
1333
    /* Check for I/O filters on this heap */
1334
0
    if (hdr->filter_len > 0) {
1335
        /* Sanity check */
1336
0
        assert(iblock->filt_ents);
1337
1338
        /* If this is a direct block, reset its initial size */
1339
0
        if (row < hdr->man_dtable.max_direct_rows) {
1340
0
            iblock->filt_ents[entry].size        = 0;
1341
0
            iblock->filt_ents[entry].filter_mask = 0;
1342
0
        } /* end if */
1343
0
    }     /* end if */
1344
1345
    /* Check for indirect block being detached */
1346
0
    if (row >= hdr->man_dtable.max_direct_rows) {
1347
0
        unsigned indir_idx; /* Index in parent's child iblock pointer array */
1348
1349
        /* Sanity check */
1350
0
        assert(iblock->child_iblocks);
1351
1352
        /* Compute index in child iblock pointer array */
1353
0
        indir_idx = entry - (hdr->man_dtable.max_direct_rows * hdr->man_dtable.cparam.width);
1354
1355
        /* Sanity check */
1356
0
        assert(iblock->child_iblocks[indir_idx]);
1357
1358
        /* Reset pointer to child indirect block in parent */
1359
0
        iblock->child_iblocks[indir_idx] = NULL;
1360
0
    } /* end if */
1361
1362
    /* Decrement the # of child blocks */
1363
    /* (If the number of children drop to 0, the indirect block will be
1364
     *  removed from the heap when its ref. count drops to zero and the
1365
     *  metadata cache calls the indirect block destructor)
1366
     */
1367
0
    iblock->nchildren--;
1368
1369
    /* Reduce the max. entry used, if necessary */
1370
0
    if (entry == iblock->max_child) {
1371
0
        if (iblock->nchildren > 0)
1372
0
            while (!H5_addr_defined(iblock->ents[iblock->max_child].addr))
1373
0
                iblock->max_child--;
1374
0
        else
1375
0
            iblock->max_child = 0;
1376
0
    } /* end if */
1377
1378
    /* If this is the root indirect block handle some special cases */
1379
0
    if (iblock->block_off == 0) {
1380
        /* If the number of children drops to 1, and that child is the first
1381
         *      direct block in the heap, convert the heap back to using a root
1382
         *      direct block
1383
         */
1384
0
        if (iblock->nchildren == 1 && H5_addr_defined(iblock->ents[0].addr))
1385
0
            if (H5HF__man_iblock_root_revert(iblock) < 0)
1386
0
                HGOTO_ERROR(H5E_HEAP, H5E_CANTSHRINK, FAIL,
1387
0
                            "can't convert root indirect block back to root direct block");
1388
1389
        /* If the indirect block wasn't removed already (by reverting it) */
1390
0
        if (!iblock->removed_from_cache) {
1391
            /* Check for reducing size of root indirect block */
1392
0
            if (iblock->nchildren > 0 && hdr->man_dtable.cparam.start_root_rows != 0 &&
1393
0
                entry > iblock->max_child) {
1394
0
                unsigned max_child_row; /* Row for max. child entry */
1395
1396
                /* Compute information needed for determining whether to reduce size of root indirect block */
1397
0
                max_child_row = iblock->max_child / hdr->man_dtable.cparam.width;
1398
1399
                /* Check if the root indirect block should be reduced */
1400
0
                if (iblock->nrows > 1 && max_child_row <= (iblock->nrows / 2))
1401
0
                    if (H5HF__man_iblock_root_halve(iblock) < 0)
1402
0
                        HGOTO_ERROR(H5E_HEAP, H5E_CANTSHRINK, FAIL,
1403
0
                                    "can't reduce size of root indirect block");
1404
0
            } /* end if */
1405
0
        }     /* end if */
1406
0
    }         /* end if */
1407
1408
    /* If the indirect block wasn't removed already (by reverting it) */
1409
0
    if (!iblock->removed_from_cache) {
1410
        /* Mark indirect block as modified */
1411
0
        if (H5HF__iblock_dirty(iblock) < 0)
1412
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTDIRTY, FAIL, "can't mark indirect block as dirty");
1413
1414
        /* Check for last child being removed from indirect block */
1415
0
        if (iblock->nchildren == 0) {
1416
0
            bool did_protect = false; /* Whether the indirect block was protected */
1417
1418
            /* If this indirect block's refcount is >1, then it's being deleted
1419
             *  from the fractal heap (since its nchildren == 0), but is still
1420
             *  referred to from free space sections in the heap (refcount >1).
1421
             *  Its space in the file needs to be freed now, and it also needs
1422
             *  to be removed from the metadata cache now, in case the space in
1423
             *  the file is reused by another piece of metadata that is inserted
1424
             *  into the cache before the indirect block's entry is evicted
1425
             *  (having two entries at the same address would be an error, from
1426
             *  the cache's perspective).
1427
             */
1428
            /* Lock indirect block for deletion */
1429
0
            if (NULL == (del_iblock = H5HF__man_iblock_protect(hdr, iblock->addr, iblock->nrows,
1430
0
                                                               iblock->parent, iblock->par_entry, true,
1431
0
                                                               H5AC__NO_FLAGS_SET, &did_protect)))
1432
0
                HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to protect fractal heap indirect block");
1433
0
            assert(did_protect == true);
1434
1435
            /* Check for deleting root indirect block (and no root direct block) */
1436
0
            if (iblock->block_off == 0 && hdr->man_dtable.curr_root_rows > 0)
1437
                /* Reset header information back to "empty heap" state */
1438
0
                if (H5HF__hdr_empty(hdr) < 0)
1439
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTSHRINK, FAIL, "can't make heap empty");
1440
1441
            /* Detach from parent indirect block */
1442
0
            if (iblock->parent) {
1443
                /* Destroy flush dependency between indirect block and parent */
1444
0
                if (H5AC_destroy_flush_dependency(iblock->fd_parent, iblock) < 0)
1445
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency");
1446
0
                iblock->fd_parent = NULL;
1447
1448
                /* Detach from parent indirect block */
1449
0
                if (H5HF__man_iblock_detach(iblock->parent, iblock->par_entry) < 0)
1450
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTATTACH, FAIL, "can't detach from parent indirect block");
1451
0
                iblock->parent    = NULL;
1452
0
                iblock->par_entry = 0;
1453
0
            } /* end if */
1454
0
        }     /* end if */
1455
0
    }         /* end if */
1456
1457
    /* Decrement the reference count on this indirect block if we're not deleting it */
1458
    /* (should be after iblock needs to be modified, so that potential 'unpin'
1459
     * on this indirect block doesn't invalidate the 'iblock' variable, if it's
1460
     * not being deleted)
1461
     */
1462
0
    if (H5HF__iblock_decr(iblock) < 0)
1463
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTDEC, FAIL, "can't decrement reference count on shared indirect block");
1464
0
    iblock = NULL;
1465
1466
    /* Delete indirect block from cache, if appropriate */
1467
0
    if (del_iblock) {
1468
0
        unsigned cache_flags    = H5AC__NO_FLAGS_SET; /* Flags for unprotect */
1469
0
        bool     took_ownership = false; /* Flag to indicate that block ownership has transitioned */
1470
1471
        /* If the refcount is still >0, unpin the block and take ownership
1472
         *  from the cache, otherwise let the cache destroy it.
1473
         */
1474
0
        if (del_iblock->rc > 0) {
1475
0
            cache_flags |= (H5AC__DELETED_FLAG | H5AC__TAKE_OWNERSHIP_FLAG);
1476
0
            cache_flags |= H5AC__UNPIN_ENTRY_FLAG;
1477
0
            took_ownership = true;
1478
0
        } /* end if */
1479
0
        else {
1480
            /* Entry should be removed from the cache */
1481
0
            cache_flags |= H5AC__DELETED_FLAG;
1482
1483
            /* If the indirect block is in real file space, tell
1484
             * the cache to free its file space as well.
1485
             */
1486
0
            if (!H5F_IS_TMP_ADDR(hdr->f, del_iblock->addr))
1487
0
                cache_flags |= H5AC__FREE_FILE_SPACE_FLAG;
1488
0
        } /* end else */
1489
1490
        /* Unprotect the indirect block, with appropriate flags */
1491
0
        if (H5HF__man_iblock_unprotect(del_iblock, cache_flags, true) < 0)
1492
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release fractal heap indirect block");
1493
1494
        /* if took ownership, free file space & mark block as removed from cache */
1495
0
        if (took_ownership) {
1496
            /* Free indirect block disk space, if it's in real space */
1497
0
            if (!H5F_IS_TMP_ADDR(hdr->f, del_iblock->addr))
1498
0
                if (H5MF_xfree(hdr->f, H5FD_MEM_FHEAP_IBLOCK, del_iblock->addr, (hsize_t)del_iblock->size) <
1499
0
                    0)
1500
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL,
1501
0
                                "unable to free fractal heap indirect block file space");
1502
0
            del_iblock->addr = HADDR_UNDEF;
1503
1504
            /* Mark block as removed from the cache */
1505
0
            del_iblock->removed_from_cache = true;
1506
0
        } /* end if */
1507
0
    }     /* end if */
1508
1509
0
done:
1510
0
    FUNC_LEAVE_NOAPI(ret_value)
1511
0
} /* end H5HF__man_iblock_detach() */
1512
1513
/*-------------------------------------------------------------------------
1514
 * Function:  H5HF__man_iblock_entry_addr
1515
 *
1516
 * Purpose: Retrieve the address of an indirect block's child
1517
 *
1518
 * Return:  SUCCEED/FAIL
1519
 *
1520
 *-------------------------------------------------------------------------
1521
 */
1522
herr_t
1523
H5HF__man_iblock_entry_addr(H5HF_indirect_t *iblock, unsigned entry, haddr_t *child_addr)
1524
0
{
1525
0
    FUNC_ENTER_PACKAGE_NOERR
1526
1527
    /*
1528
     * Check arguments.
1529
     */
1530
0
    assert(iblock);
1531
0
    assert(child_addr);
1532
1533
    /* Retrieve address of entry */
1534
0
    *child_addr = iblock->ents[entry].addr;
1535
1536
0
    FUNC_LEAVE_NOAPI(SUCCEED)
1537
0
} /* end H5HF__man_iblock_entry_addr() */
1538
1539
/*-------------------------------------------------------------------------
1540
 * Function:  H5HF__man_iblock_delete
1541
 *
1542
 * Purpose: Delete a managed indirect block
1543
 *
1544
 * Note:  This routine does _not_ modify any indirect block that points
1545
 *              to this indirect block, it is assumed that the whole heap is
1546
 *              being deleted in a top-down fashion.
1547
 *
1548
 * Return:  SUCCEED/FAIL
1549
 *
1550
 *-------------------------------------------------------------------------
1551
 */
1552
herr_t
1553
H5HF__man_iblock_delete(H5HF_hdr_t *hdr, haddr_t iblock_addr, unsigned iblock_nrows,
1554
                        H5HF_indirect_t *par_iblock, unsigned par_entry)
1555
0
{
1556
0
    H5HF_indirect_t *iblock;                           /* Pointer to indirect block */
1557
0
    unsigned         row, col;                         /* Current row & column in indirect block */
1558
0
    unsigned         entry;                            /* Current entry in row */
1559
0
    unsigned         cache_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting indirect block */
1560
0
    bool             did_protect;                      /* Whether we protected the indirect block or not */
1561
0
    herr_t           ret_value = SUCCEED;              /* Return value */
1562
1563
0
    FUNC_ENTER_PACKAGE
1564
1565
    /*
1566
     * Check arguments.
1567
     */
1568
0
    assert(hdr);
1569
0
    assert(H5_addr_defined(iblock_addr));
1570
0
    assert(iblock_nrows > 0);
1571
1572
    /* Lock indirect block */
1573
0
    if (NULL == (iblock = H5HF__man_iblock_protect(hdr, iblock_addr, iblock_nrows, par_iblock, par_entry,
1574
0
                                                   true, H5AC__NO_FLAGS_SET, &did_protect)))
1575
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to protect fractal heap indirect block");
1576
0
    assert(iblock->nchildren > 0);
1577
0
    assert(did_protect == true);
1578
1579
    /* Iterate over rows in this indirect block */
1580
0
    entry = 0;
1581
0
    for (row = 0; row < iblock->nrows; row++) {
1582
        /* Iterate over entries in this row */
1583
0
        for (col = 0; col < hdr->man_dtable.cparam.width; col++, entry++) {
1584
            /* Check for child entry at this position */
1585
0
            if (H5_addr_defined(iblock->ents[entry].addr)) {
1586
                /* Are we in a direct or indirect block row */
1587
0
                if (row < hdr->man_dtable.max_direct_rows) {
1588
0
                    hsize_t dblock_size; /* Size of direct block on disk */
1589
1590
                    /* Check for I/O filters on this heap */
1591
0
                    if (hdr->filter_len > 0)
1592
0
                        dblock_size = iblock->filt_ents[entry].size;
1593
0
                    else
1594
0
                        dblock_size = hdr->man_dtable.row_block_size[row];
1595
1596
                    /* Delete child direct block */
1597
0
                    if (H5HF__man_dblock_delete(hdr->f, iblock->ents[entry].addr, dblock_size) < 0)
1598
0
                        HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL,
1599
0
                                    "unable to release fractal heap child direct block");
1600
0
                } /* end if */
1601
0
                else {
1602
0
                    hsize_t  row_block_size; /* The size of blocks in this row */
1603
0
                    unsigned child_nrows;    /* Number of rows in new indirect block */
1604
1605
                    /* Get the row's block size */
1606
0
                    row_block_size = (hsize_t)hdr->man_dtable.row_block_size[row];
1607
1608
                    /* Compute # of rows in next child indirect block to use */
1609
0
                    child_nrows = H5HF__dtable_size_to_rows(&hdr->man_dtable, row_block_size);
1610
1611
                    /* Delete child indirect block */
1612
0
                    if (H5HF__man_iblock_delete(hdr, iblock->ents[entry].addr, child_nrows, iblock, entry) <
1613
0
                        0)
1614
0
                        HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL,
1615
0
                                    "unable to release fractal heap child indirect block");
1616
0
                } /* end else */
1617
0
            }     /* end if */
1618
0
        }         /* end for */
1619
0
    }             /* end row */
1620
1621
#ifndef NDEBUG
1622
    {
1623
        unsigned iblock_status = 0; /* Indirect block's status in the metadata cache */
1624
1625
        /* Check the indirect block's status in the metadata cache */
1626
        if (H5AC_get_entry_status(hdr->f, iblock_addr, &iblock_status) < 0)
1627
            HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL,
1628
                        "unable to check metadata cache status for indirect block");
1629
1630
        /* Check if indirect block is pinned */
1631
        assert(!(iblock_status & H5AC_ES__IS_PINNED));
1632
    }
1633
#endif /* NDEBUG */
1634
1635
    /* Indicate that the indirect block should be deleted  */
1636
0
    cache_flags |= H5AC__DIRTIED_FLAG | H5AC__DELETED_FLAG;
1637
1638
    /* If the indirect block is in real file space, tell
1639
     * the cache to free its file space as well.
1640
     */
1641
0
    if (!H5F_IS_TMP_ADDR(hdr->f, iblock_addr))
1642
0
        cache_flags |= H5AC__FREE_FILE_SPACE_FLAG;
1643
1644
0
done:
1645
    /* Unprotect the indirect block, with appropriate flags */
1646
0
    if (iblock && H5HF__man_iblock_unprotect(iblock, cache_flags, did_protect) < 0)
1647
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release fractal heap indirect block");
1648
1649
0
    FUNC_LEAVE_NOAPI(ret_value)
1650
0
} /* end H5HF__man_iblock_delete() */
1651
1652
/*-------------------------------------------------------------------------
1653
 *
1654
 * Function:    H5HF__man_iblock_size
1655
 *
1656
 * Purpose:     Gather storage used for the indirect block in fractal heap
1657
 *
1658
 * Return:      non-negative on success, negative on error
1659
 *
1660
 *-------------------------------------------------------------------------
1661
 */
1662
herr_t
1663
H5HF__man_iblock_size(H5F_t *f, H5HF_hdr_t *hdr, haddr_t iblock_addr, unsigned nrows,
1664
                      H5HF_indirect_t *par_iblock, unsigned par_entry, hsize_t *heap_size)
1665
0
{
1666
0
    H5HF_indirect_t *iblock = NULL;       /* Pointer to indirect block */
1667
0
    bool             did_protect;         /* Whether we protected the indirect block or not */
1668
0
    herr_t           ret_value = SUCCEED; /* Return value */
1669
1670
0
    FUNC_ENTER_PACKAGE
1671
1672
    /*
1673
     * Check arguments.
1674
     */
1675
0
    assert(f);
1676
0
    assert(hdr);
1677
0
    assert(H5_addr_defined(iblock_addr));
1678
0
    assert(heap_size);
1679
1680
    /* Protect the indirect block */
1681
0
    if (NULL == (iblock = H5HF__man_iblock_protect(hdr, iblock_addr, nrows, par_iblock, par_entry, false,
1682
0
                                                   H5AC__READ_ONLY_FLAG, &did_protect)))
1683
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTLOAD, FAIL, "unable to load fractal heap indirect block");
1684
1685
    /* Accumulate size of this indirect block */
1686
0
    *heap_size += iblock->size;
1687
1688
    /* Indirect entries in this indirect block */
1689
0
    if (iblock->nrows > hdr->man_dtable.max_direct_rows) {
1690
0
        unsigned first_row_bits;    /* Number of bits used bit addresses in first row */
1691
0
        unsigned num_indirect_rows; /* Number of rows of blocks in each indirect block */
1692
0
        unsigned entry;             /* Current entry in row */
1693
0
        size_t   u;                 /* Local index variable */
1694
1695
0
        entry          = hdr->man_dtable.max_direct_rows * hdr->man_dtable.cparam.width;
1696
0
        first_row_bits = H5VM_log2_of2((uint32_t)hdr->man_dtable.cparam.start_block_size) +
1697
0
                         H5VM_log2_of2(hdr->man_dtable.cparam.width);
1698
0
        num_indirect_rows = (H5VM_log2_gen(hdr->man_dtable.row_block_size[hdr->man_dtable.max_direct_rows]) -
1699
0
                             first_row_bits) +
1700
0
                            1;
1701
0
        for (u = hdr->man_dtable.max_direct_rows; u < iblock->nrows; u++, num_indirect_rows++) {
1702
0
            size_t v; /* Local index variable */
1703
1704
0
            for (v = 0; v < hdr->man_dtable.cparam.width; v++, entry++)
1705
0
                if (H5_addr_defined(iblock->ents[entry].addr))
1706
0
                    if (H5HF__man_iblock_size(f, hdr, iblock->ents[entry].addr, num_indirect_rows, iblock,
1707
0
                                              entry, heap_size) < 0)
1708
0
                        HGOTO_ERROR(H5E_HEAP, H5E_CANTLOAD, FAIL,
1709
0
                                    "unable to get fractal heap storage info for indirect block");
1710
0
        } /* end for */
1711
0
    }     /* end if */
1712
1713
0
done:
1714
    /* Release the indirect block */
1715
0
    if (iblock && H5HF__man_iblock_unprotect(iblock, H5AC__NO_FLAGS_SET, did_protect) < 0)
1716
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release fractal heap indirect block");
1717
0
    iblock = NULL;
1718
1719
0
    FUNC_LEAVE_NOAPI(ret_value)
1720
0
} /* end H5HF__man_iblock_size() */
1721
1722
/*-------------------------------------------------------------------------
1723
 *
1724
 * Function:    H5HF_man_iblock_parent_info
1725
 *
1726
 * Purpose:     Determine the parent block's offset and entry location
1727
 *    (within it's parent) of an indirect block, given its offset
1728
 *    within the heap.
1729
 *
1730
 * Return:  Non-negative on success / Negative on failure
1731
 *
1732
 *-------------------------------------------------------------------------
1733
 */
1734
herr_t
1735
H5HF__man_iblock_parent_info(const H5HF_hdr_t *hdr, hsize_t block_off, hsize_t *ret_par_block_off,
1736
                             unsigned *ret_entry)
1737
0
{
1738
0
    hsize_t  par_block_off;              /* Offset of parent within heap */
1739
0
    hsize_t  prev_par_block_off;         /* Offset of previous parent block within heap */
1740
0
    unsigned row, col;                   /* Row & column for block */
1741
0
    unsigned prev_row = 0, prev_col = 0; /* Previous row & column for block */
1742
0
    herr_t   ret_value = SUCCEED;        /* Return value */
1743
1744
0
    FUNC_ENTER_PACKAGE
1745
1746
    /*
1747
     * Check arguments.
1748
     */
1749
0
    assert(hdr);
1750
0
    assert(block_off > 0);
1751
0
    assert(ret_entry);
1752
1753
    /* Look up row & column for object */
1754
0
    if (H5HF__dtable_lookup(&hdr->man_dtable, block_off, &row, &col) < 0)
1755
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTCOMPUTE, FAIL, "can't compute row & column of block");
1756
1757
    /* Sanity check - first lookup must be an indirect block */
1758
0
    assert(row >= hdr->man_dtable.max_direct_rows);
1759
1760
    /* Traverse down, until a direct block at the offset is found, then
1761
     *  use previous (i.e. parent's) offset, row, and column.
1762
     */
1763
0
    prev_par_block_off = par_block_off = 0;
1764
0
    while (row >= hdr->man_dtable.max_direct_rows) {
1765
        /* Retain previous parent block offset */
1766
0
        prev_par_block_off = par_block_off;
1767
1768
        /* Compute the new parent indirect block's offset in the heap's address space */
1769
        /* (based on previous block offset) */
1770
0
        par_block_off += hdr->man_dtable.row_block_off[row];
1771
0
        par_block_off += hdr->man_dtable.row_block_size[row] * col;
1772
1773
        /* Preserve current row & column */
1774
0
        prev_row = row;
1775
0
        prev_col = col;
1776
1777
        /* Look up row & column in new indirect block for object */
1778
0
        if (H5HF__dtable_lookup(&hdr->man_dtable, (block_off - par_block_off), &row, &col) < 0)
1779
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTCOMPUTE, FAIL, "can't compute row & column of block");
1780
0
    } /* end while */
1781
1782
    /* Sanity check */
1783
0
    assert(row == 0);
1784
0
    assert(col == 0);
1785
1786
    /* Set return parameters */
1787
0
    *ret_par_block_off = prev_par_block_off;
1788
0
    *ret_entry         = (prev_row * hdr->man_dtable.cparam.width) + prev_col;
1789
1790
0
done:
1791
0
    FUNC_LEAVE_NOAPI(ret_value)
1792
0
} /* end H5HF__man_iblock_par_info() */
1793
1794
/*-------------------------------------------------------------------------
1795
 * Function:  H5HF__man_iblock_dest
1796
 *
1797
 * Purpose: Destroys a fractal heap indirect block in memory.
1798
 *
1799
 * Return:  Non-negative on success/Negative on failure
1800
 *
1801
 *-------------------------------------------------------------------------
1802
 */
1803
herr_t
1804
H5HF__man_iblock_dest(H5HF_indirect_t *iblock)
1805
0
{
1806
0
    herr_t ret_value = SUCCEED; /* Return value */
1807
1808
0
    FUNC_ENTER_PACKAGE
1809
1810
    /*
1811
     * Check arguments.
1812
     */
1813
0
    assert(iblock);
1814
0
    assert(iblock->rc == 0);
1815
1816
    /* Decrement reference count on shared info */
1817
0
    assert(iblock->hdr);
1818
0
    if (H5HF__hdr_decr(iblock->hdr) < 0)
1819
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTDEC, FAIL, "can't decrement reference count on shared heap header");
1820
0
    if (iblock->parent)
1821
0
        if (H5HF__iblock_decr(iblock->parent) < 0)
1822
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTDEC, FAIL,
1823
0
                        "can't decrement reference count on shared indirect block");
1824
1825
    /* Release entry tables */
1826
0
    if (iblock->ents)
1827
0
        iblock->ents = H5FL_SEQ_FREE(H5HF_indirect_ent_t, iblock->ents);
1828
0
    if (iblock->filt_ents)
1829
0
        iblock->filt_ents = H5FL_SEQ_FREE(H5HF_indirect_filt_ent_t, iblock->filt_ents);
1830
0
    if (iblock->child_iblocks)
1831
0
        iblock->child_iblocks = H5FL_SEQ_FREE(H5HF_indirect_ptr_t, iblock->child_iblocks);
1832
1833
    /* Free fractal heap indirect block info */
1834
0
    iblock = H5FL_FREE(H5HF_indirect_t, iblock);
1835
1836
0
done:
1837
0
    FUNC_LEAVE_NOAPI(ret_value)
1838
0
} /* end H5HF__man_iblock_dest() */