Coverage Report

Created: 2026-01-10 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hdf5/src/H5HG.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
 * Purpose: Operations on the global heap.  The global heap is the set of
15
 *    all collections and each collection contains one or more
16
 *    global heap objects.  An object belongs to exactly one
17
 *    collection.  A collection is treated as an atomic entity for
18
 *    the purposes of I/O and caching.
19
 *
20
 *    Each file has a small cache of global heap collections called
21
 *    the CWFS list and recently accessed collections with free
22
 *    space appear on this list.  As collections are accessed the
23
 *    collection is moved toward the front of the list.  New
24
 *    collections are added to the front of the list while old
25
 *    collections are added to the end of the list.
26
 *
27
 *    The collection model reduces the overhead which would be
28
 *    incurred if the global heap were a single object, and the
29
 *    CWFS list allows the library to cheaply choose a collection
30
 *    for a new object based on object size, amount of free space
31
 *    in the collection, and temporal locality.
32
 */
33
34
/****************/
35
/* Module Setup */
36
/****************/
37
38
#include "H5HGmodule.h" /* This source code file is part of the H5HG module */
39
40
/***********/
41
/* Headers */
42
/***********/
43
#include "H5private.h"   /* Generic Functions     */
44
#include "H5Eprivate.h"  /* Error handling        */
45
#include "H5Fprivate.h"  /* File access       */
46
#include "H5FLprivate.h" /* Free Lists                               */
47
#include "H5HGpkg.h"     /* Global heaps        */
48
#include "H5MFprivate.h" /* File memory management    */
49
#include "H5MMprivate.h" /* Memory management     */
50
51
/****************/
52
/* Local Macros */
53
/****************/
54
55
/*
56
 * The maximum number of links allowed to a global heap object.
57
 */
58
0
#define H5HG_MAXLINK 65535
59
60
/*
61
 * The maximum number of indices allowed in a global heap object.
62
 */
63
0
#define H5HG_MAXIDX 65535
64
65
/******************/
66
/* Local Typedefs */
67
/******************/
68
69
/********************/
70
/* Package Typedefs */
71
/********************/
72
73
/********************/
74
/* Local Prototypes */
75
/********************/
76
77
static haddr_t H5HG__create(H5F_t *f, size_t size);
78
static size_t  H5HG__alloc(H5F_t *f, H5HG_heap_t *heap, size_t size, unsigned *heap_flags_ptr);
79
80
/*********************/
81
/* Package Variables */
82
/*********************/
83
84
/* Package initialization variable */
85
bool H5_PKG_INIT_VAR = false;
86
87
/* Declare a free list to manage the H5HG_heap_t struct */
88
H5FL_DEFINE(H5HG_heap_t);
89
90
/* Declare a free list to manage sequences of H5HG_obj_t's */
91
H5FL_SEQ_DEFINE(H5HG_obj_t);
92
93
/* Declare a PQ free list to manage heap chunks */
94
H5FL_BLK_DEFINE(gheap_chunk);
95
96
/*****************************/
97
/* Library Private Variables */
98
/*****************************/
99
100
/*******************/
101
/* Local Variables */
102
/*******************/
103
104
/*-------------------------------------------------------------------------
105
 * Function:  H5HG__create
106
 *
107
 * Purpose: Creates a global heap collection of the specified size.  If
108
 *    SIZE is less than some minimum it will be readjusted.  The
109
 *    new collection is allocated in the file and added to the
110
 *    beginning of the CWFS list.
111
 *
112
 * Return:  Success:  Ptr to a cached heap.  The pointer is valid
113
 *        only until some other hdf5 library function
114
 *        is called.
115
 *
116
 *    Failure:  NULL
117
 *
118
 *-------------------------------------------------------------------------
119
 */
120
static haddr_t
121
H5HG__create(H5F_t *f, size_t size)
122
0
{
123
0
    H5HG_heap_t *heap = NULL;
124
0
    uint8_t     *p    = NULL;
125
0
    haddr_t      addr = HADDR_UNDEF;
126
0
    size_t       n;
127
0
    haddr_t      ret_value = HADDR_UNDEF; /* Return value */
128
129
0
    FUNC_ENTER_PACKAGE
130
131
    /* Check args */
132
0
    assert(f);
133
0
    if (size < H5HG_MINSIZE)
134
0
        size = H5HG_MINSIZE;
135
0
    size = H5HG_ALIGN(size);
136
137
    /* Create it */
138
0
    H5_CHECK_OVERFLOW(size, size_t, hsize_t);
139
0
    if (HADDR_UNDEF == (addr = H5MF_alloc(f, H5FD_MEM_GHEAP, (hsize_t)size)))
140
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTINIT, HADDR_UNDEF, "unable to allocate file space for global heap");
141
0
    if (NULL == (heap = H5FL_CALLOC(H5HG_heap_t)))
