Coverage Report

Created: 2025-08-26 06:30

/src/hdf5/src/H5HL.c
Line
Count
Source (jump to first uncovered line)
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:         H5HL.c
16
 *
17
 * Purpose:         Heap functions for the local heaps used by symbol
18
 *                  tables to store names (among other things).
19
 *
20
 *-------------------------------------------------------------------------
21
 */
22
23
/****************/
24
/* Module Setup */
25
/****************/
26
27
#include "H5HLmodule.h" /* This source code file is part of the H5HL module */
28
29
/***********/
30
/* Headers */
31
/***********/
32
#include "H5private.h"   /* Generic Functions                        */
33
#include "H5ACprivate.h" /* Metadata Cache                           */
34
#include "H5Eprivate.h"  /* Error Handling                           */
35
#include "H5Fprivate.h"  /* Files                                    */
36
#include "H5FLprivate.h" /* Free Lists                               */
37
#include "H5HLpkg.h"     /* Local Heaps                              */
38
#include "H5MFprivate.h" /* File Memory Management                   */
39
#include "H5MMprivate.h" /* Memory Management                        */
40
41
/****************/
42
/* Local Macros */
43
/****************/
44
45
0
#define H5HL_MIN_HEAP 128 /* Minimum size to reduce heap buffer to */
46
47
/******************/
48
/* Local Typedefs */
49
/******************/
50
51
/********************/
52
/* Package Typedefs */
53
/********************/
54
55
/********************/
56
/* Local Prototypes */
57
/********************/
58
59
static H5HL_free_t *H5HL__remove_free(H5HL_t *heap, H5HL_free_t *fl);
60
static herr_t       H5HL__minimize_heap_space(H5F_t *f, H5HL_t *heap);
61
static herr_t       H5HL__dirty(H5HL_t *heap);
62
63
/*********************/
64
/* Package Variables */
65
/*********************/
66
67
/* Package initialization variable */
68
bool H5_PKG_INIT_VAR = false;
69
70
/* Declare a free list to manage the H5HL_free_t struct */
71
H5FL_DEFINE(H5HL_free_t);
72
73
/* Declare a PQ free list to manage the heap chunk information */
74
H5FL_BLK_DEFINE(lheap_chunk);
75
76
/*****************************/
77
/* Library Private Variables */
78
/*****************************/
79
80
/*******************/
81
/* Local Variables */
82
/*******************/
83
84
/*-------------------------------------------------------------------------
85
 * Function:    H5HL_create
86
 *
87
 * Purpose:     Creates a new heap data structure on disk and caches it
88
 *              in memory.  SIZE_HINT is a hint for the initial size of the
89
 *              data area of the heap.  If size hint is invalid then a
90
 *              reasonable (but probably not optimal) size will be chosen.
91
 *
92
 * Return:      Success:    SUCCEED. The file address of new heap is
93
 *                          returned through the ADDR argument.
94
 *              Failure:    FAIL.  addr_p will be HADDR_UNDEF.
95
 *
96
 *-------------------------------------------------------------------------
97
 */
98
herr_t
99
H5HL_create(H5F_t *f, size_t size_hint, haddr_t *addr_p /*out*/)
100
0
{
101
0
    H5HL_t      *heap       = NULL; /* Heap created                 */
102
0
    H5HL_prfx_t *prfx       = NULL; /* Heap prefix                  */
103
0
    hsize_t      total_size = 0;    /* Total heap size on disk      */
104
0
    herr_t       ret_value  = SUCCEED;
105
106
0
    FUNC_ENTER_NOAPI(FAIL)
107
108
    /* check arguments */
109
0
    assert(f);
110
0
    assert(addr_p);
111
112
    /* Adjust size hint as necessary */
113
0
    if (size_hint && size_hint < H5HL_SIZEOF_FREE(f))
114
0
        size_hint = H5HL_SIZEOF_FREE(f);
115
0
    size_hint = H5HL_ALIGN(size_hint);
116
117
    /* Allocate new heap structure */
118
0
    if (NULL == (heap = H5HL__new(H5F_SIZEOF_SIZE(f), H5F_SIZEOF_ADDR(f), H5HL_SIZEOF_HDR(f))))
119
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTALLOC, FAIL, "can't allocate new heap struct");
120
121
    /* Allocate file space */
122
0
    total_size = heap->prfx_size + size_hint;
123
0
    if (HADDR_UNDEF == (heap->prfx_addr = H5MF_alloc(f, H5FD_MEM_LHEAP, total_size)))
124
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTALLOC, FAIL, "unable to allocate file memory");
125
126
    /* Initialize info */
127
0
    heap->single_cache_obj = true;
128
0
    heap->dblk_addr        = heap->prfx_addr + (hsize_t)heap->prfx_size;
129
0
    heap->dblk_size        = size_hint;
130
0
    if (size_hint)
131
0
        if (NULL == (heap->dblk_image = H5FL_BLK_CALLOC(lheap_chunk, size_hint)))
132
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTALLOC, FAIL, "memory allocation failed");
133
134
    /* free list */
135
0
    if (size_hint) {
136
0
        if (NULL == (heap->freelist = H5FL_MALLOC(H5HL_free_t)))
137
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTALLOC, FAIL, "memory allocation failed");
138
0
        heap->freelist->offset = 0;
139
0
        heap->freelist->size   = size_hint;
140
0
        heap->freelist->prev = heap->freelist->next = NULL;
141
0
        heap->free_block                            = 0;
142
0
    }
143
0
    else {
144
0
        heap->freelist   = NULL;
145
0
        heap->free_block = H5HL_FREE_NULL;
146
0
    }
