Coverage Report

Created: 2024-06-18 06:29

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