142
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, HADDR_UNDEF, "memory allocation failed");
143
0
    heap->addr   = addr;
144
0
    heap->size   = size;
145
0
    heap->shared = H5F_SHARED(f);
146
147
0
    if (NULL == (heap->chunk = H5FL_BLK_MALLOC(gheap_chunk, size)))
148
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, HADDR_UNDEF, "memory allocation failed");
149
0
    memset(heap->chunk, 0, size);
150
0
    heap->nalloc = H5HG_NOBJS(f, size);
151
0
    heap->nused  = 1; /* account for index 0, which is used for the free object */
152
0
    if (NULL == (heap->obj = H5FL_SEQ_MALLOC(H5HG_obj_t, heap->nalloc)))
153
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, HADDR_UNDEF, "memory allocation failed");
154
155
    /* Initialize the header */
156
0
    H5MM_memcpy(heap->chunk, H5HG_MAGIC, (size_t)H5_SIZEOF_MAGIC);
157
0
    p    = heap->chunk + H5_SIZEOF_MAGIC;
158
0
    *p++ = H5HG_VERSION;
159
0
    *p++ = 0; /*reserved*/
160
0
    *p++ = 0; /*reserved*/
161
0
    *p++ = 0; /*reserved*/
162
0
    H5F_ENCODE_LENGTH(f, p, size);
163
164
    /*
165
     * Padding so free space object is aligned. If malloc returned memory
166
     * which was always at least H5HG_ALIGNMENT aligned then we could just
167
     * align the pointer, but this might not be the case.
168
     */
169
0
    n = (size_t)H5HG_ALIGN(p - heap->chunk) - (size_t)(p - heap->chunk);
170
0
    p += n;
171
172
    /* The freespace object */
173
0
    heap->obj[0].size = size - H5HG_SIZEOF_HDR(f);
174
0
    assert(H5HG_ISALIGNED(heap->obj[0].size));
175
0
    heap->obj[0].nrefs = 0;
176
0
    heap->obj[0].begin = p;
177
0
    UINT16ENCODE(p, 0); /*object ID*/
178
0
    UINT16ENCODE(p, 0); /*reference count*/
179
0
    UINT32ENCODE(p, 0); /*reserved*/
180
0
    H5F_ENCODE_LENGTH(f, p, heap->obj[0].size);
181
182
    /* Add this heap to the beginning of the CWFS list */
183
0
    if (H5F_cwfs_add(f, heap) < 0)
184
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTINIT, HADDR_UNDEF,
185
0
                    "unable to add global heap collection to file's CWFS");
186
187
    /* Add the heap to the cache */
188
0
    if (H5AC_insert_entry(f, H5AC_GHEAP, addr, heap, H5AC__NO_FLAGS_SET) < 0)
189
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTINIT, HADDR_UNDEF, "unable to cache global heap collection");
190
191
0
    ret_value = addr;
192
193
0
done:
194
    /* Cleanup on error */
195
0
    if (!H5_addr_defined(ret_value)) {
196
0
        if (H5_addr_defined(addr)) {
197
            /* Release the space on disk */
198
0
            if (H5MF_xfree(f, H5FD_MEM_GHEAP, addr, (hsize_t)size) < 0)
199
0
                HDONE_ERROR(H5E_BTREE, H5E_CANTFREE, HADDR_UNDEF, "unable to free global heap");
200
201
            /* Check if the heap object was allocated */
202
0
            if (heap)
203
                /* Destroy the heap object */
204
0
                if (H5HG__free(heap) < 0)
205
0
                    HDONE_ERROR(H5E_HEAP, H5E_CANTFREE, HADDR_UNDEF,
206
0
                                "unable to destroy global heap collection");
207
0
        } /* end if */
208
0
    }     /* end if */
209
210
0
    FUNC_LEAVE_NOAPI(ret_value)
211
0
} /* H5HG__create() */
212
213
/*-------------------------------------------------------------------------
214
 * Function:  H5HG__protect
215
 *
216
 * Purpose: Convenience wrapper around H5AC_protect on an indirect block
217
 *
218
 * Return:  Pointer to indirect block on success, NULL on failure
219
 *
220
 *-------------------------------------------------------------------------
221
 */
222
H5HG_heap_t *
223
H5HG__protect(H5F_t *f, haddr_t addr, unsigned flags)
224
0
{
225
0
    H5HG_heap_t *heap;             /* Global heap */
226
0
    H5HG_heap_t *ret_value = NULL; /* Return value */
227
228
0
    FUNC_ENTER_PACKAGE
229
230
    /* Check arguments */
231
0
    assert(f);
232
0
    assert(H5_addr_defined(addr));
233
234
    /* only H5AC__READ_ONLY_FLAG may appear in flags */
235
0
    assert((flags & (unsigned)(~H5AC__READ_ONLY_FLAG)) == 0);
236
237
    /* Lock the heap into memory */
238
0
    if (NULL == (heap = (H5HG_heap_t *)H5AC_protect(f, H5AC_GHEAP, addr, f, flags)))
239
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, NULL, "unable to protect global heap");
240
241
    /* Set the heap's address */