147
148
    /* Allocate the heap prefix */
149
0
    if (NULL == (prfx = H5HL__prfx_new(heap)))
150
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTALLOC, FAIL, "memory allocation failed");
151
152
    /* Add to cache */
153
0
    if (FAIL == H5AC_insert_entry(f, H5AC_LHEAP_PRFX, heap->prfx_addr, prfx, H5AC__NO_FLAGS_SET))
154
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTINIT, FAIL, "unable to cache local heap prefix");
155
156
    /* Set address to return */
157
0
    *addr_p = heap->prfx_addr;
158
159
0
done:
160
0
    if (ret_value < 0) {
161
0
        *addr_p = HADDR_UNDEF;
162
0
        if (prfx) {
163
0
            if (FAIL == H5HL__prfx_dest(prfx))
164
0
                HDONE_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "unable to destroy local heap prefix");
165
0
        }
166
0
        else {
167
0
            if (heap) {
168
0
                if (H5_addr_defined(heap->prfx_addr))
169
0
                    if (FAIL == H5MF_xfree(f, H5FD_MEM_LHEAP, heap->prfx_addr, total_size))
170
0
                        HDONE_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "can't release heap data?");
171
0
                if (FAIL == H5HL__dest(heap))
172
0
                    HDONE_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "unable to destroy local heap");
173
0
            }
174
0
        }
175
0
    }
176
0
    FUNC_LEAVE_NOAPI(ret_value)
177
0
} /* end H5HL_create() */
178
179
/*-------------------------------------------------------------------------
180
 * Function:    H5HL__minimize_heap_space
181
 *
182
 * Purpose:     Go through the heap's freelist and determine if we can
183
 *              eliminate the free blocks at the tail of the buffer.
184
 *
185
 * Return:      SUCCEED/FAIL
186
 *
187
 *-------------------------------------------------------------------------
188
 */
189
static herr_t
190
H5HL__minimize_heap_space(H5F_t *f, H5HL_t *heap)
191
0
{
192
0
    size_t new_heap_size = heap->dblk_size; /* New size of heap */
193
0
    herr_t ret_value     = SUCCEED;
194
195
0
    FUNC_ENTER_PACKAGE
196
197
    /* Check args */
198
0
    assert(f);
199
0
    assert(heap);
200
201
    /* Check to see if we can reduce the size of the heap in memory by
202
     * eliminating free blocks at the tail of the buffer before flushing the
203
     * buffer out.
204
     */
205
0
    if (heap->freelist) {
206
0
        H5HL_free_t *tmp_fl;
207
0
        H5HL_free_t *last_fl = NULL;
208
209
        /* Search for a free block at the end of the buffer */
210
0
        for (tmp_fl = heap->freelist; tmp_fl; tmp_fl = tmp_fl->next)
211
            /* Check if the end of this free block is at the end of the buffer */
212
0
            if (tmp_fl->offset + tmp_fl->size == heap->dblk_size) {
213
0
                last_fl = tmp_fl;
214
0
                break;
215
0
            }
216
217
        /* Found free block at the end of the buffer, decide what to do
218
         * about it
219
         */
220
0
        if (last_fl) {
221
            /* If the last free block's size is more than half the memory
222
             * buffer size (and the memory buffer is larger than the
223
             * minimum size), reduce or eliminate it.
224
             */
225
0
            if (last_fl->size >= (heap->dblk_size / 2) && heap->dblk_size > H5HL_MIN_HEAP) {
226
                /* Reduce size of buffer until it's too small or would
227
                 * eliminate the free block
228
                 */
229
0
                while (new_heap_size > H5HL_MIN_HEAP &&
230
0
                       new_heap_size >= (last_fl->offset + H5HL_SIZEOF_FREE(f)))
231
0
                    new_heap_size /= 2;
232
233
                /* Check if reducing the memory buffer size would
234
                 * eliminate the free block
235
                 */
236
0
                if (new_heap_size < (last_fl->offset + H5HL_SIZEOF_FREE(f))) {
237
                    /* Check if this is the only block on the free list */
238
0
                    if (last_fl->prev == NULL && last_fl->next == NULL) {
239
                        /* Double the new memory size */
240
0
                        new_heap_size *= 2;
241
242
                        /* Truncate the free block */
243
0
                        last_fl->size = H5HL_ALIGN(new_heap_size - last_fl->offset);
244
0
                        new_heap_size = last_fl->offset + last_fl->size;
245
0
                        assert(last_fl->size >= H5HL_SIZEOF_FREE(f));
246
0
                    }
247
0
                    else {
248
                        /* Set the size of the memory buffer to the start
249
                         * of the free list
250
                         */
251
0
                        new_heap_size = last_fl->offset;
252
253
                        /* Eliminate the free block from the list */
254
0
                        last_fl = H5HL__remove_free(heap, last_fl);
255
0
                    }
256
0
                }
257
0
                else {
258
                    /* Truncate the free block */
259
0
                    last_fl->size = H5HL_ALIGN(new_heap_size - last_fl->offset);
260
0
                    new_heap_size = last_fl->offset + last_fl->size;
261
0
                    assert(last_fl->size >= H5HL_SIZEOF_FREE(f));
262
0
                    assert(last_fl->size == H5HL_ALIGN(last_fl->size));
263
0
                }
264
0
            }
265
0
        }
266
0
    }
267
268
    /* If the heap grew smaller than disk storage then move the
269
     * data segment of the heap to another contiguous block of disk
270
     * storage.
271
     */
272
0
    if (new_heap_size != heap->dblk_size) {
273
0
        assert(new_heap_size < heap->dblk_size);
274
275
        /* Resize the memory buffer */
276
0
        if (NULL == (heap->dblk_image = H5FL_BLK_REALLOC(lheap_chunk, heap->dblk_image, new_heap_size)))
277
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTALLOC, FAIL, "memory allocation failed");
278
279
        /* Reallocate data block in file */
280
0
        if (FAIL == H5HL__dblk_realloc(f, heap, new_heap_size))
281
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTRESIZE, FAIL, "reallocating data block failed");
282
0
    }