242
0
    heap->addr = addr;
243
244
    /* Set the return value */
245
0
    ret_value = heap;
246
247
0
done:
248
0
    FUNC_LEAVE_NOAPI(ret_value)
249
0
} /* H5HG__protect() */
250
251
/*-------------------------------------------------------------------------
252
 * Function:  H5HG__alloc
253
 *
254
 * Purpose: Given a heap with enough free space, this function will split
255
 *    the free space to make a new empty heap object and initialize
256
 *    the header.  SIZE is the exact size of the object data to be
257
 *    stored. It will be increased to make room for the object
258
 *    header and then rounded up for alignment.
259
 *
260
 * Return:  Success:  The heap object ID of the new object.
261
 *
262
 *    Failure:  0
263
 *
264
 *-------------------------------------------------------------------------
265
 */
266
static size_t
267
H5HG__alloc(H5F_t *f, H5HG_heap_t *heap, size_t size, unsigned *heap_flags_ptr)
268
0
{
269
0
    size_t   idx;
270
0
    uint8_t *p;
271
0
    size_t   need      = H5HG_SIZEOF_OBJHDR(f) + H5HG_ALIGN(size);
272
0
    size_t   ret_value = 0; /* Return value */
273
274
0
    FUNC_ENTER_PACKAGE
275
276
    /* Check args */
277
0
    assert(heap);
278
0
    assert(heap->obj[0].size >= need);
279
0
    assert(heap_flags_ptr);
280
281
    /*
282
     * Find an ID for the new object. ID zero is reserved for the free space
283
     * object.
284
     */
285
0
    if (heap->nused <= H5HG_MAXIDX)
286
0
        idx = heap->nused++;
287
0
    else {
288
0
        for (idx = 1; idx < heap->nused; idx++)
289
0
            if (NULL == heap->obj[idx].begin)
290
0
                break;
291
0
    } /* end else */
292
293
0
    assert(idx < heap->nused);
294
295
    /* Check if we need more room to store heap objects */
296
0
    if (idx >= heap->nalloc) {
297
0
        size_t      new_alloc; /* New allocation number */
298
0
        H5HG_obj_t *new_obj;   /* New array of object descriptions */
299
300
        /* Determine the new number of objects to index */
301
        /* nalloc is *not* guaranteed to be a power of 2! - NAF 10/26/09 */
302
0
        new_alloc = MIN(MAX(heap->nalloc * 2, (idx + 1)), (H5HG_MAXIDX + 1));
303
0
        assert(idx < new_alloc);
304
305
        /* Reallocate array of objects */
306
0
        if (NULL == (new_obj = H5FL_SEQ_REALLOC(H5HG_obj_t, heap->obj, new_alloc)))
307
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTALLOC, 0, "memory allocation failed");
308
309
        /* Clear newly allocated space */
310
0
        memset(&new_obj[heap->nalloc], 0, (new_alloc - heap->nalloc) * sizeof(heap->obj[0]));
311
312
        /* Update heap information */
313
0
        heap->nalloc = new_alloc;
314
0
        heap->obj    = new_obj;
315
0
        assert(heap->nalloc > heap->nused);
316
0
    } /* end if */
317
318
    /* Initialize the new object */
319
0
    heap->obj[idx].nrefs = 0;
320
0
    heap->obj[idx].size  = size;
321
0
    heap->obj[idx].begin = heap->obj[0].begin;
322
0
    p                    = heap->obj[idx].begin;
323
0
    UINT16ENCODE(p, idx);
324
0
    UINT16ENCODE(p, 0); /*nrefs*/
325
0
    UINT32ENCODE(p, 0); /*reserved*/
326
0
    H5F_ENCODE_LENGTH(f, p, size);
327
328
    /* Fix the free space object */
329
0
    if (need == heap->obj[0].size) {
330
        /*
331
         * All free space has been exhausted from this collection.
332
         */
333
0
        heap->obj[0].size  = 0;
334
0
        heap->obj[0].begin = NULL;
335
0
    } /* end if */
336
0
    else if (heap->obj[0].size - need >= H5HG_SIZEOF_OBJHDR(f)) {
337
        /*
338
         * Some free space remains and it's larger than a heap object header,
339
         * so write the new free heap object header to the heap.
340
         */
341
0
        heap->obj[0].size -= need;
342
0
        heap->obj[0].begin += need;
343
0
        p = heap->obj[0].begin;
344
0
        UINT16ENCODE(p, 0); /*id*/
345
0
        UINT16ENCODE(p, 0); /*nrefs*/
346
0
        UINT32ENCODE(p, 0); /*reserved*/
347
0
        H5F_ENCODE_LENGTH(f, p, heap->obj[0].size);
348
0
        assert(H5HG_ISALIGNED(heap->obj[0].size));
349
0
    } /* end else-if */
350
0
    else {
351
        /*
352
         * Some free space remains but it's smaller than a heap object header,
353
         * so we don't write the header.
354
         */
355
0
        heap->obj[0].size -= need;
356
0
        heap->obj[0].begin += need;
357
0
        assert(H5HG_ISALIGNED(heap->obj[0].size));
358
0
    }
359
360
    /* Mark the heap as dirty */
361
0
    *heap_flags_ptr |= H5AC__DIRTIED_FLAG;
362
363
    /* Set the return value */
364
0
    ret_value = idx;
365
366
0
done:
367
0
    FUNC_LEAVE_NOAPI(ret_value)
368
0
} /* end H5HG__alloc() */
369
370
/*-------------------------------------------------------------------------
371
 * Function:  H5HG_extend
372
 *
373
 * Purpose: Extend a heap to hold an object of SIZE bytes.
374
 *    SIZE is the exact size of the object data to be
375
 *    stored. It will be increased to make room for the object
376
 *    header and then rounded up for alignment.
377
 *
378
 * Return:  Success:  Non-negative
379
 *
380
 *    Failure:  Negative
381
 *
382
 *-------------------------------------------------------------------------
383
 */
384
herr_t
385
H5HG_extend(H5F_t *f, haddr_t addr, size_t need)
386
0
{
387
0
    H5HG_heap_t *heap       = NULL;               /* Pointer to heap to extend */
388
0
    unsigned     heap_flags = H5AC__NO_FLAGS_SET; /* Flags to unprotecting heap */
389
0
    size_t       old_size;                        /* Previous size of the heap's chunk */
390
0
    uint8_t     *new_chunk;                       /* Pointer to new chunk information */
391
0
    uint8_t     *p;                               /* Pointer to raw heap info */
392
0
    unsigned     u;                               /* Local index variable */
393
0
    herr_t       ret_value = SUCCEED;             /* Return value */
394
395
0
    FUNC_ENTER_NOAPI_NOINIT
396
397
    /* Check args */
398
0
    assert(f);
399
0
    assert(H5_addr_defined(addr));
400
401
    /* Protect the heap */
402
0
    if (NULL == (heap = H5HG__protect(f, addr, H5AC__NO_FLAGS_SET)))
403
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to protect global heap");
404
405
    /* Re-allocate the heap information in memory */
406
0
    if (NULL == (new_chunk = H5FL_BLK_REALLOC(gheap_chunk, heap->chunk, (heap->size + need))))
407
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "new heap allocation failed");
408
0
    memset(new_chunk + heap->size, 0, need);
409
410
    /* Adjust the size of the heap */
411
0
    old_size = heap->size;
412
0
    heap->size += need;
413
414
    /* Encode the new size of the heap */
415
0
    p = new_chunk + H5_SIZEOF_MAGIC + 1 /* version */ + 3 /* reserved */;
416
0
    H5F_ENCODE_LENGTH(f, p, heap->size);
417
418
    /* Move the pointers to the existing objects to their new locations */
419
0
    for (u = 0; u < heap->nused; u++)
420
0
        if (heap->obj[u].begin)
421
0
            heap->obj[u].begin = new_chunk + (heap->obj[u].begin - heap->chunk);
422
423
    /* Update the heap chunk pointer now */
424
0
    heap->chunk = new_chunk;
425
426
    /* Update the free space information for the heap  */
427
0
    heap->obj[0].size += need;
428
0
    if (heap->obj[0].begin == NULL)
429
0
        heap->obj[0].begin = heap->chunk + old_size;
430
0
    p = heap->obj[0].begin;
431
0
    UINT16ENCODE(p, 0); /*id*/
432
0
    UINT16ENCODE(p, 0); /*nrefs*/
433
0
    UINT32ENCODE(p, 0); /*reserved*/
434
0
    H5F_ENCODE_LENGTH(f, p, heap->obj[0].size);
435
0
    assert(H5HG_ISALIGNED(heap->obj[0].size));
436
437
    /* Resize the heap in the cache */
438
0
    if (H5AC_resize_entry(heap, heap->size) < 0)
439
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTRESIZE, FAIL, "unable to resize global heap in cache");
440
441
    /* Mark the heap as dirty */
442
0
    heap_flags |= H5AC__DIRTIED_FLAG;
443
444
0
done:
445
0
    if (heap && H5AC_unprotect(f, H5AC_GHEAP, heap->addr, heap, heap_flags) < 0)
446
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to unprotect heap");
447
448
0
    FUNC_LEAVE_NOAPI(ret_value)