283
284
0
done:
285
0
    FUNC_LEAVE_NOAPI(ret_value)
286
0
} /* H5HL__minimize_heap_space() */
287
288
/*-------------------------------------------------------------------------
289
 * Function:    H5HL_protect
290
 *
291
 * Purpose:     This function is a wrapper for the H5AC_protect call.
292
 *
293
 * Return:      Success:    Non-NULL pointer to the local heap prefix.
294
 *              Failure:    NULL
295
 *
296
 *-------------------------------------------------------------------------
297
 */
298
H5HL_t *
299
H5HL_protect(H5F_t *f, haddr_t addr, unsigned flags)
300
1.70k
{
301
1.70k
    H5HL_cache_prfx_ud_t prfx_udata;       /* User data for protecting local heap prefix       */
302
1.70k
    H5HL_prfx_t         *prfx      = NULL; /* Local heap prefix                                */
303
1.70k
    H5HL_dblk_t         *dblk      = NULL; /* Local heap data block                            */
304
1.70k
    H5HL_t              *heap      = NULL; /* Heap data structure                              */
305
1.70k
    H5HL_t              *ret_value = NULL;
306
307
1.70k
    FUNC_ENTER_NOAPI(NULL)
308
309
    /* Check arguments */
310
1.70k
    assert(f);
311
1.70k
    assert(H5_addr_defined(addr));
312
313
    /* Only the H5AC__READ_ONLY_FLAG may appear in flags */
314
1.70k
    assert((flags & (unsigned)(~H5AC__READ_ONLY_FLAG)) == 0);
315
316
    /* Construct the user data for protect callback */
317
1.70k
    prfx_udata.sizeof_size = H5F_SIZEOF_SIZE(f);
318
1.70k
    prfx_udata.sizeof_addr = H5F_SIZEOF_ADDR(f);
319
1.70k
    prfx_udata.prfx_addr   = addr;
320
1.70k
    prfx_udata.sizeof_prfx = H5HL_SIZEOF_HDR(f);
321
322
    /* Protect the local heap prefix */
323
1.70k
    if (NULL == (prfx = (H5HL_prfx_t *)H5AC_protect(f, H5AC_LHEAP_PRFX, addr, &prfx_udata, flags)))
324
148
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, NULL, "unable to load heap prefix");
325
326
    /* Get the pointer to the heap */
327
1.55k
    heap = prfx->heap;
328
329
    /* Check if the heap is already pinned in memory */
330
    /* (for re-entrant situation) */
331
1.55k
    if (heap->prots == 0) {
332
1.55k
        void *pin_obj; /* Pointer to local heap object to pin */
333
334
        /* Check if heap has separate data block */
335
1.55k
        if (heap->single_cache_obj)
336
            /* Pin prefix */
337
77
            pin_obj = prfx;
338
1.48k
        else {
339
            /* Protect the local heap data block */
340
1.48k
            if (NULL ==
341
1.48k
                (dblk = (H5HL_dblk_t *)H5AC_protect(f, H5AC_LHEAP_DBLK, heap->dblk_addr, heap, flags)))
342
206
                HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, NULL, "unable to load heap data block");
343
344
            /* Pin data block */
345
1.27k
            pin_obj = dblk;
346
1.27k
        }
347
348
        /* Pin local heap object */
349
1.35k
        if (H5AC_pin_protected_entry(pin_obj) < 0)
350
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTPIN, NULL, "unable to pin local heap object");
351
1.35k
    }
352
353
    /* Increment # of times heap is protected */
354
1.35k
    heap->prots++;
355
356
    /* Set return value */
357
1.35k
    ret_value = heap;
358
359
1.70k
done:
360
    /* Release the prefix from the cache, now pinned */
361
1.70k
    if (prfx && heap && H5AC_unprotect(f, H5AC_LHEAP_PRFX, heap->prfx_addr, prfx, H5AC__NO_FLAGS_SET) < 0)
362
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, NULL, "unable to release local heap prefix");
363
364
    /* Release the data block from the cache, now pinned */
365
1.70k
    if (dblk && heap && H5AC_unprotect(f, H5AC_LHEAP_DBLK, heap->dblk_addr, dblk, H5AC__NO_FLAGS_SET) < 0)
366
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, NULL, "unable to release local heap data block");
367
368
1.70k
    FUNC_LEAVE_NOAPI(ret_value)
369
1.70k
} /* end H5HL_protect() */
370
371
/*-------------------------------------------------------------------------
372
 * Function:    H5HL_offset_into
373
 *
374
 * Purpose:     Called directly after the call to H5HL_protect so that
375
 *              a pointer to the object in the heap can be obtained.
376
 *
377
 * Return:      Success:    Valid pointer
378
 *              Failure:    NULL
379
 *
380
 *-------------------------------------------------------------------------
381
 */
382
void *
383
H5HL_offset_into(const H5HL_t *heap, size_t offset)
384
3.93k
{
385
3.93k
    void *ret_value = NULL;
386
387
3.93k
    FUNC_ENTER_NOAPI(NULL)
388
389
    /* Sanity check */
390
3.93k
    assert(heap);
391
3.93k
    if (offset >= heap->dblk_size)
392
757
        HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, NULL, "unable to offset into local heap data block");
393
394
3.17k
    ret_value = heap->dblk_image + offset;
395
396
3.93k
done:
397
3.93k
    FUNC_LEAVE_NOAPI(ret_value)
398
3.93k
} /* end H5HL_offset_into() */
399
400
/*-------------------------------------------------------------------------
401
 * Function:    H5HL_unprotect
402
 *
403
 * Purpose:     Unprotect the data retrieved by the H5HL_protect call.
404
 *
405
 * Return:      SUCCEED/FAIL
406
 *
407
 *-------------------------------------------------------------------------
408
 */
409
herr_t
410
H5HL_unprotect(H5HL_t *heap)
411
1.35k
{
412
1.35k
    herr_t ret_value = SUCCEED;
413
414
1.35k
    FUNC_ENTER_NOAPI(FAIL)
415
416
    /* Check arguments */
417
1.35k
    assert(heap);
418
419
    /* Decrement # of times heap is protected */
420
1.35k
    heap->prots--;
421
422
    /* Check for last unprotection of heap */
423
1.35k
    if (heap->prots == 0) {
424
        /* Check for separate heap data block */
425
1.35k
        if (heap->single_cache_obj) {
426
            /* Mark local heap prefix as evictable again */
427
77
            if (FAIL == H5AC_unpin_entry(heap->prfx))
428
0
                HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPIN, FAIL, "unable to unpin local heap data block");
429
77
        }
430
1.27k
        else {
431
            /* Sanity check */
432
1.27k
            assert(heap->dblk);
433
434
            /* Mark local heap data block as evictable again */
435
            /* (data block still pins prefix) */
436
1.27k
            if (FAIL == H5AC_unpin_entry(heap->dblk))
437
0
                HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPIN, FAIL, "unable to unpin local heap data block");
438
1.27k
        }
439
1.35k
    }
440
441
1.35k
done:
442
1.35k
    FUNC_LEAVE_NOAPI(ret_value)
443
1.35k
} /* end H5HL_unprotect() */
444
445
/*-------------------------------------------------------------------------
446
 * Function:    H5HL__remove_free
447
 *
448
 * Purpose:     Removes free list element FL from the specified heap and
449
 *              frees it.
450
 *
451
 * Return:      NULL
452
 *
453
 *-------------------------------------------------------------------------
454
 */
455
static H5HL_free_t *
456
H5HL__remove_free(H5HL_t *heap, H5HL_free_t *fl)
457
0
{
458
0
    H5HL_free_t *ret_value = NULL;
459
460
0
    FUNC_ENTER_PACKAGE_NOERR
461
462
0
    if (fl->prev)
463
0
        fl->prev->next = fl->next;
464
0
    if (fl->next)
465
0
        fl->next->prev = fl->prev;
466
467
0
    if (!fl->prev)
468
0
        heap->freelist = fl->next;
469
470
    /* H5FL_FREE always returns NULL so we can't check for errors */
471
0
    ret_value = (H5HL_free_t *)H5FL_FREE(H5HL_free_t, fl);
472
473
0
    FUNC_LEAVE_NOAPI(ret_value)
474
0
} /* end H5HL__remove_free() */
475
476
/*-------------------------------------------------------------------------
477
 * Function:    H5HL__dirty
478
 *
479
 * Purpose:     Mark heap as dirty
480
 *
481
 * Return:      SUCCEED/FAIL
482
 *
483
 *-------------------------------------------------------------------------
484
 */
485
static herr_t
486
H5HL__dirty(H5HL_t *heap)
487
0
{
488
0
    herr_t ret_value = SUCCEED;
489
490
0
    FUNC_ENTER_PACKAGE
491
492
    /* Check arguments */
493
0
    assert(heap);
494
0
    assert(heap->prfx);
495
496
    /* Mark heap data block as dirty, if there is one */
497
0
    if (!heap->single_cache_obj) {
498
        /* Sanity check */
499
0
        assert(heap->dblk);
500
501
0
        if (FAIL == H5AC_mark_entry_dirty(heap->dblk))
502
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTMARKDIRTY, FAIL, "unable to mark heap data block as dirty");
503
0
    }
504
505
    /* Mark heap prefix as dirty */
506
0
    if (FAIL == H5AC_mark_entry_dirty(heap->prfx))
507
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTMARKDIRTY, FAIL, "unable to mark heap prefix as dirty");
508
509
0
done:
510
0
    FUNC_LEAVE_NOAPI(ret_value)
511
0
} /* end H5HL__dirty() */
512
513
/*-------------------------------------------------------------------------
514
 * Function:    H5HL_insert
515
 *
516
 * Purpose:     Inserts a new item into the heap.
517
 *
518
 * Return:      Success:    SUCCEED
519
 *                          Offset set to location of new item within heap
520
 *
521
 *              Failure:    FAIL
522
 *                          Offset set to SIZE_MAX
523
 *
524
 *-------------------------------------------------------------------------
525
 */
526
herr_t
527
H5HL_insert(H5F_t *f, H5HL_t *heap, size_t buf_size, const void *buf, size_t *offset_out)
528
0
{
529
0
    H5HL_free_t *fl = NULL, *last_fl = NULL;
530
0
    size_t       need_size;
531
0
    size_t       offset = 0;
532
0
    bool         found;
533
0
    herr_t       ret_value = SUCCEED;
534
535
0
    FUNC_ENTER_NOAPI(FAIL)
536
537
    /* Check arguments */
538
0
    assert(f);
539
0
    assert(heap);
540
0
    assert(buf_size > 0);
541
0
    assert(buf);
542
0
    assert(offset_out);
543
544
    /* Mark heap as dirty in cache */
545
    /* (A bit early in the process, but it's difficult to determine in the
546
     *  code below where to mark the heap as dirty, especially in error cases,
547
     *  so we just accept that an extra flush of the heap info could occur
548
     *  if an error occurs -QAK)
549
     */
550
0
    if (FAIL == H5HL__dirty(heap))
551
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTMARKDIRTY, FAIL, "unable to mark heap as dirty");
552
553
    /* In order to keep the free list descriptors aligned on word boundaries,
554
     * whatever that might mean, we round the size up to the next multiple of
555
     * a word.
556
     */