449
0
} /* end H5HG_extend() */
450
451
/*-------------------------------------------------------------------------
452
 * Function:  H5HG_insert
453
 *
454
 * Purpose: A new object is inserted into the global heap.  It will be
455
 *    placed in the first collection on the CWFS list which has
456
 *    enough free space and that collection will be advanced one
457
 *    position in the list.  If no collection on the CWFS list has
458
 *    enough space then  a new collection will be created.
459
 *
460
 *    It is legal to push a zero-byte object onto the heap to get
461
 *    the reference count features of heap objects.
462
 *
463
 * Return:  Success:  Non-negative, and a heap object handle returned
464
 *        through the HOBJ pointer.
465
 *
466
 *    Failure:  Negative
467
 *
468
 *-------------------------------------------------------------------------
469
 */
470
herr_t
471
H5HG_insert(H5F_t *f, size_t size, const void *obj, H5HG_t *hobj /*out*/)
472
0
{
473
0
    size_t       need; /*total space needed for object    */
474
0
    size_t       idx;
475
0
    haddr_t      addr; /* Address of heap to add object within */
476
0
    H5HG_heap_t *heap       = NULL;
477
0
    unsigned     heap_flags = H5AC__NO_FLAGS_SET;
478
0
    herr_t       ret_value  = SUCCEED; /* Return value */
479
480
0
    FUNC_ENTER_NOAPI_TAG(H5AC__GLOBALHEAP_TAG, FAIL)
481
482
    /* Check args */
483
0
    assert(f);
484
0
    assert(0 == size || obj);
485
0
    assert(hobj);
486
487
0
    if (0 == (H5F_INTENT(f) & H5F_ACC_RDWR))
488
0
        HGOTO_ERROR(H5E_HEAP, H5E_WRITEERROR, FAIL, "no write intent on file");
489
490
    /* Find a large enough collection on the CWFS list */
491
0
    need = H5HG_SIZEOF_OBJHDR(f) + H5HG_ALIGN(size);
492
493
    /* Look for a heap in the file's CWFS that has enough space for the object */
494
0
    addr = HADDR_UNDEF;
495
0
    if (H5F_cwfs_find_free_heap(f, need, &addr) < 0)
496
0
        HGOTO_ERROR(H5E_HEAP, H5E_NOTFOUND, FAIL, "error trying to locate heap");
497
498
    /*
499
     * If we didn't find any collection with enough free space then allocate a
500
     * new collection large enough for the message plus the collection header.
501
     */
502
0
    if (!H5_addr_defined(addr)) {
503
0
        addr = H5HG__create(f, need + H5HG_SIZEOF_HDR(f));
504
505
0
        if (!H5_addr_defined(addr))
506
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTINIT, FAIL, "unable to allocate a global heap collection");
507
0
    } /* end if */
508
0
    assert(H5_addr_defined(addr));
509
510
0
    if (NULL == (heap = H5HG__protect(f, addr, H5AC__NO_FLAGS_SET)))
511
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to protect global heap");
512
513
    /* Split the free space to make room for the new object */
514
0
    if (0 == (idx = H5HG__alloc(f, heap, size, &heap_flags)))
515
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTALLOC, FAIL, "unable to allocate global heap object");
516
517
    /* Copy data into the heap */
518
0
    if (size > 0)
519
0
        H5MM_memcpy(heap->obj[idx].begin + H5HG_SIZEOF_OBJHDR(f), obj, size);
520
0
    heap_flags |= H5AC__DIRTIED_FLAG;
521
522
    /* Return value */
523
0
    hobj->addr = heap->addr;
524
0
    hobj->idx  = idx;
525
526
0
done:
527
0
    if (heap && H5AC_unprotect(f, H5AC_GHEAP, heap->addr, heap, heap_flags) < 0)
528
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to unprotect heap.");
529
530
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
531
0
} /* H5HG_insert() */
532
533
/*-------------------------------------------------------------------------
534
 * Function:  H5HG_read
535
 *
536
 * Purpose: Reads the specified global heap object into the buffer OBJECT
537
 *    supplied by the caller.  If the caller doesn't supply a
538
 *    buffer then one will be allocated.  The buffer should be
539
 *    large enough to hold the result.
540
 *
541
 * Return:  Success:  The buffer containing the result.
542
 *
543
 *    Failure:  NULL
544
 *
545
 *-------------------------------------------------------------------------
546
 */
547
void *
548
H5HG_read(H5F_t *f, H5HG_t *hobj, void *object /*out*/, size_t *buf_size)
549
0
{
550
0
    H5HG_heap_t *heap = NULL;          /* Pointer to global heap object */
551
0
    size_t       size;                 /* Size of the heap object */
552
0
    uint8_t     *p;                    /* Pointer to object in heap buffer */
553
0
    void        *orig_object = object; /* Keep a copy of the original object pointer */
554
0
    void        *ret_value   = NULL;   /* Return value */
555
556
0
    FUNC_ENTER_NOAPI_TAG(H5AC__GLOBALHEAP_TAG, NULL)
557
558
    /* Check args */
559
0
    assert(f);
560
0
    assert(hobj);
561
562
    /* Heap object idx 0 is the free space in the heap and should never be given out */
563
0
    if (0 == hobj->idx)
564
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, NULL, "bad heap index, heap object = {%" PRIxHADDR ", %zu}",
565
0
                    hobj->addr, hobj->idx);
566
567
    /* Load the heap */
568
0
    if (NULL == (heap = H5HG__protect(f, hobj->addr, H5AC__READ_ONLY_FLAG)))
569
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, NULL, "unable to protect global heap");
570
0
    if (hobj->idx >= heap->nused)
571
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, NULL, "bad heap index, heap object = {%" PRIxHADDR ", %zu}",
572
0
                    hobj->addr, hobj->idx);
573
0
    if (NULL == heap->obj[hobj->idx].begin)
574
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, NULL, "bad heap pointer, heap object = {%" PRIxHADDR ", %zu}",
575
0
                    hobj->addr, hobj->idx);
576
577
0
    size = heap->obj[hobj->idx].size;
578
0
    p    = heap->obj[hobj->idx].begin + H5HG_SIZEOF_OBJHDR(f);
579
580
    /* Allocate a buffer for the object read in, if the user didn't give one */
581
0
    if (!object && NULL == (object = H5MM_malloc(size)))
582
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
583
0
    H5MM_memcpy(object, p, size);
584
585
    /*
586
     * Advance the heap in the CWFS list. We might have done this already
587
     * with the H5AC_protect(), but it won't hurt to do it twice.
588
     */
589
0
    if (heap->obj[0].begin) {
590
0
        if (H5F_cwfs_advance_heap(f, heap, false) < 0)
591
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTMODIFY, NULL, "can't adjust file's CWFS");
592
0
    } /* end if */
593
594
    /* If the caller would like to know the heap object's size, set that */
595
0
    if (buf_size)
596
0
        *buf_size = size;
597
598
    /* Set return value */
599
0
    ret_value = object;
600
601
0
done:
602
0
    if (heap && H5AC_unprotect(f, H5AC_GHEAP, hobj->addr, heap, H5AC__NO_FLAGS_SET) < 0)
603
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, NULL, "unable to release object header");
604
605
0
    if (NULL == ret_value && NULL == orig_object && object)
606
0
        H5MM_free(object);
607
608
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
609
0
} /* end H5HG_read() */
610
611
/*-------------------------------------------------------------------------
612
 * Function:  H5HG_link
613
 *
614
 * Purpose: Adjusts the link count for a global heap object by adding
615
 *    ADJUST to the current value.  This function will fail if the
616
 *    new link count would overflow.  Nothing special happens when
617
 *    the link count reaches zero; in order for a heap object to be
618
 *    removed one must call H5HG_remove().
619
 *
620
 * Return:  Success:  Number of links present after the adjustment.
621
 *
622
 *    Failure:  Negative
623
 *
624
 *-------------------------------------------------------------------------
625
 */
626
int
627
H5HG_link(H5F_t *f, const H5HG_t *hobj, int adjust)
628
0
{
629
0
    H5HG_heap_t *heap       = NULL;
630
0
    unsigned     heap_flags = H5AC__NO_FLAGS_SET;
631
0
    int          ret_value  = -1; /* Return value */
632
633
0
    FUNC_ENTER_NOAPI_TAG(H5AC__GLOBALHEAP_TAG, FAIL)
634
635
    /* Check args */
636
0
    assert(f);
637
0
    assert(hobj);
638
0
    if (0 == (H5F_INTENT(f) & H5F_ACC_RDWR))
639
0
        HGOTO_ERROR(H5E_HEAP, H5E_WRITEERROR, FAIL, "no write intent on file");
640
641
    /* Heap object idx 0 is the free space in the heap and should never be given out */
642
0
    if (0 == hobj->idx)
643
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "bad heap index, heap object = {%" PRIxHADDR ", %zu}",
644
0
                    hobj->addr, hobj->idx);
645
646
    /* Load the heap */
647
0
    if (NULL == (heap = H5HG__protect(f, hobj->addr, H5AC__NO_FLAGS_SET)))
648
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to protect global heap");
649
650
0
    if (adjust != 0) {
651
0
        if (hobj->idx >= heap->nused)
652
0
            HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "bad heap index, heap object = {%" PRIxHADDR ", %zu}",
653
0
                        hobj->addr, hobj->idx);
654
0
        if (NULL == heap->obj[hobj->idx].begin)
655
0
            HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "bad heap pointer, heap object = {%" PRIxHADDR ", %zu}",
656
0
                        hobj->addr, hobj->idx);
657
0
        if ((heap->obj[hobj->idx].nrefs + adjust) < 0)
658
0
            HGOTO_ERROR(H5E_HEAP, H5E_BADRANGE, FAIL, "new link count would be out of range");