557
0
    need_size = H5HL_ALIGN(buf_size);
558
559
    /* Look for a free slot large enough for this object and which would
560
     * leave zero or at least H5G_SIZEOF_FREE bytes left over.
561
     */
562
0
    for (fl = heap->freelist, found = false; fl; fl = fl->next) {
563
0
        if (fl->size > need_size && fl->size - need_size >= H5HL_SIZEOF_FREE(f)) {
564
            /* A big enough free block was found */
565
0
            offset = fl->offset;
566
0
            fl->offset += need_size;
567
0
            fl->size -= need_size;
568
0
            assert(fl->offset == H5HL_ALIGN(fl->offset));
569
0
            assert(fl->size == H5HL_ALIGN(fl->size));
570
0
            found = true;
571
0
            break;
572
0
        }
573
0
        else if (fl->size == need_size) {
574
            /* Free block of exact size found */
575
0
            offset = fl->offset;
576
0
            fl     = H5HL__remove_free(heap, fl);
577
0
            found  = true;
578
0
            break;
579
0
        }
580
0
        else if (!last_fl || last_fl->offset < fl->offset) {
581
            /* Track free space that's closest to end of heap */
582
0
            last_fl = fl;
583
0
        }
584
0
    }
585
586
    /* If no free chunk was large enough, then allocate more space and
587
     * add it to the free list.  If the heap ends with a free chunk, we
588
     * can extend that free chunk.  Otherwise we'll have to make another
589
     * free chunk.  If the heap must expand, we double its size.
590
     */
591
0
    if (found == false) {
592
0
        size_t need_more;     /* How much more space we need */
593
0
        size_t new_dblk_size; /* Final size of space allocated for heap data block */
594
0
        size_t old_dblk_size; /* Previous size of space allocated for heap data block */
595
0
        htri_t was_extended;  /* Whether the local heap's data segment on disk was extended */
596
597
        /* At least double the heap's size, making certain there's enough room
598
         * for the new object
599
         */
600
0
        need_more = MAX(need_size, heap->dblk_size);
601
602
        /* If there is no last free block or it's not at the end of the heap,
603
         * and the amount of space to allocate is not big enough to include at
604
         * least the new object and a free-list info, trim down the amount of
605
         * space requested to just the amount of space needed.  (Generally
606
         * speaking, this only occurs when the heap is small -QAK)
607
         */
608
0
        if (!(last_fl && last_fl->offset + last_fl->size == heap->dblk_size) &&
609
0
            (need_more < (need_size + H5HL_SIZEOF_FREE(f))))
610
0
            need_more = need_size;
611
612
0
        new_dblk_size = heap->dblk_size + need_more;
613
0
        assert(heap->dblk_size < new_dblk_size);
614
0
        old_dblk_size = heap->dblk_size;
615
0
        H5_CHECK_OVERFLOW(heap->dblk_size, size_t, hsize_t);
616
0
        H5_CHECK_OVERFLOW(new_dblk_size, size_t, hsize_t);
617
618
        /* Extend current heap if possible */
619
0
        was_extended = H5MF_try_extend(f, H5FD_MEM_LHEAP, heap->dblk_addr, (hsize_t)(heap->dblk_size),
620
0
                                       (hsize_t)need_more);
621
0
        if (FAIL == was_extended)
622
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTEXTEND, FAIL, "error trying to extend heap");
623
624
        /* Check if we extended the heap data block in file */
625
0
        if (was_extended == true) {
626
            /* Check for prefix & data block contiguous */
627
0
            if (heap->single_cache_obj) {
628
                /* Resize prefix+data block */
629
0
                if (FAIL == H5AC_resize_entry(heap->prfx, (size_t)(heap->prfx_size + new_dblk_size)))
630
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTRESIZE, FAIL, "unable to resize heap prefix in cache");
631
0
            }
632
0
            else {
633
                /* Resize 'standalone' data block */
634
0
                if (FAIL == H5AC_resize_entry(heap->dblk, (size_t)new_dblk_size))
635
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTRESIZE, FAIL, "unable to resize heap data block in cache");
636
0
            }
637
638
            /* Note new size */
639
0
            heap->dblk_size = new_dblk_size;
640
0
        }
641
0
        else { /* ...if we can't, allocate a new chunk & release the old */
642
            /* Reallocate data block in file */
643
0
            if (FAIL == H5HL__dblk_realloc(f, heap, new_dblk_size))
644
0
                HGOTO_ERROR(H5E_HEAP, H5E_CANTRESIZE, FAIL, "reallocating data block failed");
645
0
        }
646
647
        /* If the last free list in the heap is at the end of the heap, extend it */
648
0
        if (last_fl && last_fl->offset + last_fl->size == old_dblk_size) {
649
            /*
650
             * Increase the size of the last free block.
651
             */
652
0
            offset = last_fl->offset;
653
0
            last_fl->offset += need_size;
654
0
            last_fl->size += need_more - need_size;
655
0
            assert(last_fl->offset == H5HL_ALIGN(last_fl->offset));
656
0
            assert(last_fl->size == H5HL_ALIGN(last_fl->size));
657
658
0
            if (last_fl->size < H5HL_SIZEOF_FREE(f))
659
0
                last_fl = H5HL__remove_free(heap, last_fl);
660
0
        }