659
0
        if ((heap->obj[hobj->idx].nrefs + adjust) > H5HG_MAXLINK)
660
0
            HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "new link count would be out of range");
661
0
        heap->obj[hobj->idx].nrefs += adjust;
662
0
        heap_flags |= H5AC__DIRTIED_FLAG;
663
0
    } /* end if */
664
665
    /* Set return value */
666
0
    ret_value = heap->obj[hobj->idx].nrefs;
667
668
0
done:
669
0
    if (heap && H5AC_unprotect(f, H5AC_GHEAP, hobj->addr, heap, heap_flags) < 0)
670
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release object header");
671
672
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
673
0
} /* end H5HG_link() */
674
675
/*-------------------------------------------------------------------------
676
 * Function:    H5HG_get_obj_size
677
 *
678
 * Purpose:     Returns the size of a global heap object.
679
 * Return:      Success:        Non-negative
680
 *
681
 *              Failure:        Negative
682
 *
683
 *-------------------------------------------------------------------------
684
 */
685
herr_t
686
H5HG_get_obj_size(H5F_t *f, H5HG_t *hobj, size_t *obj_size)
687
0
{
688
0
    H5HG_heap_t *heap      = NULL;    /* Pointer to global heap object */
689
0
    herr_t       ret_value = SUCCEED; /* Return value */
690
691
0
    FUNC_ENTER_NOAPI_TAG(H5AC__GLOBALHEAP_TAG, FAIL)
692
693
    /* Check args */
694
0
    assert(f);
695
0
    assert(hobj);
696
0
    assert(obj_size);
697
698
    /* Heap object idx 0 is the free space in the heap and should never be given out */
699
0
    if (0 == hobj->idx)
700
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "bad heap index, heap object = {%" PRIxHADDR ", %zu}",
701
0
                    hobj->addr, hobj->idx);
702
703
    /* Load the heap */
704
0
    if (NULL == (heap = H5HG__protect(f, hobj->addr, H5AC__READ_ONLY_FLAG)))
705
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to protect global heap");
706
707
    /* Sanity check the heap object */
708
0
    if (hobj->idx >= heap->nused)
709
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "bad heap index, heap object = {%" PRIxHADDR ", %zu}",
710
0
                    hobj->addr, hobj->idx);
711
0
    if (NULL == heap->obj[hobj->idx].begin)
712
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "bad heap pointer, heap object = {%" PRIxHADDR ", %zu}",
713
0
                    hobj->addr, hobj->idx);
714
715
    /* Set object size */
716
0
    *obj_size = heap->obj[hobj->idx].size;
717
718
0
done:
719
0
    if (heap && H5AC_unprotect(f, H5AC_GHEAP, hobj->addr, heap, H5AC__NO_FLAGS_SET) < 0)
720
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release object header");
721
722
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
723
0
} /* end H5HG_get_obj_size() */
724
725
/*-------------------------------------------------------------------------
726
 * Function:  H5HG_remove
727
 *
728
 * Purpose: Removes the specified object from the global heap.
729
 *
730
 * Return:  Non-negative on success/Negative on failure
731
 *
732
 *-------------------------------------------------------------------------
733
 */
734
herr_t
735
H5HG_remove(H5F_t *f, H5HG_t *hobj)
736
0
{
737
0
    H5HG_heap_t *heap = NULL;
738
0
    uint8_t     *p = NULL, *obj_start = NULL;
739
0
    size_t       need;
740
0
    unsigned     u;
741
0
    unsigned     flags     = H5AC__NO_FLAGS_SET; /* Whether the heap gets deleted */
742
0
    herr_t       ret_value = SUCCEED;            /* Return value */
743
744
0
    FUNC_ENTER_NOAPI_TAG(H5AC__GLOBALHEAP_TAG, FAIL)
745
746
    /* Check args */
747
0
    assert(f);
748
0
    assert(hobj);
749
0
    if (0 == (H5F_INTENT(f) & H5F_ACC_RDWR))
750
0
        HGOTO_ERROR(H5E_HEAP, H5E_WRITEERROR, FAIL, "no write intent on file");
751
752
    /* Heap object idx 0 is the free space in the heap and should never be given out */
753
0
    if (0 == hobj->idx)
754
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "bad heap index, heap object = {%" PRIxHADDR ", %zu}",
755
0
                    hobj->addr, hobj->idx);
756
757
    /* Load the heap */
758
0
    if (NULL == (heap = H5HG__protect(f, hobj->addr, H5AC__NO_FLAGS_SET)))
759
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, "unable to protect global heap");
760
761
    /* Sanity check the heap object (split around bugfix below) */
762
0
    if (hobj->idx >= heap->nused)
763
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "bad heap index, heap object = {%" PRIxHADDR ", %zu}",
764
0
                    hobj->addr, hobj->idx);
765
766
    /* When the application selects the same location to rewrite the VL element by using H5Sselect_elements,
767
     * it can happen that the entry has been removed by first rewrite.  Here we simply skip the removal of
768
     * the entry and let the second rewrite happen (see HDFFV-10635).  In the future, it'd be nice to handle
769
     * this situation in H5T_conv_vlen in H5Tconv.c instead of this level (HDFFV-10648). */
770
0
    if (heap->obj[hobj->idx].nrefs == 0 && heap->obj[hobj->idx].size == 0 && !heap->obj[hobj->idx].begin)
771
0
        HGOTO_DONE(SUCCEED);
772
773
    /* Finish sanity checking the heap object */
774
0
    if (NULL == heap->obj[hobj->idx].begin)
775
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "bad heap pointer, heap object = {%" PRIxHADDR ", %zu}",
776
0
                    hobj->addr, hobj->idx);
777
778
0
    obj_start = heap->obj[hobj->idx].begin;
779
    /* Include object header size */
780
0
    need = H5HG_ALIGN(heap->obj[hobj->idx].size) + H5HG_SIZEOF_OBJHDR(f);
781
782
    /* Move the new free space to the end of the heap */
783
0
    for (u = 0; u < heap->nused; u++)
784
0
        if (heap->obj[u].begin > heap->obj[hobj->idx].begin)
785
0
            heap->obj[u].begin -= need;
786
0
    if (NULL == heap->obj[0].begin) {
787
0
        heap->obj[0].begin = heap->chunk + (heap->size - need);
788
0
        heap->obj[0].size  = need;
789
0
        heap->obj[0].nrefs = 0;
790
0
    } /* end if */
791
0
    else
792
0
        heap->obj[0].size += need;
793
0
    memmove(obj_start, obj_start + need, heap->size - (size_t)((obj_start + need) - heap->chunk));
794
0
    if (heap->obj[0].size >= H5HG_SIZEOF_OBJHDR(f)) {
795
0
        p = heap->obj[0].begin;
796
0
        UINT16ENCODE(p, 0); /*id*/
797
0
        UINT16ENCODE(p, 0); /*nrefs*/
798
0
        UINT32ENCODE(p, 0); /*reserved*/
799
0
        H5F_ENCODE_LENGTH(f, p, heap->obj[0].size);
800
0
    } /* end if */
801
0
    memset(heap->obj + hobj->idx, 0, sizeof(H5HG_obj_t));
802
0
    flags |= H5AC__DIRTIED_FLAG;
803
804
0
    if ((heap->obj[0].size + H5HG_SIZEOF_HDR(f)) == heap->size) {
805
        /*
806
         * The collection is empty. Remove it from the CWFS list and return it
807
         * to the file free list.
808
         */
809
0
        flags |=
810
0
            H5AC__DELETED_FLAG |
811
0
            H5AC__FREE_FILE_SPACE_FLAG; /* Indicate that the object was deleted, for the unprotect call */
812
0
    }                                   /* end if */
813
0
    else {
814
        /*
815
         * If the heap is in the CWFS list then advance it one position.  The
816
         * H5AC_protect() might have done that too, but that's okay.  If the
817
         * heap isn't on the CWFS list then add it to the end.
818
         */
819
0
        if (H5F_cwfs_advance_heap(f, heap, true) < 0)
820
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTMODIFY, FAIL, "can't adjust file's CWFS");
821
0
    } /* end else */
822
823
0
done:
824
0
    if (heap && H5AC_unprotect(f, H5AC_GHEAP, hobj->addr, heap, flags) < 0)
825
0
        HDONE_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "unable to release object header");
826
827
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
828
0
} /* end H5HG_remove() */
829
830
/*-------------------------------------------------------------------------
831
 * Function:    H5HG__free
832
 *
833
 * Purpose:     Destroys a global heap collection in memory
834
 *
835
 * Return:      SUCCEED/FAIL
836
 *
837
 *-------------------------------------------------------------------------
838
 */
839
herr_t
840
H5HG__free(H5HG_heap_t *heap)
841
0
{
842
0
    herr_t ret_value = SUCCEED; /* Return value */
843
844
0
    FUNC_ENTER_PACKAGE
845
846
    /* Check arguments */
847
0
    assert(heap);
848
849
    /* Remove the heap from the CWFS list */
850
0
    if (H5F_cwfs_remove_heap(heap->shared, heap) < 0)
851
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTREMOVE, FAIL, "can't remove heap from file's CWFS");
852
853
0
    if (heap->chunk)
854
0
        heap->chunk = H5FL_BLK_FREE(gheap_chunk, heap->chunk);
855
0
    if (heap->obj)
856
0
        heap->obj = H5FL_SEQ_FREE(H5HG_obj_t, heap->obj);
857
0
    heap = H5FL_FREE(H5HG_heap_t, heap);
858
859
0
done:
860
0
    FUNC_LEAVE_NOAPI(ret_value)
861
0
} /* H5HG__free() */