661
0
        else {
662
            /* Create a new free list element large enough that we can
663
             * take some space out of it right away.
664
             */
665
0
            offset = old_dblk_size;
666
0
            if (need_more - need_size >= H5HL_SIZEOF_FREE(f)) {
667
0
                if (NULL == (fl = H5FL_MALLOC(H5HL_free_t)))
668
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTALLOC, FAIL, "memory allocation failed");
669
0
                fl->offset = old_dblk_size + need_size;
670
0
                fl->size   = need_more - need_size;
671
0
                assert(fl->offset == H5HL_ALIGN(fl->offset));
672
0
                assert(fl->size == H5HL_ALIGN(fl->size));
673
0
                fl->prev = NULL;
674
0
                fl->next = heap->freelist;
675
0
                if (heap->freelist)
676
0
                    heap->freelist->prev = fl;
677
0
                heap->freelist = fl;
678
0
            }
679
0
        }
680
681
0
        if (NULL == (heap->dblk_image = H5FL_BLK_REALLOC(lheap_chunk, heap->dblk_image, heap->dblk_size)))
682
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTALLOC, FAIL, "memory allocation failed");
683
684
        /* Clear new section so junk doesn't appear in the file */
685
        /* (Avoid clearing section which will be overwritten with newly inserted data) */
686
0
        memset(heap->dblk_image + offset + buf_size, 0, (new_dblk_size - (offset + buf_size)));
687
0
    }
688
689
    /* Copy the data into the heap */
690
0
    H5MM_memcpy(heap->dblk_image + offset, buf, buf_size);
691
692
0
    *offset_out = offset;
693
694
0
done:
695
0
    FUNC_LEAVE_NOAPI(ret_value)
696
0
} /* H5HL_insert() */
697
698
/*-------------------------------------------------------------------------
699
 * Function:    H5HL_remove
700
 *
701
 * Purpose:     Removes an object or part of an object from the heap at
702
 *              address ADDR of file F.  The object (or part) to remove
703
 *              begins at byte OFFSET from the beginning of the heap and
704
 *              continues for SIZE bytes.
705
 *
706
 *              Once part of an object is removed, one must not attempt
707
 *              to access that part.  Removing the beginning of an object
708
 *              results in the object OFFSET increasing by the amount
709
 *              truncated.  Removing the end of an object results in
710
 *              object truncation.  Removing the middle of an object results
711
 *              in two separate objects, one at the original offset and
712
 *              one at the first offset past the removed portion.
713
 *
714
 * Return:      SUCCEED/FAIL
715
 *
716
 *-------------------------------------------------------------------------
717
 */
718
herr_t
719
H5HL_remove(H5F_t *f, H5HL_t *heap, size_t offset, size_t size)
720
0
{
721
0
    H5HL_free_t *fl        = NULL;
722
0
    herr_t       ret_value = SUCCEED;
723
724
0
    FUNC_ENTER_NOAPI(FAIL)
725
726
    /* Check arguments */
727
0
    assert(f);
728
0
    assert(heap);
729
0
    assert(size > 0);
730
0
    assert(offset == H5HL_ALIGN(offset));
731
732
0
    size = H5HL_ALIGN(size);
733
734
0
    assert(offset < heap->dblk_size);
735
0
    assert(offset + size <= heap->dblk_size);
736
737
    /* Mark heap as dirty in cache
738
     *
739
     * (A bit early in the process, but it's difficult to determine in the
740
     *  code below where to mark the heap as dirty, especially in error cases,
741
     *  so we just accept that an extra flush of the heap info could occur
742
     *  if an error occurs -QAK)
743
     */
744
0
    if (FAIL == H5HL__dirty(heap))
745
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTMARKDIRTY, FAIL, "unable to mark heap as dirty");
746
747
    /* Check if this chunk can be prepended or appended to an already
748
     * free chunk.  It might also fall between two chunks in such a way
749
     * that all three chunks can be combined into one.
750
     */
751
0
    fl = heap->freelist;
752
0
    while (fl) {
753
0
        H5HL_free_t *fl2 = NULL;
754
755
0
        if ((offset + size) == fl->offset) {
756
0
            fl->offset = offset;
757
0
            fl->size += size;
758
0
            assert(fl->offset == H5HL_ALIGN(fl->offset));
759
0
            assert(fl->size == H5HL_ALIGN(fl->size));
760
0
            fl2 = fl->next;
761
0
            while (fl2) {
762
0
                if ((fl2->offset + fl2->size) == fl->offset) {
763
0
                    fl->offset = fl2->offset;
764
0
                    fl->size += fl2->size;
765
0
                    assert(fl->offset == H5HL_ALIGN(fl->offset));
766
0
                    assert(fl->size == H5HL_ALIGN(fl->size));
767
0
                    fl2 = H5HL__remove_free(heap, fl2);
768
0
                    if (((fl->offset + fl->size) == heap->dblk_size) && ((2 * fl->size) > heap->dblk_size)) {
769
0
                        if (FAIL == H5HL__minimize_heap_space(f, heap))
770
0
                            HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "heap size minimization failed");
771
0
                    }
772
0
                    HGOTO_DONE(SUCCEED);
773
0
                }
774
0
                fl2 = fl2->next;
775
0
            }
776
0
            if (((fl->offset + fl->size) == heap->dblk_size) && ((2 * fl->size) > heap->dblk_size)) {
777
0
                if (FAIL == H5HL__minimize_heap_space(f, heap))
778
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "heap size minimization failed");
779
0
            }
780
0
            HGOTO_DONE(SUCCEED);
781
0
        }
782
0
        else if (fl->offset + fl->size == offset) {
783
0
            fl->size += size;
784
0
            fl2 = fl->next;
785
0
            assert(fl->size == H5HL_ALIGN(fl->size));
786
0
            while (fl2) {
787
0
                if (fl->offset + fl->size == fl2->offset) {
788
0
                    fl->size += fl2->size;
789
0
                    assert(fl->size == H5HL_ALIGN(fl->size));
790
0
                    fl2 = H5HL__remove_free(heap, fl2);
791
0
                    if (((fl->offset + fl->size) == heap->dblk_size) && ((2 * fl->size) > heap->dblk_size)) {
792
0
                        if (FAIL == H5HL__minimize_heap_space(f, heap))
793
0
                            HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "heap size minimization failed");
794
0
                    }
795
0
                    HGOTO_DONE(SUCCEED);
796
0
                }
797
0
                fl2 = fl2->next;
798
0
            }
799
0
            if (((fl->offset + fl->size) == heap->dblk_size) && ((2 * fl->size) > heap->dblk_size)) {
800
0
                if (FAIL == H5HL__minimize_heap_space(f, heap))
801
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "heap size minimization failed");
802
0
            }
803
0
            HGOTO_DONE(SUCCEED);
804
0
        }
805
0
        fl = fl->next;
806
0
    }
807
808
    /*
809
     * The amount which is being removed must be large enough to
810
     * hold the free list data.  If not, the freed chunk is forever
811
     * lost.
812
     */
813
0
    if (size < H5HL_SIZEOF_FREE(f))
814
0
        HGOTO_DONE(SUCCEED);
815
816
    /* Add an entry to the free list */
817
0
    if (NULL == (fl = H5FL_MALLOC(H5HL_free_t)))
818
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTALLOC, FAIL, "memory allocation failed");
819
0
    fl->offset = offset;
820
0
    fl->size   = size;
821
0
    assert(fl->offset == H5HL_ALIGN(fl->offset));
822
0
    assert(fl->size == H5HL_ALIGN(fl->size));
823
0
    fl->prev = NULL;
824
0
    fl->next = heap->freelist;
825
0
    if (heap->freelist)
826
0
        heap->freelist->prev = fl;
827
0
    heap->freelist = fl;
828
829
0
    if (((fl->offset + fl->size) == heap->dblk_size) && ((2 * fl->size) > heap->dblk_size))
830
0
        if (FAIL == H5HL__minimize_heap_space(f, heap))
831
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "heap size minimization failed");
832
833
0
done:
834
0
    FUNC_LEAVE_NOAPI(ret_value)
835
0
} /* end H5HL_remove() */
836
837
/*-------------------------------------------------------------------------
838
 * Function:    H5HL_delete
839
 *
840
 * Purpose:     Deletes a local heap from disk, freeing disk space used.
841
 *
842
 * Return:      SUCCEED/FAIL
843
 *
844
 *-------------------------------------------------------------------------
845
 */
846
herr_t
847
H5HL_delete(H5F_t *f, haddr_t addr)
848
0
{
849
0
    H5HL_t              *heap = NULL;                      /* Local heap to delete */
850
0
    H5HL_cache_prfx_ud_t prfx_udata;                       /* User data for protecting local heap prefix */
851
0
    H5HL_prfx_t         *prfx        = NULL;               /* Local heap prefix */
852
0
    H5HL_dblk_t         *dblk        = NULL;               /* Local heap data block */
853
0
    unsigned             cache_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting heap */
854
0
    herr_t               ret_value   = SUCCEED;
855
856
0
    FUNC_ENTER_NOAPI(FAIL)
857
858
    /* Check arguments */
859
0
    assert(f);
860
0
    assert(H5_addr_defined(addr));
861
862
    /* Construct the user data for protect callback */
863
0
    prfx_udata.sizeof_size = H5F_SIZEOF_SIZE(f);
864
0
    prfx_udata.sizeof_addr = H5F_SIZEOF_ADDR(f);
865
0
    prfx_udata.prfx_addr   = addr;
866
0
    prfx_udata.sizeof_prfx = H5HL_SIZEOF_HDR(f);
867
868
    /* Protect the local heap prefix */
869
0
    if (NULL ==
870
0
        (prfx = (H5HL_prfx_t *)H5AC_protect(f, H5AC_LHEAP_PRFX, addr, &prfx_udata, H5AC__NO_FLAGS_SET)))
871
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to load heap prefix");
872
873
    /* Get the pointer to the heap */
874
0
    heap = prfx->heap;
875
876
    /* Check if heap has separate data block */
877
0
    if (!heap->single_cache_obj)
878
        /* Protect the local heap data block */
879
0
        if (NULL == (dblk = (H5HL_dblk_t *)H5AC_protect(f, H5AC_LHEAP_DBLK, heap->dblk_addr, heap,
880
0
                                                        H5AC__NO_FLAGS_SET)))
881
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to load heap data block");
882
883
    /* Set the flags for releasing the prefix and data block */
884
0
    cache_flags |= H5AC__DIRTIED_FLAG | H5AC__DELETED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;
885
886
0
done:
887
    /* Release the data block from the cache, now deleted */
888
0
    if (dblk && heap && H5AC_unprotect(f, H5AC_LHEAP_DBLK, heap->dblk_addr, dblk, cache_flags) < 0)
889
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release local heap data block");
890
891
    /* Release the prefix from the cache, now deleted */
892
0
    if (prfx && heap && H5AC_unprotect(f, H5AC_LHEAP_PRFX, heap->prfx_addr, prfx, cache_flags) < 0)
893
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release local heap prefix");
894
895
0
    FUNC_LEAVE_NOAPI(ret_value)
896
0
} /* end H5HL_delete() */
897
898
/*-------------------------------------------------------------------------
899
 * Function:    H5HL_heap_get_size
900
 *
901
 * Purpose:     Retrieves the current size of a heap's block
902
 *
903
 * Return:      SUCCEED/FAIL
904
 *
905
 *-------------------------------------------------------------------------
906
 */
907
size_t
908
H5HL_heap_get_size(const H5HL_t *heap)
909
1.28k
{
910
1.28k
    FUNC_ENTER_NOAPI_NOINIT_NOERR
911
912
    /* Check arguments */
913
1.28k
    assert(heap);
914
915
1.28k
    FUNC_LEAVE_NOAPI(heap->dblk_size)
916
1.28k
} /* end H5HL_heap_get_size() */
917
918
/*-------------------------------------------------------------------------
919
 * Function:    H5HL_get_size
920
 *
921
 * Purpose:     Retrieves the current size of a heap
922
 *
923
 * Return:      SUCCEED/FAIL
924
 *
925
 *-------------------------------------------------------------------------
926
 */
927
herr_t
928
H5HL_get_size(H5F_t *f, haddr_t addr, size_t *size)
929
0
{
930
0
    H5HL_cache_prfx_ud_t prfx_udata;       /* User data for protecting local heap prefix */
931
0
    H5HL_prfx_t         *prfx      = NULL; /* Local heap prefix */
932
0
    H5HL_t              *heap      = NULL; /* Heap data structure */
933
0
    herr_t               ret_value = SUCCEED;
934
935
0
    FUNC_ENTER_NOAPI(FAIL)
936
937
    /* Check arguments */
938
0
    assert(f);
939
0
    assert(H5_addr_defined(addr));
940
0
    assert(size);
941
942
    /* Construct the user data for protect callback */
943
0
    prfx_udata.sizeof_size = H5F_SIZEOF_SIZE(f);
944
0
    prfx_udata.sizeof_addr = H5F_SIZEOF_ADDR(f);
945
0
    prfx_udata.prfx_addr   = addr;
946
0
    prfx_udata.sizeof_prfx = H5HL_SIZEOF_HDR(f);
947
948
    /* Protect the local heap prefix */
949
0
    if (NULL ==
950
0
        (prfx = (H5HL_prfx_t *)H5AC_protect(f, H5AC_LHEAP_PRFX, addr, &prfx_udata, H5AC__READ_ONLY_FLAG)))
951
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to load heap prefix");
952
953
    /* Get the pointer to the heap */
954
0
    heap = prfx->heap;
955
956
    /* Set the size to return */
957
0
    *size = heap->dblk_size;
958
959
0
done:
960
0
    if (prfx && FAIL == H5AC_unprotect(f, H5AC_LHEAP_PRFX, heap->prfx_addr, prfx, H5AC__NO_FLAGS_SET))
961
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release local heap prefix");
962
963
0
    FUNC_LEAVE_NOAPI(ret_value)
964
0
} /* end H5HL_get_size() */
965
966
/*-------------------------------------------------------------------------
967
 * Function:    H5HL_heapsize
968
 *
969
 * Purpose:     Compute the size in bytes of the specified instance of
970
 *              H5HL_t via H5HL_size()
971
 *
972
 * Return:      SUCCEED/FAIL
973
 *
974
 *-------------------------------------------------------------------------
975
 */
976
herr_t
977
H5HL_heapsize(H5F_t *f, haddr_t addr, hsize_t *heap_size)
978
0
{
979
0
    H5HL_cache_prfx_ud_t prfx_udata;       /* User data for protecting local heap prefix */
980
0
    H5HL_prfx_t         *prfx      = NULL; /* Local heap prefix */
981
0
    H5HL_t              *heap      = NULL; /* Heap data structure */
982
0
    herr_t               ret_value = SUCCEED;
983
984
0
    FUNC_ENTER_NOAPI(FAIL)
985
986
    /* Check arguments */
987
0
    assert(f);
988
0
    assert(H5_addr_defined(addr));
989
0
    assert(heap_size);
990
991
    /* Construct the user data for protect callback */
992
0
    prfx_udata.sizeof_size = H5F_SIZEOF_SIZE(f);
993
0
    prfx_udata.sizeof_addr = H5F_SIZEOF_ADDR(f);
994
0
    prfx_udata.prfx_addr   = addr;
995
0
    prfx_udata.sizeof_prfx = H5HL_SIZEOF_HDR(f);
996
997
    /* Protect the local heap prefix */
998
0
    if (NULL ==
999
0
        (prfx = (H5HL_prfx_t *)H5AC_protect(f, H5AC_LHEAP_PRFX, addr, &prfx_udata, H5AC__READ_ONLY_FLAG)))
1000
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to load heap prefix");
1001
1002
    /* Get the pointer to the heap */
1003
0
    heap = prfx->heap;
1004
1005
    /* Accumulate the size of the local heap */
1006
0
    *heap_size += (hsize_t)(heap->prfx_size + heap->dblk_size);
1007
1008
0
done:
1009
0
    if (prfx && FAIL == H5AC_unprotect(f, H5AC_LHEAP_PRFX, heap->prfx_addr, prfx, H5AC__NO_FLAGS_SET))
1010
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release local heap prefix");
1011
1012
0
    FUNC_LEAVE_NOAPI(ret_value)
1013
0
} /* end H5HL_heapsize() */