Coverage Report

Created: 2025-08-29 07:09

/src/hdf5/src/H5SM.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
/* Module Setup */
15
/****************/
16
17
#define H5O_FRIEND      /*suppress error about including H5Opkg   */
18
#include "H5SMmodule.h" /* This source code file is part of the H5SM module */
19
20
/***********/
21
/* Headers */
22
/***********/
23
#include "H5private.h"   /* Generic Functions     */
24
#include "H5Eprivate.h"  /* Error handling        */
25
#include "H5Fprivate.h"  /* File access                          */
26
#include "H5FLprivate.h" /* Free Lists                           */
27
#include "H5MFprivate.h" /* File memory management    */
28
#include "H5MMprivate.h" /* Memory management     */
29
#include "H5Opkg.h"      /* Object Headers                       */
30
#include "H5SMpkg.h"     /* Shared object header messages        */
31
32
/****************/
33
/* Local Macros */
34
/****************/
35
36
/******************/
37
/* Local Typedefs */
38
/******************/
39
40
/* Udata struct for calls to H5SM__read_iter_op */
41
typedef struct H5SM_read_udata_t {
42
    H5F_t            *file;         /* File in which sharing is happening (in) */
43
    H5O_msg_crt_idx_t idx;          /* Creation index of this message (in) */
44
    size_t            buf_size;     /* Size of the encoded message (out) */
45
    void             *encoding_buf; /* The encoded message (out) */
46
} H5SM_read_udata_t;
47
48
/* Typedef to increment a reference count in the B-tree */
49
typedef struct {
50
    H5SM_mesg_key_t *key;      /* IN: key for message being incremented */
51
    bool             found;    /* OUT: if message was found */
52
    H5O_fheap_id_t   fheap_id; /* OUT: fheap ID of record */
53
} H5SM_incr_ref_opdata_t;
54
55
/********************/
56
/* Local Prototypes */
57
/********************/
58
static herr_t  H5SM__create_index(H5F_t *f, H5SM_index_header_t *header);
59
static herr_t  H5SM__delete_index(H5F_t *f, H5SM_index_header_t *header, bool delete_heap);
60
static haddr_t H5SM__create_list(H5F_t *f, H5SM_index_header_t *header);
61
static herr_t  H5SM__find_in_list(const H5SM_list_t *list, const H5SM_mesg_key_t *key, size_t *empty_pos,
62
                                  size_t *list_pos);
63
static herr_t  H5SM__convert_list_to_btree(H5F_t *f, H5SM_index_header_t *header, H5SM_list_t **_list,
64
                                           H5HF_t *fheap, H5O_t *open_oh);
65
static herr_t  H5SM__bt2_convert_to_list_op(const void *record, void *op_data);
66
static herr_t  H5SM__convert_btree_to_list(H5F_t *f, H5SM_index_header_t *header);
67
static herr_t  H5SM__incr_ref(void *record, void *_op_data, bool *changed);
68
static herr_t  H5SM__write_mesg(H5F_t *f, H5O_t *open_oh, H5SM_index_header_t *header, bool defer,
69
                                unsigned type_id, void *mesg, unsigned *cache_flags_ptr);
70
static herr_t  H5SM__decr_ref(void *record, void *op_data, bool *changed);
71
static herr_t  H5SM__delete_from_index(H5F_t *f, H5O_t *open_oh, H5SM_index_header_t *header,
72
                                       const H5O_shared_t *mesg, unsigned *cache_flags,
73
                                       size_t  */*out*/ mesg_size, void  **/*out*/ encoded_mesg);
74
static herr_t  H5SM__type_to_flag(unsigned type_id, unsigned *type_flag);
75
static herr_t  H5SM__read_iter_op(H5O_t *oh, H5O_mesg_t *mesg, unsigned sequence, unsigned *oh_modified,
76
                                  void *_udata);
77
static herr_t  H5SM__read_mesg_fh_cb(const void *obj, size_t obj_len, void *_udata);
78
static herr_t  H5SM__read_mesg(H5F_t *f, const H5SM_sohm_t *mesg, H5HF_t *fheap, H5O_t *open_oh,
79
                               size_t *encoding_size /*out*/, void **encoded_mesg /*out*/);
80
81
/*********************/
82
/* Package Variables */
83
/*********************/
84
85
/* Package initialization variable */
86
bool H5_PKG_INIT_VAR = false;
87
88
H5FL_DEFINE(H5SM_master_table_t);
89
H5FL_ARR_DEFINE(H5SM_index_header_t, H5O_SHMESG_MAX_NINDEXES);
90
H5FL_DEFINE(H5SM_list_t);
91
H5FL_ARR_DEFINE(H5SM_sohm_t, H5O_SHMESG_MAX_LIST_SIZE);
92
93
/*****************************/
94
/* Library Private Variables */
95
/*****************************/
96
97
/*******************/
98
/* Local Variables */
99
/*******************/
100
101
/*-------------------------------------------------------------------------
102
 * Function:    H5SM_init
103
 *
104
 * Purpose:     Initializes the Shared Message interface.
105
 *
106
 *              Creates a master SOHM table in the file and in the cache.
107
 *              This function should not be called for files that have
108
 *              SOHMs disabled in the FCPL.
109
 *
110
 * Return:      Non-negative on success/Negative on failure
111
 *
112
 *-------------------------------------------------------------------------
113
 */
114
herr_t
115
H5SM_init(H5F_t *f, H5P_genplist_t *fc_plist, const H5O_loc_t *ext_loc)
116
0
{
117
0
    H5O_shmesg_table_t   sohm_table;                 /* SOHM message for superblock extension */
118
0
    H5SM_master_table_t *table      = NULL;          /* SOHM master table for file */
119
0
    H5AC_ring_t          orig_ring  = H5AC_RING_INV; /* Original ring value */
120
0
    haddr_t              table_addr = HADDR_UNDEF;   /* Address of SOHM master table in file */
121
0
    unsigned             list_max, btree_min;        /* Phase change limits for SOHM indices */
122
0
    unsigned             index_type_flags[H5O_SHMESG_MAX_NINDEXES]; /* Messages types stored in each index */
123
0
    unsigned minsizes[H5O_SHMESG_MAX_NINDEXES]; /* Message size sharing threshold for each index */
124
0
    unsigned type_flags_used;                   /* Message type flags used, for sanity checking */
125
0
    unsigned x;                                 /* Local index variable */
126
0
    herr_t   ret_value = SUCCEED;               /* Return value */
127
128
0
    FUNC_ENTER_NOAPI_TAG(H5AC__SOHM_TAG, FAIL)
129
130
0
    assert(f);
131
    /* File should not already have a SOHM table */
132
0
    assert(!H5_addr_defined(H5F_SOHM_ADDR(f)));
133
134
    /* Set the ring type in the DXPL */
135
0
    H5AC_set_ring(H5AC_RING_USER, &orig_ring);
136
137
    /* Initialize master table */
138
0
    if (NULL == (table = H5FL_CALLOC(H5SM_master_table_t)))
139
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTALLOC, FAIL, "memory allocation failed for SOHM table");
140
0
    table->num_indexes = H5F_SOHM_NINDEXES(f);
141
0
    table->table_size  = H5SM_TABLE_SIZE(f);
142
143
    /* Get information from fcpl */
144
0
    if (H5P_get(fc_plist, H5F_CRT_SHMSG_INDEX_TYPES_NAME, &index_type_flags) < 0)
145
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "can't get SOHM type flags");
146
0
    if (H5P_get(fc_plist, H5F_CRT_SHMSG_LIST_MAX_NAME, &list_max) < 0)
147
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "can't get SOHM list maximum");
148
0
    if (H5P_get(fc_plist, H5F_CRT_SHMSG_BTREE_MIN_NAME, &btree_min) < 0)
149
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "can't get SOHM btree minimum");
150
0
    if (H5P_get(fc_plist, H5F_CRT_SHMSG_INDEX_MINSIZE_NAME, &minsizes) < 0)
151
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "can't get SOHM message min sizes");
152
153
    /* Verify that values are valid */
154
0
    if (table->num_indexes > H5O_SHMESG_MAX_NINDEXES)
155
0
        HGOTO_ERROR(H5E_SOHM, H5E_BADRANGE, FAIL, "number of indexes in property list is too large");
156
157
    /* Check that type flags weren't duplicated anywhere */
158
0
    type_flags_used = 0;
159
0
    for (x = 0; x < table->num_indexes; ++x) {
160
0
        if (index_type_flags[x] & type_flags_used)
161
0
            HGOTO_ERROR(H5E_SOHM, H5E_BADVALUE, FAIL,
162
0
                        "the same shared message type flag is assigned to more than one index");
163
0
        type_flags_used |= index_type_flags[x];
164
0
    } /* end for */
165
166
    /* Check that number of indexes in table and in superblock make sense.
167
     * Right now we just use one byte to hold the number of indexes.
168
     */
169
0
    assert(table->num_indexes < 256);
170
171
    /* Check that list and btree cutoffs make sense.  There can't be any
172
     * values greater than the list max but less than the btree min; the
173
     * list max has to be greater than or equal to one less than the btree
174
     * min.
175
     */
176
0
    assert(list_max + 1 >= btree_min);
177
0
    assert(table->num_indexes > 0 && table->num_indexes <= H5O_SHMESG_MAX_NINDEXES);
178
179
    /* Allocate the SOHM indexes as an array. */
180
0
    if (NULL == (table->indexes =
181
0
                     (H5SM_index_header_t *)H5FL_ARR_MALLOC(H5SM_index_header_t, (size_t)table->num_indexes)))
182
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTALLOC, FAIL, "memory allocation failed for SOHM indexes");
183
184
    /* Initialize all of the indexes, but don't allocate space for them to
185
     * hold messages until we actually need to write to them.
186
     */
187
0
    for (x = 0; x < table->num_indexes; x++) {
188
0
        table->indexes[x].btree_min     = btree_min;
189
0
        table->indexes[x].list_max      = list_max;
190
0
        table->indexes[x].mesg_types    = index_type_flags[x];
191
0
        table->indexes[x].min_mesg_size = minsizes[x];
192
0
        table->indexes[x].index_addr    = HADDR_UNDEF;
193
0
        table->indexes[x].heap_addr     = HADDR_UNDEF;
194
0
        table->indexes[x].num_messages  = 0;
195
196
        /* Indexes start as lists unless the list-to-btree threshold is zero */
197
0
        if (table->indexes[x].list_max > 0)
198
0
            table->indexes[x].index_type = H5SM_LIST;
199
0
        else
200
0
            table->indexes[x].index_type = H5SM_BTREE;
201
202
        /* Compute the size of a list index for this SOHM index */
203
0
        table->indexes[x].list_size = H5SM_LIST_SIZE(f, list_max);
204
0
    } /* end for */
205
206
    /* Allocate space for the table on disk */
207
0
    if (HADDR_UNDEF == (table_addr = H5MF_alloc(f, H5FD_MEM_SOHM_TABLE, (hsize_t)table->table_size)))
208
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTALLOC, FAIL, "file allocation failed for SOHM table");
209
210
    /* Cache the new table */
211
0
    if (H5AC_insert_entry(f, H5AC_SOHM_TABLE, table_addr, table, H5AC__NO_FLAGS_SET) < 0)
212
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTINS, FAIL, "can't add SOHM table to cache");
213
214
    /* Record the address of the master table in the file */
215
0
    H5F_SET_SOHM_ADDR(f, table_addr);
216
217
    /* Check for sharing attributes in this file, which means that creation
218
     *  indices must be tracked on object header message in the file.
219
     */
220
0
    if (type_flags_used & H5O_SHMESG_ATTR_FLAG)
221
0
        H5F_SET_STORE_MSG_CRT_IDX(f, true);
222
223
    /* Set the ring type to superblock extension */
224
0
    H5AC_set_ring(H5AC_RING_SBE, NULL);
225
226
    /* Write shared message information to the superblock extension */
227
0
    sohm_table.addr     = H5F_SOHM_ADDR(f);
228
0
    sohm_table.version  = H5F_SOHM_VERS(f);
229
0
    sohm_table.nindexes = H5F_SOHM_NINDEXES(f);
230
0
    if (H5O_msg_create(ext_loc, H5O_SHMESG_ID, H5O_MSG_FLAG_CONSTANT | H5O_MSG_FLAG_DONTSHARE,
231
0
                       H5O_UPDATE_TIME, &sohm_table) < 0)
232
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTINIT, FAIL, "unable to update SOHM header message");
233
234
0
done:
235
    /* Reset the ring in the API context */
236
0
    if (orig_ring != H5AC_RING_INV)
237
0
        H5AC_set_ring(orig_ring, NULL);
238
239
0
    if (ret_value < 0) {
240
0
        if (table_addr != HADDR_UNDEF)
241
0
            H5MF_xfree(f, H5FD_MEM_SOHM_TABLE, table_addr, (hsize_t)table->table_size);
242
0
        if (table != NULL)
243
0
            table = H5FL_FREE(H5SM_master_table_t, table);
244
0
    } /* end if */
245
246
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
247
0
} /* end H5SM_init() */
248
249
/*-------------------------------------------------------------------------
250
 * Function:    H5SM__type_to_flag
251
 *
252
 * Purpose:     Get the shared message flag for a given message type.
253
 *
254
 * Return:      Non-negative on success/Negative on failure
255
 *
256
 *-------------------------------------------------------------------------
257
 */
258
static herr_t
259
H5SM__type_to_flag(unsigned type_id, unsigned *type_flag)
260
0
{
261
0
    herr_t ret_value = SUCCEED; /* Return value */
262
263
0
    FUNC_ENTER_PACKAGE
264
265
    /* Translate the H5O type_id into an H5SM type flag */
266
0
    switch (type_id) {
267
0
        case H5O_FILL_ID:
268
0
            type_id = H5O_FILL_NEW_ID;
269
            /* FALLTHROUGH */
270
0
            H5_ATTR_FALLTHROUGH
271
272
0
        case H5O_SDSPACE_ID:
273
0
        case H5O_DTYPE_ID:
274
0
        case H5O_FILL_NEW_ID:
275
0
        case H5O_PLINE_ID:
276
0
        case H5O_ATTR_ID:
277
0
            *type_flag = (unsigned)1 << type_id;
278
0
            break;
279
280
0
        default:
281
0
            HGOTO_ERROR(H5E_SOHM, H5E_BADTYPE, FAIL, "unknown message type ID");
282
0
    } /* end switch */
283
284
0
done:
285
0
    FUNC_LEAVE_NOAPI(ret_value)
286
0
} /* end H5SM__type_to_flag() */
287
288
/*-------------------------------------------------------------------------
289
 * Function:    H5SM__get_index
290
 *
291
 * Purpose:     Get the index number for a given message type.
292
 *
293
 *              Returns the number of the index in the supplied table
294
 *              that holds messages of type type_id, or negative if
295
 *              there is no index for this message type.
296
 *
297
 * Return:      Non-negative on success/Negative on failure
298
 *
299
 *-------------------------------------------------------------------------
300
 */
301
herr_t
302
H5SM__get_index(const H5SM_master_table_t *table, unsigned type_id, ssize_t *idx)
303
0
{
304
0
    unsigned type_flag;
305
0
    ssize_t  found_index = -1;
306
0
    herr_t   ret_value   = SUCCEED; /* Return value */
307
308
0
    FUNC_ENTER_PACKAGE
309
310
    /* Translate the H5O type_id into an H5SM type flag */
311
0
    if (H5SM__type_to_flag(type_id, &type_flag) < 0)
312
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "can't map message type to flag");
313
314
    /* Search the indexes until we find one that matches this flag or we've
315
     * searched them all.
316
     */
317
0
    for (size_t x = 0; x < table->num_indexes; ++x)
318
0
        if (table->indexes[x].mesg_types & type_flag) {
319
0
            found_index = (ssize_t)x;
320
0
            break;
321
0
        }
322
323
    /* Set output parameter */
324
0
    *idx = found_index;
325
326
0
done:
327
0
    FUNC_LEAVE_NOAPI(ret_value)
328
0
} /* end H5SM__get_index() */
329
330
/*-------------------------------------------------------------------------
331
 * Function:    H5SM_type_shared
332
 *
333
 * Purpose:     Check if a given message type is shared in a file.
334
 *
335
 * Return:      Non-negative on success/Negative on failure
336
 *
337
 *-------------------------------------------------------------------------
338
 */
339
htri_t
340
H5SM_type_shared(H5F_t *f, unsigned type_id)
341
0
{
342
0
    H5SM_master_table_t *table = NULL;      /* Shared object master table */
343
0
    unsigned             type_flag;         /* Flag corresponding to message type */
344
0
    size_t               u;                 /* Local index variable */
345
0
    htri_t               ret_value = false; /* Return value */
346
347
0
    FUNC_ENTER_NOAPI_NOINIT_TAG(H5AC__SOHM_TAG)
348
349
    /* Translate the H5O type_id into an H5SM type flag */
350
0
    if (H5SM__type_to_flag(type_id, &type_flag) < 0)
351
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "can't map message type to flag");
352
353
    /* Look up the master SOHM table */
354
0
    if (H5_addr_defined(H5F_SOHM_ADDR(f))) {
355
0
        H5SM_table_cache_ud_t cache_udata; /* User-data for callback */
356
357
        /* Set up user data for callback */
358
0
        cache_udata.f = f;
359
360
0
        if (NULL == (table = (H5SM_master_table_t *)H5AC_protect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f),
361
0
                                                                 &cache_udata, H5AC__READ_ONLY_FLAG)))
362
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM master table");
363
0
    } /* end if */
364
0
    else
365
        /* No shared messages of any type */
366
0
        HGOTO_DONE(false);
367
368
    /* Search the indexes until we find one that matches this flag or we've
369
     * searched them all.
370
     */
371
0
    for (u = 0; u < table->num_indexes; u++)
372
0
        if (table->indexes[u].mesg_types & type_flag)
373
0
            HGOTO_DONE(true);
374
375
0
done:
376
    /* Release the master SOHM table */
377
0
    if (table && H5AC_unprotect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f), table, H5AC__NO_FLAGS_SET) < 0)
378
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM master table");
379
380
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
381
0
} /* end H5SM_type_shared() */
382
383
/*-------------------------------------------------------------------------
384
 * Function:    H5SM_get_fheap_addr
385
 *
386
 * Purpose:     Gets the address of the fractal heap used to store
387
 *              messages of type type_id.
388
 *
389
 * Return:      Non-negative on success/negative on failure
390
 *
391
 *-------------------------------------------------------------------------
392
 */
393
herr_t
394
H5SM_get_fheap_addr(H5F_t *f, unsigned type_id, haddr_t *fheap_addr)
395
0
{
396
0
    H5SM_master_table_t  *table = NULL;        /* Shared object master table */
397
0
    H5SM_table_cache_ud_t cache_udata;         /* User-data for callback */
398
0
    ssize_t               index_num;           /* Which index */
399
0
    herr_t                ret_value = SUCCEED; /* Return value */
400
401
0
    FUNC_ENTER_NOAPI_TAG(H5AC__SOHM_TAG, FAIL)
402
403
    /* Sanity checks */
404
0
    assert(f);
405
0
    assert(fheap_addr);
406
407
    /* Set up user data for callback */
408
0
    cache_udata.f = f;
409
410
    /* Look up the master SOHM table */
411
0
    if (NULL == (table = (H5SM_master_table_t *)H5AC_protect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f),
412
0
                                                             &cache_udata, H5AC__READ_ONLY_FLAG)))
413
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM master table");
414
415
    /* Look up index for message type */
416
0
    if (H5SM__get_index(table, type_id, &index_num) < 0)
417
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "unable to check for SOHM index");
418
0
    if (index_num < 0)
419
0
        HGOTO_ERROR(H5E_SOHM, H5E_NOTFOUND, FAIL, "unable to find correct SOHM index");
420
421
    /* Retrieve heap address for index */
422
0
    *fheap_addr = table->indexes[index_num].heap_addr;
423
424
0
done:
425
    /* Release the master SOHM table */
426
0
    if (table && H5AC_unprotect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f), table, H5AC__NO_FLAGS_SET) < 0)
427
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM master table");
428
429
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
430
0
} /* end H5SM_get_fheap_addr() */
431
432
/*-------------------------------------------------------------------------
433
 * Function:    H5SM__create_index
434
 *
435
 * Purpose:     Allocates storage for an index, populating the HEADER struct.
436
 *
437
 * Return:      Non-negative on success/negative on failure
438
 *
439
 *-------------------------------------------------------------------------
440
 */
441
static herr_t
442
H5SM__create_index(H5F_t *f, H5SM_index_header_t *header)
443
0
{
444
0
    H5HF_create_t fheap_cparam;     /* Fractal heap creation parameters */
445
0
    H5HF_t       *fheap     = NULL; /* Fractal heap handle */
446
0
    H5B2_t       *bt2       = NULL; /* v2 B-tree handle for index */
447
0
    herr_t        ret_value = SUCCEED;
448
449
0
    FUNC_ENTER_PACKAGE
450
451
    /* Sanity check */
452
0
    assert(header);
453
0
    assert(header->index_addr == HADDR_UNDEF);
454
0
    assert(header->btree_min <= header->list_max + 1);
455
456
    /* In most cases, the index starts as a list */
457
0
    if (header->list_max > 0) {
458
0
        haddr_t list_addr = HADDR_UNDEF; /* Address of SOHM list */
459
460
        /* Create the list index */
461
0
        if (HADDR_UNDEF == (list_addr = H5SM__create_list(f, header)))
462
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTCREATE, FAIL, "list creation failed for SOHM index");
463
464
        /* Set the index type & address */
465
0
        header->index_type = H5SM_LIST;
466
0
        header->index_addr = list_addr;
467
0
    } /* end if */
468
    /* index is a B-tree */
469
0
    else {
470
0
        H5B2_create_t bt2_cparam;              /* v2 B-tree creation parameters */
471
0
        haddr_t       tree_addr = HADDR_UNDEF; /* Address of SOHM B-tree */
472
473
        /* Create the v2 B-tree index */
474
0
        bt2_cparam.cls           = H5SM_INDEX;
475
0
        bt2_cparam.node_size     = (uint32_t)H5SM_B2_NODE_SIZE;
476
0
        bt2_cparam.rrec_size     = (uint32_t)H5SM_SOHM_ENTRY_SIZE(f);
477
0
        bt2_cparam.split_percent = H5SM_B2_SPLIT_PERCENT;
478
0
        bt2_cparam.merge_percent = H5SM_B2_MERGE_PERCENT;
479
0
        if (NULL == (bt2 = H5B2_create(f, &bt2_cparam, f)))
480
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTCREATE, FAIL, "B-tree creation failed for SOHM index");
481
482
        /* Retrieve the v2 B-tree's address in the file */
483
0
        if (H5B2_get_addr(bt2, &tree_addr) < 0)
484
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "can't get v2 B-tree address for SOHM index");
485
486
        /* Set the index type & address */
487
0
        header->index_type = H5SM_BTREE;
488
0
        header->index_addr = tree_addr;
489
0
    } /* end else */
490
491
    /* Create a heap to hold the shared messages that the list or B-tree will index */
492
0
    memset(&fheap_cparam, 0, sizeof(fheap_cparam));
493
0
    fheap_cparam.managed.width            = H5O_FHEAP_MAN_WIDTH;
494
0
    fheap_cparam.managed.start_block_size = H5O_FHEAP_MAN_START_BLOCK_SIZE;
495
0
    fheap_cparam.managed.max_direct_size  = H5O_FHEAP_MAN_MAX_DIRECT_SIZE;
496
0
    fheap_cparam.managed.max_index        = H5O_FHEAP_MAN_MAX_INDEX;
497
0
    fheap_cparam.managed.start_root_rows  = H5O_FHEAP_MAN_START_ROOT_ROWS;
498
0
    fheap_cparam.checksum_dblocks         = H5O_FHEAP_CHECKSUM_DBLOCKS;
499
0
    fheap_cparam.id_len                   = 0;
500
0
    fheap_cparam.max_man_size             = H5O_FHEAP_MAX_MAN_SIZE;
501
0
    if (NULL == (fheap = H5HF_create(f, &fheap_cparam)))
502
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTINIT, FAIL, "unable to create fractal heap");
503
504
0
    if (H5HF_get_heap_addr(fheap, &(header->heap_addr)) < 0)
505
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTGETSIZE, FAIL, "can't get fractal heap address");
506
507
#ifndef NDEBUG
508
    {
509
        size_t fheap_id_len; /* Size of a fractal heap ID */
510
511
        /* Sanity check ID length */
512
        if (H5HF_get_id_len(fheap, &fheap_id_len) < 0)
513
            HGOTO_ERROR(H5E_SOHM, H5E_CANTGETSIZE, FAIL, "can't get fractal heap ID length");
514
        assert(fheap_id_len == H5O_FHEAP_ID_LEN);
515
    }
516
#endif /* NDEBUG */
517
518
0
done:
519
    /* Release resources */
520
0
    if (fheap && H5HF_close(fheap) < 0)
521
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close fractal heap");
522
0
    if (bt2 && H5B2_close(bt2) < 0)
523
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close v2 B-tree for SOHM index");
524
525
0
    FUNC_LEAVE_NOAPI(ret_value)
526
0
} /* end H5SM__create_index */
527
528
/*-------------------------------------------------------------------------
529
 * Function:    H5SM__delete_index
530
 *
531
 * Purpose:     De-allocates storage for an index whose header is HEADER.
532
 *
533
 *              If DELETE_HEAP is true, deletes the index's heap, eliminating
534
 *              it completely.
535
 *
536
 *              If DELETE_HEAP is false, the heap is not deleted.  This is
537
 *              useful when deleting only the index header as the index is
538
 *              converted from a list to a B-tree and back again.
539
 *
540
 * Return:      Non-negative on success/negative on failure
541
 *
542
 *-------------------------------------------------------------------------
543
 */
544
static herr_t
545
H5SM__delete_index(H5F_t *f, H5SM_index_header_t *header, bool delete_heap)
546
0
{
547
0
    herr_t ret_value = SUCCEED; /* Return value */
548
549
0
    FUNC_ENTER_PACKAGE
550
551
    /* Determine whether index is a list or a B-tree. */
552
0
    if (header->index_type == H5SM_LIST) {
553
0
        unsigned index_status = 0; /* Index list's status in the metadata cache */
554
555
        /* Check the index list's status in the metadata cache */
556
0
        if (H5AC_get_entry_status(f, header->index_addr, &index_status) < 0)
557
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL,
558
0
                        "unable to check metadata cache status for direct block");
559
560
        /* If the index list is in the cache, expunge it now */
561
0
        if (index_status & H5AC_ES__IN_CACHE) {
562
            /* Sanity checks on index list */
563
0
            assert(!(index_status & H5AC_ES__IS_PINNED));
564
0
            assert(!(index_status & H5AC_ES__IS_PROTECTED));
565
566
            /* Evict the index list from the metadata cache */
567
0
            if (H5AC_expunge_entry(f, H5AC_SOHM_LIST, header->index_addr, H5AC__FREE_FILE_SPACE_FLAG) < 0)
568
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTREMOVE, FAIL, "unable to remove list index from cache");
569
0
        } /* end if */
570
0
    }     /* end if */
571
0
    else {
572
0
        assert(header->index_type == H5SM_BTREE);
573
574
        /* Delete the B-tree. */
575
0
        if (H5B2_delete(f, header->index_addr, f, NULL, NULL) < 0)
576
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTDELETE, FAIL, "unable to delete B-tree");
577
578
        /* Revert to list unless B-trees can have zero records */
579
0
        if (header->btree_min > 0)
580
0
            header->index_type = H5SM_LIST;
581
0
    } /* end else */
582
583
    /* Free the index's heap if requested. */
584
0
    if (delete_heap == true) {
585
0
        if (H5HF_delete(f, header->heap_addr) < 0)
586
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTDELETE, FAIL, "unable to delete fractal heap");
587
0
        header->heap_addr = HADDR_UNDEF;
588
0
    } /* end if */
589
590
    /* Reset index info */
591
0
    header->index_addr   = HADDR_UNDEF;
592
0
    header->num_messages = 0;
593
594
0
done:
595
0
    FUNC_LEAVE_NOAPI(ret_value)
596
0
} /* end H5SM__delete_index */
597
598
/*-------------------------------------------------------------------------
599
 * Function:    H5SM__create_list
600
 *
601
 * Purpose:     Creates a list of SOHM messages.
602
 *
603
 *              Called when a new index is created from scratch or when a
604
 *              B-tree needs to be converted back into a list.
605
 *
606
 * Return:      Non-negative on success/Negative on failure
607
 *
608
 *-------------------------------------------------------------------------
609
 */
610
static haddr_t
611
H5SM__create_list(H5F_t *f, H5SM_index_header_t *header)
612
0
{
613
0
    H5SM_list_t *list = NULL;             /* List of messages */
614
0
    hsize_t      x;                       /* Counter variable */
615
0
    size_t       num_entries;             /* Number of messages to create in list */
616
0
    haddr_t      addr      = HADDR_UNDEF; /* Address of the list on disk */
617
0
    haddr_t      ret_value = HADDR_UNDEF; /* Return value */
618
619
0
    FUNC_ENTER_PACKAGE
620
621
0
    assert(f);
622
0
    assert(header);
623
624
0
    num_entries = header->list_max;
625
626
    /* Allocate list in memory */
627
0
    if (NULL == (list = H5FL_CALLOC(H5SM_list_t)))
628
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTALLOC, HADDR_UNDEF, "file allocation failed for SOHM list");
629
0
    if (NULL == (list->messages = (H5SM_sohm_t *)H5FL_ARR_CALLOC(H5SM_sohm_t, num_entries)))
630
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTALLOC, HADDR_UNDEF, "file allocation failed for SOHM list");
631
632
    /* Initialize messages in list */
633
0
    for (x = 0; x < num_entries; x++)
634
0
        list->messages[x].location = H5SM_NO_LOC;
635
636
    /* Point list at header passed in */
637
0
    list->header = header;
638
639
    /* Allocate space for the list on disk */
640
0
    if (HADDR_UNDEF == (addr = H5MF_alloc(f, H5FD_MEM_SOHM_INDEX, (hsize_t)header->list_size)))
641
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTALLOC, HADDR_UNDEF, "file allocation failed for SOHM list");
642
643
    /* Put the list into the cache */
644
0
    if (H5AC_insert_entry(f, H5AC_SOHM_LIST, addr, list, H5AC__NO_FLAGS_SET) < 0)
645
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTINS, HADDR_UNDEF, "can't add SOHM list to cache");
646
647
    /* Set return value */
648
0
    ret_value = addr;
649
650
0
done:
651
0
    if (ret_value == HADDR_UNDEF) {
652
0
        if (list != NULL) {
653
0
            if (list->messages != NULL)
654
0
                list->messages = H5FL_ARR_FREE(H5SM_sohm_t, list->messages);
655
0
            list = H5FL_FREE(H5SM_list_t, list);
656
0
        } /* end if */
657
0
        if (addr != HADDR_UNDEF)
658
0
            H5MF_xfree(f, H5FD_MEM_SOHM_INDEX, addr, (hsize_t)header->list_size);
659
0
    } /* end if */
660
661
0
    FUNC_LEAVE_NOAPI(ret_value)
662
0
} /* end H5SM__create_list */
663
664
/*-------------------------------------------------------------------------
665
 * Function:    H5SM__convert_list_to_btree
666
 *
667
 * Purpose:     Given a list index, turns it into a B-tree index.  This is
668
 *              done when too many messages are added to the list.
669
 *
670
 *              Requires that *_LIST be a valid list and currently protected
671
 *              in the cache.  Unprotects (and expunges) *_LIST from the cache.
672
 *
673
 *              _LIST needs to be a double pointer so that the calling function
674
 *              knows if it is released from the cache if this function exits
675
 *              in error.  Trying to free it again will trigger an assert.
676
 *
677
 * Return:      Non-negative on success
678
 *              Negative on failure
679
 *
680
 *-------------------------------------------------------------------------
681
 */
682
static herr_t
683
H5SM__convert_list_to_btree(H5F_t *f, H5SM_index_header_t *header, H5SM_list_t **_list, H5HF_t *fheap,
684
                            H5O_t *open_oh)
685
0
{
686
0
    H5SM_list_t    *list;         /* Pointer to the existing message list */
687
0
    H5SM_mesg_key_t key;          /* Key for inserting records in v2 B-tree */
688
0
    H5B2_create_t   bt2_cparam;   /* v2 B-tree creation parameters */
689
0
    H5B2_t         *bt2 = NULL;   /* v2 B-tree handle for index */
690
0
    haddr_t         tree_addr;    /* New v2 B-tree's address */
691
0
    size_t          num_messages; /* Number of messages being tracked */
692
0
    size_t          x;
693
0
    void           *encoding_buf = NULL;
694
0
    herr_t          ret_value    = SUCCEED; /* Return value */
695
696
0
    FUNC_ENTER_PACKAGE
697
698
0
    assert(_list && *_list);
699
0
    assert(header);
700
701
    /* Get pointer to list of messages to convert */
702
0
    list = *_list;
703
704
    /* Create the new v2 B-tree for tracking the messages */
705
0
    bt2_cparam.cls           = H5SM_INDEX;
706
0
    bt2_cparam.node_size     = (uint32_t)H5SM_B2_NODE_SIZE;
707
0
    bt2_cparam.rrec_size     = (uint32_t)H5SM_SOHM_ENTRY_SIZE(f);
708
0
    bt2_cparam.split_percent = H5SM_B2_SPLIT_PERCENT;
709
0
    bt2_cparam.merge_percent = H5SM_B2_MERGE_PERCENT;
710
0
    if (NULL == (bt2 = H5B2_create(f, &bt2_cparam, f)))
711
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTCREATE, FAIL, "B-tree creation failed for SOHM index");
712
713
    /* Retrieve the v2 B-tree's address in the file */
714
0
    if (H5B2_get_addr(bt2, &tree_addr) < 0)
715
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "can't get v2 B-tree address for SOHM index");
716
717
    /* Set up key values that all messages will use.  Since these messages
718
     * are in the heap, they have a heap ID and no encoding or type_id.
719
     */
720
0
    key.file          = f;
721
0
    key.fheap         = fheap;
722
0
    key.encoding_size = 0;
723
0
    key.encoding      = NULL;
724
725
    /* Insert each record into the new B-tree */
726
0
    for (x = 0; x < header->list_max; x++) {
727
0
        if (list->messages[x].location != H5SM_NO_LOC) {
728
            /* Copy message into key */
729
0
            key.message = list->messages[x];
730
731
            /* Get the encoded message */
732
0
            if (H5SM__read_mesg(f, &(key.message), fheap, open_oh, &key.encoding_size, &encoding_buf) < 0)
733
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTLOAD, FAIL, "Couldn't read SOHM message in list");
734
735
0
            key.encoding = encoding_buf;
736
737
            /* Insert the message into the B-tree */
738
0
            if (H5B2_insert(bt2, &key) < 0)
739
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTINSERT, FAIL, "couldn't add SOHM to B-tree");
740
741
            /* Free buffer from H5SM__read_mesg */
742
0
            if (encoding_buf)
743
0
                encoding_buf = H5MM_xfree(encoding_buf);
744
0
        } /* end if */
745
0
    }     /* end for */
746
747
    /* Unprotect list in cache and release heap */
748
0
    if (H5AC_unprotect(f, H5AC_SOHM_LIST, header->index_addr, list,
749
0
                       H5AC__DELETED_FLAG | H5AC__FREE_FILE_SPACE_FLAG) < 0)
750
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to release SOHM list");
751
0
    *_list = list = NULL;
752
753
    /* Delete the old list index (but not its heap, which the new index is
754
     * still using!)
755
     */
756
0
    num_messages = header->num_messages; /* preserve this across the index deletion */
757
0
    if (H5SM__delete_index(f, header, false) < 0)
758
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTDELETE, FAIL, "can't free list index");
759
760
    /* Set/restore header info */
761
0
    header->index_addr   = tree_addr;
762
0
    header->index_type   = H5SM_BTREE;
763
0
    header->num_messages = num_messages;
764
765
0
done:
766
    /* Release resources */
767
0
    if (bt2 && H5B2_close(bt2) < 0)
768
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close v2 B-tree for SOHM index");
769
0
    if (encoding_buf)
770
0
        encoding_buf = H5MM_xfree(encoding_buf);
771
772
0
    FUNC_LEAVE_NOAPI(ret_value)
773
0
} /* H5SM__convert_list_to_btree() */
774
775
/*-------------------------------------------------------------------------
776
 * Function:  H5SM__bt2_convert_to_list_op
777
 *
778
 * Purpose: An H5B2_remove_t callback function to convert a SOHM
779
 *              B-tree index to a list.
780
 *
781
 *              Inserts this record into the list passed through op_data.
782
 *
783
 * Return:  Non-negative on success
784
 *              Negative on failure
785
 *
786
 *-------------------------------------------------------------------------
787
 */
788
static herr_t
789
H5SM__bt2_convert_to_list_op(const void *record, void *op_data)
790
0
{
791
0
    const H5SM_sohm_t *message = (const H5SM_sohm_t *)record;
792
0
    const H5SM_list_t *list    = (const H5SM_list_t *)op_data;
793
0
    size_t             mesg_idx; /* Index of message to modify */
794
795
0
    FUNC_ENTER_PACKAGE_NOERR
796
797
    /* Sanity checks */
798
0
    assert(record);
799
0
    assert(op_data);
800
801
    /* Get the message index, and increment the # of messages in list */
802
0
    mesg_idx = list->header->num_messages++;
803
0
    assert(list->header->num_messages <= list->header->list_max);
804
805
    /* Insert this message at the end of the list */
806
0
    assert(list->messages[mesg_idx].location == H5SM_NO_LOC);
807
0
    assert(message->location != H5SM_NO_LOC);
808
0
    H5MM_memcpy(&(list->messages[mesg_idx]), message, sizeof(H5SM_sohm_t));
809
810
0
    FUNC_LEAVE_NOAPI(SUCCEED)
811
0
} /* end H5SM__bt2_convert_to_list_op() */
812
813
/*-------------------------------------------------------------------------
814
 * Function:    H5SM__convert_btree_to_list
815
 *
816
 * Purpose:     Given a B-tree index, turns it into a list index.  This is
817
 *              done when too many messages are deleted from the B-tree.
818
 *
819
 * Return:      Non-negative on success
820
 *              Negative on failure
821
 *
822
 *-------------------------------------------------------------------------
823
 */
824
static herr_t
825
H5SM__convert_btree_to_list(H5F_t *f, H5SM_index_header_t *header)
826
0
{
827
0
    H5SM_list_t         *list = NULL;
828
0
    H5SM_list_cache_ud_t cache_udata; /* User-data for metadata cache callback */
829
0
    haddr_t              btree_addr;
830
0
    herr_t               ret_value = SUCCEED;
831
832
0
    FUNC_ENTER_PACKAGE
833
834
    /* Remember the address of the old B-tree, but change the header over to be
835
     * a list..
836
     */
837
0
    btree_addr = header->index_addr;
838
839
0
    header->num_messages = 0;
840
0
    header->index_type   = H5SM_LIST;
841
842
    /* Create a new list index */
843
0
    if (HADDR_UNDEF == (header->index_addr = H5SM__create_list(f, header)))
844
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTINIT, FAIL, "unable to create shared message list");
845
846
    /* Set up user data for metadata cache callback */
847
0
    cache_udata.f      = f;
848
0
    cache_udata.header = header;
849
850
    /* Protect the SOHM list */
851
0
    if (NULL == (list = (H5SM_list_t *)H5AC_protect(f, H5AC_SOHM_LIST, header->index_addr, &cache_udata,
852
0
                                                    H5AC__NO_FLAGS_SET)))
853
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM list index");
854
855
    /* Delete the B-tree and have messages copy themselves to the
856
     * list as they're deleted
857
     */
858
0
    if (H5B2_delete(f, btree_addr, f, H5SM__bt2_convert_to_list_op, list) < 0)
859
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTDELETE, FAIL, "unable to delete B-tree");
860
861
0
done:
862
    /* Release the SOHM list from the cache */
863
0
    if (list && H5AC_unprotect(f, H5AC_SOHM_LIST, header->index_addr, list, H5AC__DIRTIED_FLAG) < 0)
864
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to unprotect SOHM index");
865
866
0
    FUNC_LEAVE_NOAPI(ret_value)
867
0
} /* end H5SM__convert_btree_to_list() */
868
869
/*-------------------------------------------------------------------------
870
 * Function:    H5SM__can_share_common
871
 *
872
 * Purpose:     "trivial" checks for determining if a message can be shared.
873
 *
874
 * Note:  These checks are common to the "can share" and "try share"
875
 *    routines and are the "fast" checks before we need to protect
876
 *    the SOHM master table.
877
 *
878
 * Return:      true if message could be a SOHM
879
 *              false if this message couldn't be a SOHM
880
 *              Negative on failure
881
 *
882
 *-------------------------------------------------------------------------
883
 */
884
static htri_t
885
H5SM__can_share_common(const H5F_t *f, unsigned type_id, const void *mesg)
886
0
{
887
0
    htri_t ret_value = FAIL; /* Return value */
888
889
0
    FUNC_ENTER_PACKAGE
890
891
    /* Check whether this message ought to be shared or not */
892
    /* If sharing is disabled in this file, don't share the message */
893
0
    if (!H5_addr_defined(H5F_SOHM_ADDR(f)))
894
0
        HGOTO_DONE(false);
895
896
    /* Type-specific check */
897
0
    if ((ret_value = H5O_msg_can_share(type_id, mesg)) < 0)
898
0
        HGOTO_ERROR(H5E_SOHM, H5E_BADTYPE, FAIL, "can_share callback returned error");
899
0
    if (ret_value == false)
900
0
        HGOTO_DONE(false);
901
902
    /* At this point, the message passes the "trivial" checks and is worth
903
     *  further checks.
904
     */
905
906
0
done:
907
0
    FUNC_LEAVE_NOAPI(ret_value)
908
0
} /* end H5SM__can_share_common() */
909
910
/*-------------------------------------------------------------------------
911
 * Function:    H5SM_can_share
912
 *
913
 * Purpose:     Checks if an object header message would be shared or is
914
 *    already shared.
915
 *
916
 *              If not, returns false and does nothing.
917
 *
918
 * Return:      true if message will be a SOHM
919
 *              false if this message won't be a SOHM
920
 *              Negative on failure
921
 *
922
 *-------------------------------------------------------------------------
923
 */
924
htri_t
925
H5SM_can_share(H5F_t *f, H5SM_master_table_t *table, ssize_t *sohm_index_num, unsigned type_id,
926
               const void *mesg)
927
0
{
928
0
    size_t               mesg_size;
929
0
    H5SM_master_table_t *my_table = NULL;
930
0
    ssize_t              index_num;
931
0
    htri_t               tri_ret;
932
0
    htri_t               ret_value = true;
933
934
0
    FUNC_ENTER_NOAPI_TAG(H5AC__SOHM_TAG, FAIL)
935
936
    /* "trivial" sharing checks */
937
0
    if ((tri_ret = H5SM__can_share_common(f, type_id, mesg)) < 0)
938
0
        HGOTO_ERROR(H5E_SOHM, H5E_BADTYPE, FAIL, "'trivial' sharing checks returned error");
939
0
    if (tri_ret == false)
940
0
        HGOTO_DONE(false);
941
942
    /* Look up the master SOHM table */
943
    /* (use incoming master SOHM table if possible) */
944
0
    if (table)
945
0
        my_table = table;
946
0
    else {
947
0
        H5SM_table_cache_ud_t cache_udata; /* User-data for callback */
948
949
        /* Set up user data for callback */
950
0
        cache_udata.f = f;
951
952
0
        if (NULL == (my_table = (H5SM_master_table_t *)H5AC_protect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f),
953
0
                                                                    &cache_udata, H5AC__READ_ONLY_FLAG)))
954
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM master table");
955
0
    } /* end if */
956
957
    /* Find the right index for this message type.  If there is no such index
958
     * then this type of message isn't shareable
959
     */
960
0
    if (H5SM__get_index(my_table, type_id, &index_num) < 0)
961
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "unable to check for SOHM index");
962
0
    if (index_num < 0)
963
0
        HGOTO_DONE(false);
964
965
    /* If the message isn't big enough, don't bother sharing it */
966
0
    if (0 == (mesg_size = H5O_msg_raw_size(f, type_id, true, mesg)))
967
0
        HGOTO_ERROR(H5E_SOHM, H5E_BADMESG, FAIL, "unable to get OH message size");
968
0
    if (mesg_size < my_table->indexes[index_num].min_mesg_size)
969
0
        HGOTO_DONE(false);
970
971
    /* At this point, the message will be shared, set the index number if requested. */
972
0
    if (sohm_index_num)
973
0
        *sohm_index_num = index_num;
974
975
0
done:
976
    /* Release the master SOHM table, if we protected it */
977
0
    if (my_table && my_table != table &&
978
0
        H5AC_unprotect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f), my_table, H5AC__NO_FLAGS_SET) < 0)
979
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM master table");
980
981
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
982
0
} /* end H5SM_can_share() */
983
984
/*-------------------------------------------------------------------------
985
 * Function:    H5SM_try_share
986
 *
987
 * Purpose:     Attempts to share an object header message.
988
 *
989
 *              MESG_LOC is an H5O_mesg_loc_t struct that gives the message's
990
 *              location in an object header (address and index).  This
991
 *              function sets the type_id in MESG_LOC.
992
 *              If MESG_LOC is not NULL, this message will be "unique but
993
 *              shareable" and will be entered in the index but not actually
994
 *              shared.  If it is NULL, this message will be fully shared if
995
 *              it is shareable at all.
996
 *
997
 *              OPEN_OH is the object header that is currently open and
998
 *              protected.  If NULL, the SM module will protect any object
999
 *              header it needs (which can cause an error if that OH is
1000
 *              already protected!).
1001
 *
1002
 *              DEFER_FLAGS indicates whether the sharing operation should
1003
 *              actually occur, or whether this is just a set up call for a
1004
 *              future sharing operation.  In the latter case this argument
1005
 *              should be H5SM_DEFER.  If the message was previously deferred
1006
 *              this argument should be H5SM_WAS_DEFERRED.
1007
 *
1008
 *              MESG_FLAGS will have the H5O_MSG_FLAG_SHAREABLE or
1009
 *              H5O_MSG_FLAG_SHARED flag set if one is appropriate.  This is
1010
 *              the only way to tell the difference between a message that
1011
 *              has just been fully shared and a message that is only
1012
 *              "shareable" and is still in the object header, so it cannot
1013
 *              be NULL if MESG_LOC is not NULL.  If MESG_LOC is NULL, then
1014
 *              the message won't be "unique but shareable" and MESG_FLAGS
1015
 *              can be NULL as well.
1016
 *
1017
 *              If the message should be shared (if sharing has been
1018
 *              enabled and this message qualifies), this function turns the
1019
 *              message into a shared message, sets the H5O_MSG_FLAG_SHARED
1020
 *              flag in mesg_flags, and returns true.
1021
 *
1022
 *              If the message isn't shared, returns false.  If the message
1023
 *              isn't shared but was entered in the shared message index,
1024
 *              the H5O_MSG_FLAG_SHAREABLE flag will be set in mesg_flags
1025
 *              and returns true.
1026
 *
1027
 *              If this message was already shared, increments its reference
1028
 *              count, and leaves it otherwise unchanged, returning true and
1029
 *              setting the H5O_MSG_FLAG_SHARED flag in mesg_flags.
1030
 *
1031
 *              If mesg_flags is NULL, the calling function should just look
1032
 *              at the return value to determine if the message was shared
1033
 *              or not.  Such messages should never be "unique but shareable."
1034
 *
1035
 * Return:      true if message is now a SOHM
1036
 *              false if this message is not a SOHM
1037
 *              Negative on failure
1038
 *
1039
 *-------------------------------------------------------------------------
1040
 */
1041
htri_t
1042
H5SM_try_share(H5F_t *f, H5O_t *open_oh, unsigned defer_flags, unsigned type_id, void *mesg,
1043
               unsigned *mesg_flags)
1044
0
{
1045
0
    H5SM_master_table_t  *table = NULL;
1046
0
    H5SM_table_cache_ud_t cache_udata; /* User-data for callback */
1047
0
    unsigned              cache_flags = H5AC__NO_FLAGS_SET;
1048
0
    ssize_t               index_num;
1049
0
    htri_t                tri_ret;
1050
#ifndef NDEBUG
1051
    unsigned deferred_type = UINT_MAX;
1052
#endif
1053
0
    htri_t ret_value = true;
1054
1055
0
    FUNC_ENTER_NOAPI_TAG(H5AC__SOHM_TAG, FAIL)
1056
1057
    /* If we previously deferred this operation, the saved message type should
1058
     * be the same as the one we get here.  In debug mode, we make sure this
1059
     * holds true; otherwise we can leave now if it wasn't shared in the DEFER
1060
     * pass. */
1061
0
    if (defer_flags & H5SM_WAS_DEFERRED)
1062
#ifndef NDEBUG
1063
        deferred_type = ((H5O_shared_t *)mesg)->type;
1064
#else  /* NDEBUG */
1065
0
        if ((((H5O_shared_t *)mesg)->type != H5O_SHARE_TYPE_HERE) &&
1066
0
            (((H5O_shared_t *)mesg)->type != H5O_SHARE_TYPE_SOHM))
1067
0
            HGOTO_DONE(false);
1068
0
#endif /* NDEBUG */
1069
1070
    /* "trivial" sharing checks */
1071
0
    if (mesg_flags && (*mesg_flags & H5O_MSG_FLAG_DONTSHARE))
1072
0
        HGOTO_DONE(false);
1073
0
    if ((tri_ret = H5SM__can_share_common(f, type_id, mesg)) < 0)
1074
0
        HGOTO_ERROR(H5E_SOHM, H5E_BADTYPE, FAIL, "'trivial' sharing checks returned error");
1075
0
    if (tri_ret == false)
1076
0
        HGOTO_DONE(false);
1077
1078
    /* Set up user data for callback */
1079
0
    cache_udata.f = f;
1080
1081
    /* Look up the master SOHM table */
1082
0
    if (NULL == (table = (H5SM_master_table_t *)H5AC_protect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f),
1083
0
                                                             &cache_udata, H5AC__NO_FLAGS_SET)))
1084
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM master table");
1085
1086
    /* "complex" sharing checks */
1087
0
    if ((tri_ret = H5SM_can_share(f, table, &index_num, type_id, mesg)) < 0)
1088
0
        HGOTO_ERROR(H5E_SOHM, H5E_BADTYPE, FAIL, "'complex' sharing checks returned error");
1089
0
    if (tri_ret == false)
1090
0
        HGOTO_DONE(false);
1091
1092
    /* At this point, the message will be shared. */
1093
1094
    /* If the index hasn't been allocated yet, create it */
1095
0
    if (table->indexes[index_num].index_addr == HADDR_UNDEF) {
1096
0
        if (H5SM__create_index(f, &(table->indexes[index_num])) < 0)
1097
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTINIT, FAIL, "unable to create SOHM index");
1098
0
        cache_flags |= H5AC__DIRTIED_FLAG;
1099
0
    } /* end if */
1100
1101
    /* Write the message as a shared message.  This may or may not cause the
1102
     * message to become shared (if it is unique, it will not be shared).
1103
     */
1104
0
    if (H5SM__write_mesg(f, open_oh, &(table->indexes[index_num]), (defer_flags & H5SM_DEFER) != 0, type_id,
1105
0
                         mesg, &cache_flags) < 0)
1106
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTINSERT, FAIL, "can't write shared message");
1107
1108
    /* Set flags if this message was "written" without error and wasn't a
1109
     * 'defer' attempt; it is now either fully shared or "shareable".
1110
     */
1111
0
    if (mesg_flags) {
1112
0
        if (((H5O_shared_t *)mesg)->type == H5O_SHARE_TYPE_HERE)
1113
0
            *mesg_flags |= H5O_MSG_FLAG_SHAREABLE;
1114
0
        else {
1115
0
            assert(((H5O_shared_t *)mesg)->type == H5O_SHARE_TYPE_SOHM);
1116
0
            *mesg_flags |= H5O_MSG_FLAG_SHARED;
1117
0
        } /* end else */
1118
0
    }     /* end if */
1119
1120
0
done:
1121
0
    assert((ret_value != true) || ((H5O_shared_t *)mesg)->type == H5O_SHARE_TYPE_HERE ||
1122
0
           ((H5O_shared_t *)mesg)->type == H5O_SHARE_TYPE_SOHM);
1123
#ifndef NDEBUG
1124
    /* If we previously deferred this operation, make sure the saved message
1125
     * type is the same as the one we get here. */
1126
    if (defer_flags & H5SM_WAS_DEFERRED)
1127
        assert(deferred_type == ((H5O_shared_t *)mesg)->type);
1128
#endif /* NDEBUG */
1129
1130
    /* Release the master SOHM table */
1131
0
    if (table && H5AC_unprotect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f), table, cache_flags) < 0)
1132
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM master table");
1133
1134
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
1135
0
} /* end H5SM_try_share() */
1136
1137
/*-------------------------------------------------------------------------
1138
 * Function:  H5SM__incr_ref
1139
 *
1140
 * Purpose: Increment the reference count for a SOHM message and return
1141
 *              the message's heap ID.
1142
 *
1143
 *              The message pointer is actually returned via op_data, which
1144
 *              should be a pointer to a H5SM_fheap_id_t.
1145
 *
1146
 * Return:  Non-negative on success
1147
 *              Negative on failure
1148
 *
1149
 *-------------------------------------------------------------------------
1150
 */
1151
static herr_t
1152
H5SM__incr_ref(void *record, void *_op_data, bool *changed)
1153
0
{
1154
0
    H5SM_sohm_t            *message   = (H5SM_sohm_t *)record;
1155
0
    H5SM_incr_ref_opdata_t *op_data   = (H5SM_incr_ref_opdata_t *)_op_data;
1156
0
    herr_t                  ret_value = SUCCEED;
1157
1158
0
    FUNC_ENTER_PACKAGE
1159
1160
0
    assert(record);
1161
0
    assert(op_data);
1162
0
    assert(changed);
1163
1164
    /* If the message was previously shared in an object header, share
1165
     * it in the heap now.
1166
     */
1167
0
    if (message->location == H5SM_IN_OH) {
1168
0
        assert(op_data->key && op_data->key->fheap);
1169
1170
        /* Put the message in the heap and record its new heap ID */
1171
0
        if (H5HF_insert(op_data->key->fheap, op_data->key->encoding_size, op_data->key->encoding,
1172
0
                        &message->u.heap_loc.fheap_id) < 0)
1173
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTINSERT, FAIL, "unable to insert message into fractal heap");
1174
1175
0
        message->location             = H5SM_IN_HEAP;
1176
0
        message->u.heap_loc.ref_count = 2;
1177
0
    } /* end if */
1178
0
    else {
1179
0
        assert(message->location == H5SM_IN_HEAP);
1180
        /* If it's already in the heap, just increment the ref count */
1181
0
        ++message->u.heap_loc.ref_count;
1182
0
    } /* end else */
1183
1184
    /* If we got here, the message has changed */
1185
0
    *changed = true;
1186
1187
    /* Set the heap ID and indicate it was found */
1188
0
    op_data->fheap_id = message->u.heap_loc.fheap_id;
1189
0
    op_data->found    = true;
1190
1191
0
done:
1192
0
    FUNC_LEAVE_NOAPI(ret_value)
1193
0
} /* end H5SM__incr_ref() */
1194
1195
/*-------------------------------------------------------------------------
1196
 * Function:    H5SM__write_mesg
1197
 *
1198
 * Purpose:     This routine adds a shareable message to an index.
1199
 *              The behavior is controlled by the DEFER parameter:
1200
 *
1201
 *              If DEFER is true, this routine Simulates adding a shareable
1202
 *              message to an index.  It determines what the outcome would
1203
 *              be with DEFER set the false and updates the shared message
1204
 *              info, but does not actually add the message to a heap, list,
1205
 *              or b-tree.  Assumes that an open object header will be
1206
 *              available when H5SM__write_mesg is called with DEFER set to
1207
 *              false.
1208
 *
1209
 *              If DEFER is false, this routine adds a shareable message to
1210
 *              an index.  If this is the first such message and we have an
1211
 *              object header location for this message, we record it in the
1212
 *              index but don't modify the message passed in.  If the message
1213
 *              is already in the index or we don't have an object header
1214
 *              location for it, it is shared in the heap and this function
1215
 *              sets its sharing struct to reflect this.
1216
 *
1217
 *              The index could be a list or a B-tree.
1218
 *
1219
 * Return:      Non-negative on success
1220
 *              Negative on failure
1221
 *
1222
 *-------------------------------------------------------------------------
1223
 */
1224
static herr_t
1225
H5SM__write_mesg(H5F_t *f, H5O_t *open_oh, H5SM_index_header_t *header, bool defer, unsigned type_id,
1226
                 void *mesg, unsigned *cache_flags_ptr)
1227
0
{
1228
0
    H5SM_list_t         *list = NULL;             /* List index */
1229
0
    H5SM_mesg_key_t      key;                     /* Key used to search the index */
1230
0
    H5SM_list_cache_ud_t cache_udata;             /* User-data for metadata cache callback */
1231
0
    H5O_shared_t         shared;                  /* Shared H5O message */
1232
0
    bool                 found = false;           /* Was the message in the index? */
1233
0
    H5HF_t              *fheap = NULL;            /* Fractal heap handle */
1234
0
    H5B2_t              *bt2   = NULL;            /* v2 B-tree handle for index */
1235
0
    size_t               buf_size;                /* Size of the encoded message */
1236
0
    void                *encoding_buf = NULL;     /* Buffer for encoded message */
1237
0
    size_t               empty_pos    = SIZE_MAX; /* Empty entry in list */
1238
0
    herr_t               ret_value    = SUCCEED;
1239
1240
0
    FUNC_ENTER_PACKAGE
1241
1242
    /* Sanity check */
1243
0
    assert(header);
1244
0
    assert(header->index_type != H5SM_BADTYPE);
1245
0
    assert(cache_flags_ptr);
1246
1247
    /* Encode the message to be written */
1248
0
    if ((buf_size = H5O_msg_raw_size(f, type_id, true, mesg)) == 0)
1249
0
        HGOTO_ERROR(H5E_SOHM, H5E_BADSIZE, FAIL, "can't find message size");
1250
0
    if (NULL == (encoding_buf = H5MM_malloc(buf_size)))
1251
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTALLOC, FAIL, "can't allocate buffer for encoding");
1252
0
    if (H5O_msg_encode(f, type_id, true, (unsigned char *)encoding_buf, mesg) < 0)
1253
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTENCODE, FAIL, "can't encode message to be shared");
1254
1255
    /* Open the fractal heap for this index */
1256
0
    if (NULL == (fheap = H5HF_open(f, header->heap_addr)))
1257
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTOPENOBJ, FAIL, "unable to open fractal heap");
1258
1259
    /* Set up a key for the message to be written */
1260
0
    key.file             = f;
1261
0
    key.fheap            = fheap;
1262
0
    key.encoding         = encoding_buf;
1263
0
    key.encoding_size    = buf_size;
1264
0
    key.message.hash     = H5_checksum_lookup3(encoding_buf, buf_size, type_id);
1265
0
    key.message.location = H5SM_NO_LOC;
1266
1267
    /* Assume the message is already in the index and try to increment its
1268
     * reference count.  If this fails, the message isn't in the index after
1269
     * all and we'll need to add it.
1270
     */
1271
0
    if (header->index_type == H5SM_LIST) {
1272
0
        size_t list_pos; /* Position in a list index */
1273
1274
        /* Set up user data for metadata cache callback */
1275
0
        cache_udata.f      = f;
1276
0
        cache_udata.header = header;
1277
1278
        /* The index is a list; get it from the cache */
1279
0
        if (NULL == (list = (H5SM_list_t *)H5AC_protect(f, H5AC_SOHM_LIST, header->index_addr, &cache_udata,
1280
0
                                                        defer ? H5AC__READ_ONLY_FLAG : H5AC__NO_FLAGS_SET)))
1281
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM index");
1282
1283
        /* See if the message is already in the index and get its location.
1284
         * Also record the first empty list position we find in case we need it
1285
         * later.
1286
         */
1287
0
        if (H5SM__find_in_list(list, &key, &empty_pos, &list_pos) < 0)
1288
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTINSERT, FAIL, "unable to search for message in list");
1289
1290
0
        if (defer) {
1291
0
            if (list_pos != SIZE_MAX)
1292
0
                found = true;
1293
0
        } /* end if */
1294
0
        else {
1295
0
            if (list_pos != SIZE_MAX) {
1296
                /* If the message was previously shared in an object header, share
1297
                 * it in the heap now.
1298
                 */
1299
0
                if (list->messages[list_pos].location == H5SM_IN_OH) {
1300
                    /* Put the message in the heap and record its new heap ID */
1301
0
                    if (H5HF_insert(fheap, key.encoding_size, key.encoding, &shared.u.heap_id) < 0)
1302
0
                        HGOTO_ERROR(H5E_SOHM, H5E_CANTINSERT, FAIL,
1303
0
                                    "unable to insert message into fractal heap");
1304
1305
0
                    list->messages[list_pos].location             = H5SM_IN_HEAP;
1306
0
                    list->messages[list_pos].u.heap_loc.fheap_id  = shared.u.heap_id;
1307
0
                    list->messages[list_pos].u.heap_loc.ref_count = 2;
1308
0
                } /* end if */
1309
0
                else {
1310
                    /* If the message was already in the heap, increase its ref count */
1311
0
                    assert(list->messages[list_pos].location == H5SM_IN_HEAP);
1312
0
                    ++(list->messages[list_pos].u.heap_loc.ref_count);
1313
0
                } /* end else */
1314
1315
                /* Set up the shared location to point to the shared location */
1316
0
                shared.u.heap_id = list->messages[list_pos].u.heap_loc.fheap_id;
1317
0
                found            = true;
1318
0
            } /* end if */
1319
0
        }     /* end else */
1320
0
    }         /* end if */
1321
    /* Index is a B-tree */
1322
0
    else {
1323
0
        assert(header->index_type == H5SM_BTREE);
1324
1325
        /* Open the index v2 B-tree */
1326
0
        if (NULL == (bt2 = H5B2_open(f, header->index_addr, f)))
1327
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTOPENOBJ, FAIL, "unable to open v2 B-tree for SOHM index");
1328
1329
0
        if (defer) {
1330
            /* If this returns 0, it means that the message wasn't found. */
1331
            /* If it return 1, set the heap_id in the shared struct.  It will
1332
             * return a heap ID, since a message with a reference count greater
1333
             * than 1 is always shared in the heap.
1334
             */
1335
0
            if (H5B2_find(bt2, &key, &found, NULL, NULL) < 0)
1336
0
                HGOTO_ERROR(H5E_SOHM, H5E_NOTFOUND, FAIL, "can't search for message in index");
1337
0
        } /* end if */
1338
0
        else {
1339
0
            H5SM_incr_ref_opdata_t op_data;
1340
1341
            /* Set up callback info */
1342
0
            op_data.key   = &key;
1343
0
            op_data.found = false;
1344
1345
            /* Set the heap_id in the shared struct, if the message was found.
1346
             * It will return a heap ID, since a message with a reference count
1347
             * greater than 1 is always shared in the heap.
1348
             */
1349
0
            if (H5B2_modify(bt2, &key, true, H5SM__incr_ref, &op_data) < 0)
1350
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTMODIFY, FAIL, "B-tree modification failed");
1351
0
            if (op_data.found) {
1352
0
                shared.u.heap_id = op_data.fheap_id;
1353
0
                found            = true;
1354
0
            } /* end if */
1355
0
        }     /* end else */
1356
0
    }         /* end else */
1357
1358
0
    if (found) {
1359
        /* If the message was found, it's shared in the heap (now).  Set up a
1360
         * shared message so we can mark it as shared.
1361
         */
1362
0
        shared.type = H5O_SHARE_TYPE_SOHM;
1363
1364
#ifdef H5_USING_MEMCHECKER
1365
        /* Reset the shared message payload if deferring.  This doesn't matter
1366
         *      in the long run since the payload will get overwritten when the
1367
         *      non-deferred call to this routine occurs, but it stops memory
1368
         *      checkers like valgrind from whining when the partially initialized
1369
         *      shared message is serialized. -QAK
1370
         */
1371
        if (defer)
1372
            memset(&shared.u, 0, sizeof(shared.u));
1373
#endif /* H5_USING_MEMCHECKER */
1374
0
    }  /* end if */
1375
0
    else {
1376
0
        htri_t share_in_ohdr; /* Whether the new message can be shared in another object's header */
1377
1378
        /* Add the message to the index */
1379
1380
        /* Check if the message can be shared in another object's header */
1381
0
        if ((share_in_ohdr = H5O_msg_can_share_in_ohdr(type_id)) < 0)
1382
0
            HGOTO_ERROR(H5E_SOHM, H5E_BADTYPE, FAIL, "'share in ohdr' check returned error");
1383
1384
        /* If this message can be shared in an object header location, it is
1385
         *      "shareable" but not shared in the heap.
1386
         *
1387
         * If 'defer' flag is set:
1388
         *      We will insert it in the index but not modify the original
1389
         *              message.
1390
         *      If it can't be shared in an object header location, we will
1391
         *              insert it in the heap.  Note that we will only share
1392
         *              the message in the object header if there is an
1393
         *              "open_oh" available.
1394
         *
1395
         * If 'defer' flag is not set:
1396
         *      Insert it in the index but don't modify the original message.
1397
         *      If it can't be shared in an object header location or there's
1398
         *              no object header location available, insert it in the
1399
         *              heap.
1400
         */
1401
0
        if (share_in_ohdr && open_oh) {
1402
            /* Set up shared component info */
1403
0
            shared.type = H5O_SHARE_TYPE_HERE;
1404
1405
            /* Retrieve any creation index from the native message */
1406
0
            if (H5O_msg_get_crt_index(type_id, mesg, &shared.u.loc.index) < 0)
1407
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "unable to retrieve creation index");
1408
1409
0
            if (defer)
1410
0
                shared.u.loc.oh_addr = HADDR_UNDEF;
1411
0
            else {
1412
0
                shared.u.loc.oh_addr = H5O_OH_GET_ADDR(open_oh);
1413
1414
                /* Copy shared component info into key for inserting into index */
1415
0
                key.message.location   = H5SM_IN_OH;
1416
0
                key.message.u.mesg_loc = shared.u.loc;
1417
0
            } /* end else */
1418
0
        }     /* end if */
1419
0
        else {
1420
            /* Set up shared component info */
1421
            /* (heap ID set below, if not deferred) */
1422
0
            shared.type = H5O_SHARE_TYPE_SOHM;
1423
1424
0
            if (!defer) {
1425
                /* Put the message in the heap and record its new heap ID */
1426
0
                if (H5HF_insert(fheap, key.encoding_size, key.encoding, &shared.u.heap_id) < 0)
1427
0
                    HGOTO_ERROR(H5E_SOHM, H5E_CANTINSERT, FAIL, "unable to insert message into fractal heap");
1428
1429
0
                key.message.location             = H5SM_IN_HEAP;
1430
0
                key.message.u.heap_loc.fheap_id  = shared.u.heap_id;
1431
0
                key.message.u.heap_loc.ref_count = 1;
1432
0
            } /* end if */
1433
0
        }     /* end else */
1434
1435
0
        if (!defer) {
1436
            /* Set common information */
1437
0
            key.message.msg_type_id = type_id;
1438
1439
            /* Check whether the list has grown enough that it needs to become a B-tree */
1440
0
            if (header->index_type == H5SM_LIST && header->num_messages >= header->list_max)
1441
0
                if (H5SM__convert_list_to_btree(f, header, &list, fheap, open_oh) < 0)
1442
0
                    HGOTO_ERROR(H5E_SOHM, H5E_CANTDELETE, FAIL, "unable to convert list to B-tree");
1443
1444
            /* Insert the new message into the SOHM index */
1445
0
            if (header->index_type == H5SM_LIST) {
1446
                /* Index is a list.  Find an empty spot if we haven't already */
1447
0
                if (empty_pos == SIZE_MAX) {
1448
0
                    size_t pos;
1449
1450
0
                    if (H5SM__find_in_list(list, NULL, &empty_pos, &pos) < 0)
1451
0
                        HGOTO_ERROR(H5E_SOHM, H5E_CANTINSERT, FAIL, "unable to search for message in list");
1452
1453
0
                    if (pos == SIZE_MAX || empty_pos == SIZE_MAX)
1454
0
                        HGOTO_ERROR(H5E_SOHM, H5E_CANTINSERT, FAIL, "unable to find empty entry in list");
1455
0
                }
1456
                /* Insert message into list */
1457
0
                assert(list->messages[empty_pos].location == H5SM_NO_LOC);
1458
0
                assert(key.message.location != H5SM_NO_LOC);
1459
0
                list->messages[empty_pos] = key.message;
1460
0
            } /* end if */
1461
            /* Index is a B-tree */
1462
0
            else {
1463
0
                assert(header->index_type == H5SM_BTREE);
1464
1465
                /* Open the index v2 B-tree, if it isn't already */
1466
0
                if (NULL == bt2) {
1467
0
                    if (NULL == (bt2 = H5B2_open(f, header->index_addr, f)))
1468
0
                        HGOTO_ERROR(H5E_SOHM, H5E_CANTOPENOBJ, FAIL,
1469
0
                                    "unable to open v2 B-tree for SOHM index");
1470
0
                } /* end if */
1471
1472
0
                if (H5B2_insert(bt2, &key) < 0)
1473
0
                    HGOTO_ERROR(H5E_SOHM, H5E_CANTINSERT, FAIL, "couldn't add SOHM to B-tree");
1474
0
            } /* end else */
1475
1476
0
            ++(header->num_messages);
1477
0
            (*cache_flags_ptr) |= H5AC__DIRTIED_FLAG;
1478
0
        } /* end if */
1479
0
    }     /* end else */
1480
1481
    /* Set the file pointer & message type for the shared component */
1482
0
    shared.file        = f;
1483
0
    shared.msg_type_id = type_id;
1484
1485
    /* Update the original message's shared component */
1486
0
    if (H5O_msg_set_share(type_id, &shared, mesg) < 0)
1487
0
        HGOTO_ERROR(H5E_SOHM, H5E_BADMESG, FAIL, "unable to set sharing information");
1488
1489
0
done:
1490
    /* Release the fractal heap & v2 B-tree if we opened them */
1491
0
    if (fheap && H5HF_close(fheap) < 0)
1492
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close fractal heap");
1493
0
    if (bt2 && H5B2_close(bt2) < 0)
1494
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close v2 B-tree for SOHM index");
1495
1496
    /* If we got a list out of the cache, release it (it is always dirty after writing a message) */
1497
0
    if (list && H5AC_unprotect(f, H5AC_SOHM_LIST, header->index_addr, list,
1498
0
                               defer ? H5AC__NO_FLAGS_SET : H5AC__DIRTIED_FLAG) < 0)
1499
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM index");
1500
1501
0
    if (encoding_buf)
1502
0
        encoding_buf = H5MM_xfree(encoding_buf);
1503
1504
0
    FUNC_LEAVE_NOAPI(ret_value)
1505
0
} /* end H5SM__write_mesg() */
1506
1507
/*-------------------------------------------------------------------------
1508
 * Function:    H5SM_delete
1509
 *
1510
 * Purpose:     Given an object header message that is being deleted,
1511
 *              checks if it is a SOHM.  If so, decrements its reference
1512
 *              count.
1513
 *
1514
 *              If an object header is currently protected, it needs to
1515
 *              be passed in as open_oh so the SM code doesn't try to
1516
 *              re-protect it.
1517
 *
1518
 * Return:      Non-negative on success/Negative on failure
1519
 *
1520
 *-------------------------------------------------------------------------
1521
 */
1522
herr_t
1523
H5SM_delete(H5F_t *f, H5O_t *open_oh, H5O_shared_t *sh_mesg)
1524
0
{
1525
0
    H5SM_master_table_t  *table       = NULL;
1526
0
    unsigned              cache_flags = H5AC__NO_FLAGS_SET;
1527
0
    H5SM_table_cache_ud_t cache_udata; /* User-data for callback */
1528
0
    ssize_t               index_num;
1529
0
    size_t                mesg_size   = 0;
1530
0
    void                 *mesg_buf    = NULL;
1531
0
    void                 *native_mesg = NULL;
1532
0
    unsigned              type_id; /* Message type ID to operate on */
1533
0
    herr_t                ret_value = SUCCEED;
1534
1535
0
    FUNC_ENTER_NOAPI_TAG(H5AC__SOHM_TAG, FAIL)
1536
1537
0
    assert(f);
1538
0
    assert(H5_addr_defined(H5F_SOHM_ADDR(f)));
1539
0
    assert(sh_mesg);
1540
1541
    /* Get message type */
1542
0
    type_id = sh_mesg->msg_type_id;
1543
1544
    /* Set up user data for callback */
1545
0
    cache_udata.f = f;
1546
1547
    /* Look up the master SOHM table */
1548
0
    if (NULL == (table = (H5SM_master_table_t *)H5AC_protect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f),
1549
0
                                                             &cache_udata, H5AC__NO_FLAGS_SET)))
1550
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM master table");
1551
1552
    /* Find the correct index and try to delete from it */
1553
0
    if (H5SM__get_index(table, type_id, &index_num) < 0)
1554
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "unable to check for SOHM index");
1555
0
    if (index_num < 0)
1556
0
        HGOTO_ERROR(H5E_SOHM, H5E_NOTFOUND, FAIL, "unable to find correct SOHM index");
1557
1558
    /* If mesg_buf is not NULL, the message's reference count has reached
1559
     * zero and any file space it uses needs to be freed.  mesg_buf holds the
1560
     * serialized form of the message.
1561
     */
1562
0
    if (H5SM__delete_from_index(f, open_oh, &(table->indexes[index_num]), sh_mesg, &cache_flags, &mesg_size,
1563
0
                                &mesg_buf) < 0)
1564
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTDELETE, FAIL, "unable to delete message from SOHM index");
1565
1566
    /* Release the master SOHM table */
1567
0
    if (H5AC_unprotect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f), table, cache_flags) < 0)
1568
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM master table");
1569
0
    table = NULL;
1570
1571
    /* If buf was allocated, delete the message it holds.  This message may
1572
     * reference other shared messages that also need to be deleted, so the
1573
     * master table needs to be unprotected when we do this.
1574
     */
1575
0
    if (mesg_buf) {
1576
0
        if (NULL ==
1577
0
            (native_mesg = H5O_msg_decode(f, open_oh, type_id, mesg_size, (const unsigned char *)mesg_buf)))
1578
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTDECODE, FAIL, "can't decode shared message.");
1579
1580
0
        if (H5O_msg_delete(f, open_oh, type_id, native_mesg) < 0)
1581
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTFREE, FAIL, "can't delete shared message.");
1582
0
    } /* end if */
1583
1584
0
done:
1585
    /* Release the master SOHM table (should only happen on error) */
1586
0
    if (table && H5AC_unprotect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f), table, cache_flags) < 0)
1587
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM master table");
1588
1589
    /* Release any native message we decoded */
1590
0
    if (native_mesg)
1591
0
        H5O_msg_free(type_id, native_mesg);
1592
1593
    /* Free encoding buf */
1594
0
    if (mesg_buf)
1595
0
        mesg_buf = H5MM_xfree(mesg_buf);
1596
1597
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
1598
0
} /* end H5SM_delete() */
1599
1600
/*-------------------------------------------------------------------------
1601
 * Function:    H5SM__find_in_list
1602
 *
1603
 * Purpose:     Find a message's location in a list.  Also find the first
1604
 *              empty location in the list (since if we don't find the
1605
 *              message, we may want to insert it into an open spot).
1606
 *
1607
 *              If KEY is NULL, simply find the first empty location in the
1608
 *              list.
1609
 *
1610
 *              If EMPTY_POS is NULL, don't store anything in it.
1611
 *
1612
 * Return:      Success:    SUCCEED
1613
 *                          pos = position (if found)
1614
 *                          pos = SIZE_MAX (if not found)
1615
 *                          empty_pos = indeterminate (if found)
1616
 *                          empty_pos = 1st empty position (if not found)
1617
 *
1618
 *              Failure:    FAIL
1619
 *                          pos & empty_pos indeterminate
1620
 *
1621
 *-------------------------------------------------------------------------
1622
 */
1623
static herr_t
1624
H5SM__find_in_list(const H5SM_list_t *list, const H5SM_mesg_key_t *key, size_t *empty_pos, size_t *pos)
1625
0
{
1626
0
    size_t x;
1627
0
    herr_t ret_value = SUCCEED; /* Return value */
1628
1629
0
    FUNC_ENTER_PACKAGE
1630
1631
0
    assert(list);
1632
    /* Both key and empty_pos can be NULL, but not both! */
1633
0
    assert(key || empty_pos);
1634
1635
    /* Initialize empty_pos to an invalid value */
1636
0
    if (empty_pos)
1637
0
        *empty_pos = SIZE_MAX;
1638
1639
    /* Find the first (only) message equal to the key passed in.
1640
     * Also record the first empty position we find.
1641
     */
1642
0
    for (x = 0; x < list->header->list_max; x++) {
1643
0
        if (list->messages[x].location != H5SM_NO_LOC) {
1644
0
            int cmp;
1645
1646
0
            if (H5SM__message_compare(key, &(list->messages[x]), &cmp) < 0)
1647
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTCOMPARE, FAIL, "can't compare message records");
1648
1649
0
            if (0 == cmp) {
1650
0
                *pos = x;
1651
0
                HGOTO_DONE(SUCCEED);
1652
0
            }
1653
0
        }
1654
0
        else if (empty_pos && list->messages[x].location == H5SM_NO_LOC) {
1655
            /* Note position */
1656
0
            *empty_pos = x;
1657
1658
            /* Found earlier position possible, don't check any more */
1659
0
            empty_pos = NULL;
1660
0
        }
1661
0
    }
1662
1663
    /* If we reached this point, we didn't find the message */
1664
0
    *pos = SIZE_MAX;
1665
1666
0
done:
1667
0
    FUNC_LEAVE_NOAPI(ret_value)
1668
0
} /* end H5SM__find_in_list */
1669
1670
/*-------------------------------------------------------------------------
1671
 * Function:  H5SM__decr_ref
1672
 *
1673
 * Purpose: Decrement the reference count for a SOHM message.  Doesn't
1674
 *              remove the record from the B-tree even if the refcount
1675
 *              reaches zero.
1676
 *
1677
 *              The new message is returned through op_data.  If its
1678
 *              reference count is zero, the calling function should
1679
 *              remove this record from the B-tree.
1680
 *
1681
 * Return:  Non-negative on success
1682
 *              Negative on failure
1683
 *
1684
 *-------------------------------------------------------------------------
1685
 */
1686
static herr_t
1687
H5SM__decr_ref(void *record, void *op_data, bool *changed)
1688
0
{
1689
0
    H5SM_sohm_t *message = (H5SM_sohm_t *)record;
1690
1691
0
    FUNC_ENTER_PACKAGE_NOERR
1692
1693
0
    assert(record);
1694
0
    assert(op_data);
1695
0
    assert(changed);
1696
1697
    /* Adjust the message's reference count if it's stored in the heap.
1698
     * Messages stored in object headers always have refcounts of 1,
1699
     * so the calling function should know to just delete such a message
1700
     */
1701
0
    if (message->location == H5SM_IN_HEAP) {
1702
0
        --message->u.heap_loc.ref_count;
1703
0
        *changed = true;
1704
0
    } /* end if */
1705
1706
0
    if (op_data)
1707
0
        *(H5SM_sohm_t *)op_data = *message;
1708
1709
0
    FUNC_LEAVE_NOAPI(SUCCEED)
1710
0
} /* end H5SM__decr_ref() */
1711
1712
/*-------------------------------------------------------------------------
1713
 * Function:    H5SM__delete_from_index
1714
 *
1715
 * Purpose:     Decrement the reference count for a particular message in this
1716
 *              index.  If the reference count reaches zero, allocate a buffer
1717
 *              to hold the serialized form of this message so that any
1718
 *              resources it uses can be freed, and return this buffer in
1719
 *              ENCODED_MESG.
1720
 *
1721
 * Return:      Non-negative on success
1722
 *              Negative on failure
1723
 *
1724
 *-------------------------------------------------------------------------
1725
 */
1726
static herr_t
1727
H5SM__delete_from_index(H5F_t *f, H5O_t *open_oh, H5SM_index_header_t *header, const H5O_shared_t *mesg,
1728
                        unsigned *cache_flags, size_t *mesg_size /*out*/, void **encoded_mesg /*out*/)
1729
0
{
1730
0
    H5SM_list_t    *list = NULL;
1731
0
    H5SM_mesg_key_t key;
1732
0
    H5SM_sohm_t     message;             /* Deleted message returned from index */
1733
0
    H5SM_sohm_t    *message_ptr;         /* Pointer to deleted message returned from index */
1734
0
    H5HF_t         *fheap = NULL;        /* Fractal heap that contains the message */
1735
0
    H5B2_t         *bt2   = NULL;        /* v2 B-tree handle for index */
1736
0
    size_t          buf_size;            /* Size of the encoded message (out) */
1737
0
    void           *encoding_buf = NULL; /* The encoded message (out) */
1738
0
    unsigned        type_id;             /* Message type to operate on */
1739
0
    herr_t          ret_value = SUCCEED;
1740
1741
0
    FUNC_ENTER_PACKAGE
1742
1743
    /* Sanity check */
1744
0
    assert(f);
1745
0
    assert(header);
1746
0
    assert(mesg);
1747
0
    assert(cache_flags);
1748
0
    assert(*encoded_mesg == NULL);
1749
1750
    /* Get the message type for later */
1751
0
    type_id = mesg->msg_type_id;
1752
1753
    /* Open the heap for this type of message. */
1754
0
    if (NULL == (fheap = H5HF_open(f, header->heap_addr)))
1755
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTOPENOBJ, FAIL, "unable to open fractal heap");
1756
1757
    /* Get the message size and encoded message for the message to be deleted,
1758
     * either from its OH or from the heap.
1759
     */
1760
0
    if (mesg->type == H5O_SHARE_TYPE_HERE) {
1761
0
        key.message.location    = H5SM_IN_OH;
1762
0
        key.message.msg_type_id = type_id;
1763
0
        key.message.u.mesg_loc  = mesg->u.loc;
1764
0
    } /* end if */
1765
0
    else {
1766
0
        key.message.location             = H5SM_IN_HEAP;
1767
0
        key.message.msg_type_id          = type_id;
1768
0
        key.message.u.heap_loc.ref_count = 0; /* Refcount isn't relevant here */
1769
0
        key.message.u.heap_loc.fheap_id  = mesg->u.heap_id;
1770
0
    } /* end else */
1771
1772
    /* Get the encoded message */
1773
0
    if (H5SM__read_mesg(f, &key.message, fheap, open_oh, &buf_size, &encoding_buf) < 0)
1774
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTOPENOBJ, FAIL, "unable to open fractal heap");
1775
1776
    /* Set up key for message to be deleted. */
1777
0
    key.file          = f;
1778
0
    key.fheap         = fheap;
1779
0
    key.encoding      = encoding_buf;
1780
0
    key.encoding_size = buf_size;
1781
0
    key.message.hash  = H5_checksum_lookup3(encoding_buf, buf_size, type_id);
1782
1783
    /* Try to find the message in the index */
1784
0
    if (header->index_type == H5SM_LIST) {
1785
0
        H5SM_list_cache_ud_t cache_udata; /* User-data for metadata cache callback */
1786
0
        size_t               list_pos;    /* Position of the message in the list */
1787
1788
        /* Set up user data for metadata cache callback */
1789
0
        cache_udata.f      = f;
1790
0
        cache_udata.header = header;
1791
1792
        /* If the index is stored as a list, get it from the cache */
1793
0
        if (NULL == (list = (H5SM_list_t *)H5AC_protect(f, H5AC_SOHM_LIST, header->index_addr, &cache_udata,
1794
0
                                                        H5AC__NO_FLAGS_SET)))
1795
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM index");
1796
1797
        /* Find the message in the list */
1798
0
        if (H5SM__find_in_list(list, &key, NULL, &list_pos) < 0)
1799
0
            HGOTO_ERROR(H5E_SOHM, H5E_NOTFOUND, FAIL, "unable to search for message in list");
1800
0
        if (list_pos == SIZE_MAX)
1801
0
            HGOTO_ERROR(H5E_SOHM, H5E_NOTFOUND, FAIL, "message not in index");
1802
1803
0
        if (list->messages[list_pos].location == H5SM_IN_HEAP)
1804
0
            --(list->messages[list_pos].u.heap_loc.ref_count);
1805
1806
        /* Point to the message */
1807
0
        message_ptr = &list->messages[list_pos];
1808
0
    } /* end if */
1809
0
    else {
1810
        /* Index is a B-tree */
1811
0
        assert(header->index_type == H5SM_BTREE);
1812
1813
        /* Open the index v2 B-tree */
1814
0
        if (NULL == (bt2 = H5B2_open(f, header->index_addr, f)))
1815
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTOPENOBJ, FAIL, "unable to open v2 B-tree for SOHM index");
1816
1817
        /* If this returns failure, it means that the message wasn't found.
1818
         * If it succeeds, a copy of the modified message will be returned.
1819
         */
1820
0
        if (H5B2_modify(bt2, &key, false, H5SM__decr_ref, &message) < 0)
1821
0
            HGOTO_ERROR(H5E_SOHM, H5E_NOTFOUND, FAIL, "message not in index");
1822
1823
        /* Point to the message */
1824
0
        message_ptr = &message;
1825
0
    } /* end else */
1826
1827
    /* If the ref count is zero or this message was in an OH (which always
1828
     * has a ref count of 1) delete the message from the index
1829
     */
1830
0
    if (message_ptr->location == H5SM_IN_OH || message_ptr->u.heap_loc.ref_count == 0) {
1831
        /* Save the location */
1832
0
        H5SM_storage_loc_t old_loc = message_ptr->location;
1833
1834
        /* Updated the index header, so set its dirty flag */
1835
0
        --header->num_messages;
1836
0
        *cache_flags |= H5AC__DIRTIED_FLAG;
1837
1838
        /* Remove the message from the index */
1839
0
        if (header->index_type == H5SM_LIST)
1840
0
            message_ptr->location = H5SM_NO_LOC;
1841
0
        else {
1842
            /* Open the index v2 B-tree, if it isn't already */
1843
0
            if (NULL == bt2) {
1844
0
                if (NULL == (bt2 = H5B2_open(f, header->index_addr, f)))
1845
0
                    HGOTO_ERROR(H5E_SOHM, H5E_CANTOPENOBJ, FAIL, "unable to open v2 B-tree for SOHM index");
1846
0
            } /* end if */
1847
1848
0
            if (H5B2_remove(bt2, &key, NULL, NULL) < 0)
1849
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTREMOVE, FAIL, "unable to delete message from index");
1850
0
        } /* end else */
1851
1852
        /* Remove the message from the heap if it was stored in the heap*/
1853
0
        if (old_loc == H5SM_IN_HEAP)
1854
0
            if (H5HF_remove(fheap, &(message_ptr->u.heap_loc.fheap_id)) < 0)
1855
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTREMOVE, FAIL, "unable to remove message from heap");
1856
1857
        /* Return the message's encoding so anything it references can be freed */
1858
0
        *encoded_mesg = encoding_buf;
1859
0
        *mesg_size    = buf_size;
1860
1861
        /* If there are no messages left in the index, delete it */
1862
0
        if (header->num_messages == 0) {
1863
1864
            /* Unprotect cache and release heap */
1865
0
            if (list && H5AC_unprotect(f, H5AC_SOHM_LIST, header->index_addr, list,
1866
0
                                       H5AC__DELETED_FLAG | H5AC__FREE_FILE_SPACE_FLAG) < 0)
1867
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to release SOHM list");
1868
0
            list = NULL;
1869
1870
0
            assert(fheap);
1871
0
            if (H5HF_close(fheap) < 0)
1872
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close fractal heap");
1873
0
            fheap = NULL;
1874
1875
            /* Delete the index and its heap */
1876
0
            if (H5SM__delete_index(f, header, true) < 0)
1877
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTDELETE, FAIL, "can't delete empty index");
1878
0
        } /* end if */
1879
0
        else if (header->index_type == H5SM_BTREE && header->num_messages < header->btree_min) {
1880
            /* Otherwise, if we've just passed the btree-to-list cutoff, convert
1881
             * this B-tree into a list
1882
             */
1883
0
            if (H5SM__convert_btree_to_list(f, header) < 0)
1884
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTINIT, FAIL, "unable to convert btree to list");
1885
0
        } /* end if */
1886
0
    }     /* end if */
1887
1888
0
done:
1889
    /* Release the SOHM list */
1890
0
    if (list && H5AC_unprotect(f, H5AC_SOHM_LIST, header->index_addr, list, H5AC__DIRTIED_FLAG) < 0)
1891
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM index");
1892
1893
    /* Release the fractal heap & v2 B-tree if we opened them */
1894
0
    if (fheap && H5HF_close(fheap) < 0)
1895
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close fractal heap");
1896
0
    if (bt2 && H5B2_close(bt2) < 0)
1897
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close v2 B-tree for SOHM index");
1898
1899
    /* Free the message encoding, if we're not returning it in encoded_mesg
1900
     * or if there's been an error.
1901
     */
1902
0
    if (encoding_buf && (NULL == *encoded_mesg || ret_value < 0)) {
1903
0
        encoding_buf = H5MM_xfree(encoding_buf);
1904
0
        *mesg_size   = 0;
1905
0
    }
1906
1907
0
    FUNC_LEAVE_NOAPI(ret_value)
1908
0
} /* end H5SM__delete_from_index() */
1909
1910
/*-------------------------------------------------------------------------
1911
 * Function:    H5SM_get_info
1912
 *
1913
 * Purpose:     Get the shared message info for a file, if there is any.
1914
 *
1915
 * Return:      Non-negative on success/Negative on failure
1916
 *
1917
 *-------------------------------------------------------------------------
1918
 */
1919
herr_t
1920
H5SM_get_info(const H5O_loc_t *ext_loc, H5P_genplist_t *fc_plist)
1921
0
{
1922
0
    H5F_t               *f = ext_loc->file;         /* File pointer (convenience variable) */
1923
0
    H5O_shmesg_table_t   sohm_table;                /* SOHM message from superblock extension */
1924
0
    H5SM_master_table_t *table     = NULL;          /* SOHM master table */
1925
0
    H5AC_ring_t          orig_ring = H5AC_RING_INV; /* Original ring value */
1926
0
    unsigned             tmp_sohm_nindexes;         /* Number of shared messages indexes in the table */
1927
0
    htri_t               status;                    /* Status for message existing */
1928
0
    herr_t               ret_value = SUCCEED;       /* Return value */
1929
1930
0
    FUNC_ENTER_NOAPI_TAG(H5AC__SOHM_TAG, FAIL)
1931
1932
    /* Sanity check */
1933
0
    assert(ext_loc);
1934
0
    assert(f);
1935
0
    assert(fc_plist);
1936
1937
    /* Check for the extension having a 'shared message info' message */
1938
0
    if ((status = H5O_msg_exists(ext_loc, H5O_SHMESG_ID)) < 0)
1939
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "unable to read object header");
1940
0
    if (status) {
1941
0
        H5SM_table_cache_ud_t cache_udata;                          /* User-data for callback */
1942
0
        unsigned              index_flags[H5O_SHMESG_MAX_NINDEXES]; /* Message flags for each index */
1943
0
        unsigned              minsizes[H5O_SHMESG_MAX_NINDEXES];    /* Minimum message size for each index */
1944
0
        unsigned              sohm_l2b;                             /* SOHM list-to-btree cutoff */
1945
0
        unsigned              sohm_b2l;                             /* SOHM btree-to-list cutoff */
1946
0
        unsigned              u;                                    /* Local index variable */
1947
1948
        /* Retrieve the 'shared message info' structure */
1949
0
        if (NULL == H5O_msg_read(ext_loc, H5O_SHMESG_ID, &sohm_table))
1950
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "shared message info message not present");
1951
1952
        /* Portably initialize the arrays */
1953
0
        memset(index_flags, 0, sizeof(index_flags));
1954
0
        memset(minsizes, 0, sizeof(minsizes));
1955
1956
        /* Set SOHM info from file */
1957
0
        H5F_SET_SOHM_ADDR(f, sohm_table.addr);
1958
0
        H5F_SET_SOHM_VERS(f, sohm_table.version);
1959
0
        H5F_SET_SOHM_NINDEXES(f, sohm_table.nindexes);
1960
0
        assert(H5_addr_defined(H5F_SOHM_ADDR(f)));
1961
0
        assert(H5F_SOHM_NINDEXES(f) > 0 && H5F_SOHM_NINDEXES(f) <= H5O_SHMESG_MAX_NINDEXES);
1962
1963
        /* Set up user data for callback */
1964
0
        cache_udata.f = f;
1965
1966
        /* Set the ring type in the DXPL */
1967
0
        H5AC_set_ring(H5AC_RING_USER, &orig_ring);
1968
1969
        /* Read the rest of the SOHM table information from the cache */
1970
0
        if (NULL == (table = (H5SM_master_table_t *)H5AC_protect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f),
1971
0
                                                                 &cache_udata, H5AC__READ_ONLY_FLAG)))
1972
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM master table");
1973
1974
        /* Get index conversion limits */
1975
0
        sohm_l2b = (unsigned)table->indexes[0].list_max;
1976
0
        sohm_b2l = (unsigned)table->indexes[0].btree_min;
1977
1978
        /* Iterate through all indices */
1979
0
        for (u = 0; u < table->num_indexes; ++u) {
1980
            /* Pack information about the individual SOHM index */
1981
0
            index_flags[u] = table->indexes[u].mesg_types;
1982
0
            minsizes[u]    = (unsigned)table->indexes[u].min_mesg_size;
1983
1984
            /* Sanity check */
1985
0
            assert(sohm_l2b == table->indexes[u].list_max);
1986
0
            assert(sohm_b2l == table->indexes[u].btree_min);
1987
1988
            /* Check for sharing attributes in this file, which means that creation
1989
             *  indices must be tracked on object header message in the file.
1990
             */
1991
0
            if (index_flags[u] & H5O_SHMESG_ATTR_FLAG)
1992
0
                H5F_SET_STORE_MSG_CRT_IDX(f, true);
1993
0
        } /* end for */
1994
1995
        /* Set values in the property list */
1996
0
        tmp_sohm_nindexes = H5F_SOHM_NINDEXES(f);
1997
0
        if (H5P_set(fc_plist, H5F_CRT_SHMSG_NINDEXES_NAME, &tmp_sohm_nindexes) < 0)
1998
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTSET, FAIL, "can't set number of SOHM indexes");
1999
0
        if (H5P_set(fc_plist, H5F_CRT_SHMSG_INDEX_TYPES_NAME, index_flags) < 0)
2000
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTSET, FAIL, "can't set type flags for indexes");
2001
0
        if (H5P_set(fc_plist, H5F_CRT_SHMSG_INDEX_MINSIZE_NAME, minsizes) < 0)
2002
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTSET, FAIL, "can't set type flags for indexes");
2003
0
        if (H5P_set(fc_plist, H5F_CRT_SHMSG_LIST_MAX_NAME, &sohm_l2b) < 0)
2004
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "can't set SOHM cutoff in property list");
2005
0
        if (H5P_set(fc_plist, H5F_CRT_SHMSG_BTREE_MIN_NAME, &sohm_b2l) < 0)
2006
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "can't set SOHM cutoff in property list");
2007
0
    } /* end if */
2008
0
    else {
2009
        /* No SOHM info in file */
2010
0
        H5F_SET_SOHM_ADDR(f, HADDR_UNDEF);
2011
0
        H5F_SET_SOHM_VERS(f, 0);
2012
0
        H5F_SET_SOHM_NINDEXES(f, 0);
2013
2014
        /* Shared object header messages are disabled */
2015
0
        tmp_sohm_nindexes = H5F_SOHM_NINDEXES(f);
2016
0
        if (H5P_set(fc_plist, H5F_CRT_SHMSG_NINDEXES_NAME, &tmp_sohm_nindexes) < 0)
2017
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTSET, FAIL, "can't set number of SOHM indexes");
2018
0
    } /* end else */
2019
2020
0
done:
2021
    /* Reset the ring in the API context */
2022
0
    if (orig_ring != H5AC_RING_INV)
2023
0
        H5AC_set_ring(orig_ring, NULL);
2024
2025
    /* Release the master SOHM table if we took it out of the cache */
2026
0
    if (table && H5AC_unprotect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f), table, H5AC__NO_FLAGS_SET) < 0)
2027
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM master table");
2028
2029
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
2030
0
} /* end H5SM_get_info() */
2031
2032
/*-------------------------------------------------------------------------
2033
 * Function:    H5SM_reconstitute
2034
 *
2035
 * Purpose:     Reconstitute a shared object header message structure from
2036
 *              a plain heap ID.
2037
 *
2038
 * Return:      Non-negative on success/Negative on failure
2039
 *
2040
 *-------------------------------------------------------------------------
2041
 */
2042
herr_t
2043
H5SM_reconstitute(H5O_shared_t *sh_mesg, H5F_t *f, unsigned msg_type_id, H5O_fheap_id_t heap_id)
2044
0
{
2045
0
    FUNC_ENTER_NOAPI_NOINIT_NOERR
2046
2047
    /* Sanity check args */
2048
0
    assert(sh_mesg);
2049
2050
    /* Set flag for shared message */
2051
0
    sh_mesg->type        = H5O_SHARE_TYPE_SOHM;
2052
0
    sh_mesg->file        = f;
2053
0
    sh_mesg->msg_type_id = msg_type_id;
2054
0
    sh_mesg->u.heap_id   = heap_id;
2055
2056
0
    FUNC_LEAVE_NOAPI(SUCCEED)
2057
0
} /* end H5SM_reconstitute() */
2058
2059
/*-------------------------------------------------------------------------
2060
 * Function:  H5SM__get_refcount_bt2_cb
2061
 *
2062
 * Purpose: v2 B-tree 'find' callback to retrieve the record for a message
2063
 *
2064
 * Return:  SUCCEED/FAIL
2065
 *
2066
 *-------------------------------------------------------------------------
2067
 */
2068
static herr_t
2069
H5SM__get_refcount_bt2_cb(const void *_record, void *_op_data)
2070
0
{
2071
0
    const H5SM_sohm_t *record  = (const H5SM_sohm_t *)_record; /* v2 B-tree record for message */
2072
0
    H5SM_sohm_t       *op_data = (H5SM_sohm_t *)_op_data;      /* "op data" from v2 B-tree find */
2073
2074
0
    FUNC_ENTER_PACKAGE_NOERR
2075
2076
    /*
2077
     * Check arguments.
2078
     */
2079
0
    assert(record);
2080
0
    assert(op_data);
2081
2082
    /* Make a copy of the record */
2083
0
    *op_data = *record;
2084
2085
0
    FUNC_LEAVE_NOAPI(SUCCEED)
2086
0
} /* end H5SM__get_refcount_bt2_cb() */
2087
2088
/*-------------------------------------------------------------------------
2089
 * Function:    H5SM_get_refcount
2090
 *
2091
 * Purpose:     Retrieve the reference count for a message shared in the heap
2092
 *
2093
 * Return:      Non-negative on success/Negative on failure
2094
 *
2095
 *-------------------------------------------------------------------------
2096
 */
2097
herr_t
2098
H5SM_get_refcount(H5F_t *f, unsigned type_id, const H5O_shared_t *sh_mesg, hsize_t *ref_count)
2099
0
{
2100
0
    H5HF_t               *fheap = NULL;           /* Fractal heap that contains shared messages */
2101
0
    H5B2_t               *bt2   = NULL;           /* v2 B-tree handle for index */
2102
0
    H5SM_master_table_t  *table = NULL;           /* SOHM master table */
2103
0
    H5SM_table_cache_ud_t tbl_cache_udata;        /* User-data for callback */
2104
0
    H5SM_list_t          *list   = NULL;          /* SOHM index list for message type (if in list form) */
2105
0
    H5SM_index_header_t  *header = NULL;          /* Index header for message type */
2106
0
    H5SM_mesg_key_t       key;                    /* Key for looking up message */
2107
0
    H5SM_sohm_t           message;                /* Shared message returned from callback */
2108
0
    ssize_t               index_num;              /* Table index for message type */
2109
0
    size_t                buf_size;               /* Size of the encoded message */
2110
0
    void                 *encoding_buf = NULL;    /* Buffer for encoded message */
2111
0
    herr_t                ret_value    = SUCCEED; /* Return value */
2112
2113
0
    FUNC_ENTER_NOAPI_NOINIT_TAG(H5AC__SOHM_TAG)
2114
2115
    /* Sanity check */
2116
0
    assert(f);
2117
0
    assert(sh_mesg);
2118
0
    assert(ref_count);
2119
2120
    /* Set up user data for callback */
2121
0
    tbl_cache_udata.f = f;
2122
2123
    /* Look up the master SOHM table */
2124
0
    if (NULL == (table = (H5SM_master_table_t *)H5AC_protect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f),
2125
0
                                                             &tbl_cache_udata, H5AC__READ_ONLY_FLAG)))
2126
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM master table");
2127
2128
    /* Find the correct index and find the message in it */
2129
0
    if (H5SM__get_index(table, type_id, &index_num) < 0)
2130
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "unable to check for SOHM index");
2131
0
    if (index_num < 0)
2132
0
        HGOTO_ERROR(H5E_SOHM, H5E_NOTFOUND, FAIL, "unable to find correct SOHM index");
2133
0
    header = &(table->indexes[index_num]);
2134
2135
    /* Open the heap for this message type */
2136
0
    if (NULL == (fheap = H5HF_open(f, header->heap_addr)))
2137
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTOPENOBJ, FAIL, "unable to open fractal heap");
2138
2139
    /* Set up a SOHM message to correspond to the shared message passed in */
2140
0
    key.message.location             = H5SM_IN_HEAP;
2141
0
    key.message.u.heap_loc.fheap_id  = sh_mesg->u.heap_id;
2142
0
    key.message.u.heap_loc.ref_count = 0; /* Ref count isn't needed to find message */
2143
2144
    /* Get the encoded message */
2145
0
    if (H5SM__read_mesg(f, &key.message, fheap, NULL, &buf_size, &encoding_buf) < 0)
2146
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTOPENOBJ, FAIL, "unable to open fractal heap");
2147
2148
    /* Set up key for message to locate */
2149
0
    key.file          = f;
2150
0
    key.fheap         = fheap;
2151
0
    key.encoding      = encoding_buf;
2152
0
    key.encoding_size = buf_size;
2153
0
    key.message.hash  = H5_checksum_lookup3(encoding_buf, buf_size, type_id);
2154
2155
    /* Try to find the message in the index */
2156
0
    if (header->index_type == H5SM_LIST) {
2157
0
        H5SM_list_cache_ud_t lst_cache_udata; /* User-data for metadata cache callback */
2158
0
        size_t               list_pos;        /* Position of the message in the list */
2159
2160
        /* Set up user data for metadata cache callback */
2161
0
        lst_cache_udata.f      = f;
2162
0
        lst_cache_udata.header = header;
2163
2164
        /* If the index is stored as a list, get it from the cache */
2165
0
        if (NULL == (list = (H5SM_list_t *)H5AC_protect(f, H5AC_SOHM_LIST, header->index_addr,
2166
0
                                                        &lst_cache_udata, H5AC__READ_ONLY_FLAG)))
2167
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM index");
2168
2169
        /* Find the message in the list */
2170
0
        if (H5SM__find_in_list(list, &key, NULL, &list_pos) < 0)
2171
0
            HGOTO_ERROR(H5E_SOHM, H5E_NOTFOUND, FAIL, "unable to search for message in list");
2172
0
        if (list_pos == SIZE_MAX)
2173
0
            HGOTO_ERROR(H5E_SOHM, H5E_NOTFOUND, FAIL, "message not in index");
2174
2175
        /* Copy the message */
2176
0
        message = list->messages[list_pos];
2177
0
    } /* end if */
2178
0
    else {
2179
0
        bool msg_exists; /* Whether the message exists in the v2 B-tree */
2180
2181
        /* Index is a B-tree */
2182
0
        assert(header->index_type == H5SM_BTREE);
2183
2184
        /* Open the index v2 B-tree */
2185
0
        if (NULL == (bt2 = H5B2_open(f, header->index_addr, f)))
2186
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTOPENOBJ, FAIL, "unable to open v2 B-tree for SOHM index");
2187
2188
        /* Look up the message in the v2 B-tree */
2189
0
        msg_exists = false;
2190
0
        if (H5B2_find(bt2, &key, &msg_exists, H5SM__get_refcount_bt2_cb, &message) < 0)
2191
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "error finding message in index");
2192
0
        if (!msg_exists)
2193
0
            HGOTO_ERROR(H5E_SOHM, H5E_NOTFOUND, FAIL, "message not in index");
2194
0
    } /* end else */
2195
2196
    /* Set the refcount for the message */
2197
0
    assert(message.location == H5SM_IN_HEAP);
2198
0
    *ref_count = message.u.heap_loc.ref_count;
2199
2200
0
done:
2201
    /* Release resources */
2202
0
    if (list && H5AC_unprotect(f, H5AC_SOHM_LIST, header->index_addr, list, H5AC__NO_FLAGS_SET) < 0)
2203
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM index");
2204
0
    if (table && H5AC_unprotect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f), table, H5AC__NO_FLAGS_SET) < 0)
2205
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM master table");
2206
0
    if (fheap && H5HF_close(fheap) < 0)
2207
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close fractal heap");
2208
0
    if (bt2 && H5B2_close(bt2) < 0)
2209
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close v2 B-tree for SOHM index");
2210
0
    if (encoding_buf)
2211
0
        encoding_buf = H5MM_xfree(encoding_buf);
2212
2213
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
2214
0
} /* end H5SM_get_refcount() */
2215
2216
/*-------------------------------------------------------------------------
2217
 * Function:  H5SM__read_iter_op
2218
 *
2219
 * Purpose: OH iteration callback to get the encoded version of a message
2220
 *              by index.
2221
 *
2222
 *              The buffer needs to be freed.
2223
 *
2224
 * Return:  0 if this is not the message we're searching for
2225
 *              1 if this is the message we're searching for (with encoded
2226
 *                      value returned in udata)
2227
 *              negative on error
2228
 *
2229
 *-------------------------------------------------------------------------
2230
 */
2231
static herr_t
2232
H5SM__read_iter_op(H5O_t *oh, H5O_mesg_t *mesg /*in,out*/, unsigned sequence,
2233
                   unsigned H5_ATTR_UNUSED *oh_modified, void *_udata /*in,out*/)
2234
0
{
2235
0
    H5SM_read_udata_t *udata     = (H5SM_read_udata_t *)_udata;
2236
0
    herr_t             ret_value = H5_ITER_CONT;
2237
2238
0
    FUNC_ENTER_PACKAGE
2239
2240
    /*
2241
     * Check arguments.
2242
     */
2243
0
    assert(oh);
2244
0
    assert(mesg);
2245
0
    assert(udata);
2246
0
    assert(NULL == udata->encoding_buf);
2247
2248
    /* Check the creation index for this message */
2249
0
    if (sequence == udata->idx) {
2250
        /* Check if the message is dirty & flush it to the object header if so */
2251
0
        if (mesg->dirty)
2252
0
            if (H5O_msg_flush(udata->file, oh, mesg) < 0)
2253
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTENCODE, H5_ITER_ERROR,
2254
0
                            "unable to encode object header message");
2255
2256
        /* Get the message's encoded size */
2257
0
        udata->buf_size = mesg->raw_size;
2258
0
        assert(udata->buf_size);
2259
2260
        /* Allocate buffer to return the message in */
2261
0
        if (NULL == (udata->encoding_buf = H5MM_malloc(udata->buf_size)))
2262
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTALLOC, H5_ITER_ERROR, "memory allocation failed");
2263
2264
        /* Copy the encoded message into the buffer to return */
2265
0
        H5MM_memcpy(udata->encoding_buf, mesg->raw, udata->buf_size);
2266
2267
        /* Found the message we were looking for */
2268
0
        ret_value = H5_ITER_STOP;
2269
0
    } /* end if */
2270
2271
0
done:
2272
0
    FUNC_LEAVE_NOAPI(ret_value)
2273
0
} /* end H5SM__read_iter_op() */
2274
2275
/*-------------------------------------------------------------------------
2276
 * Function:  H5SM__read_mesg_fh_cb
2277
 *
2278
 * Purpose: Callback for H5HF_op, used in H5SM__read_mesg below.
2279
 *              Makes a copy of the message in the heap data, returned in the
2280
 *              UDATA struct.
2281
 *
2282
 * Return:  Negative on error, non-negative on success
2283
 *
2284
 *-------------------------------------------------------------------------
2285
 */
2286
static herr_t
2287
H5SM__read_mesg_fh_cb(const void *obj, size_t obj_len, void *_udata)
2288
0
{
2289
0
    H5SM_read_udata_t *udata     = (H5SM_read_udata_t *)_udata;
2290
0
    herr_t             ret_value = SUCCEED;
2291
2292
0
    FUNC_ENTER_PACKAGE
2293
2294
    /* Allocate a buffer to hold the message */
2295
0
    if (NULL == (udata->encoding_buf = H5MM_malloc(obj_len)))
2296
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTALLOC, FAIL, "memory allocation failed");
2297
2298
    /* Copy the message from the heap */
2299
0
    H5MM_memcpy(udata->encoding_buf, obj, obj_len);
2300
0
    udata->buf_size = obj_len;
2301
2302
0
done:
2303
0
    FUNC_LEAVE_NOAPI(ret_value)
2304
0
} /* end H5SM__read_mesg_fh_cb() */
2305
2306
/*-------------------------------------------------------------------------
2307
 * Function:  H5SM__read_mesg
2308
 *
2309
 * Purpose: Given an H5SM_sohm_t sohm, encodes the message into a buffer.
2310
 *              This buffer should then be freed.
2311
 *
2312
 * Return:  Non-negative on success/negative on error
2313
 *
2314
 *-------------------------------------------------------------------------
2315
 */
2316
static herr_t
2317
H5SM__read_mesg(H5F_t *f, const H5SM_sohm_t *mesg, H5HF_t *fheap, H5O_t *open_oh,
2318
                size_t *encoding_size /*out*/, void **encoded_mesg /*out*/)
2319
0
{
2320
0
    H5SM_read_udata_t udata;            /* User data for callbacks */
2321
0
    H5O_loc_t         oloc;             /* Object location for message in object header */
2322
0
    H5O_t            *oh        = NULL; /* Object header for message in object header */
2323
0
    herr_t            ret_value = SUCCEED;
2324
2325
0
    FUNC_ENTER_PACKAGE
2326
2327
0
    assert(f);
2328
0
    assert(mesg);
2329
0
    assert(fheap);
2330
2331
    /* Set up user data for message iteration */
2332
0
    udata.file         = f;
2333
0
    udata.idx          = mesg->u.mesg_loc.index;
2334
0
    udata.encoding_buf = NULL;
2335
0
    udata.idx          = 0;
2336
2337
    /* Get the message size and encoded message for the message to be deleted,
2338
     * either from its OH or from the heap.
2339
     */
2340
0
    if (mesg->location == H5SM_IN_OH) {
2341
        /* Read message from object header */
2342
0
        const H5O_msg_class_t *type = NULL; /* Actual H5O class type for the ID */
2343
0
        H5O_mesg_operator_t    op;          /* Wrapper for operator */
2344
2345
0
        type = H5O_msg_class_g[mesg->msg_type_id]; /* map the type ID to the actual type object */
2346
0
        assert(type);
2347
2348
        /* Reset object location for operation */
2349
0
        if (H5O_loc_reset(&oloc) < 0)
2350
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTRESET, FAIL, "unable to initialize location");
2351
2352
0
        if (NULL == open_oh || mesg->u.mesg_loc.oh_addr != H5O_OH_GET_ADDR(open_oh)) {
2353
            /* Open the object in the file */
2354
0
            oloc.file = f;
2355
0
            oloc.addr = mesg->u.mesg_loc.oh_addr;
2356
0
            if (H5O_open(&oloc) < 0)
2357
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTLOAD, FAIL, "unable to open object header");
2358
2359
            /* Load the object header from the cache */
2360
0
            if (NULL == (oh = H5O_protect(&oloc, H5AC__READ_ONLY_FLAG, false)))
2361
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load object header");
2362
0
        } /* end if */
2363
0
        else
2364
0
            oh = open_oh;
2365
2366
        /* Use the "real" iterate routine so it doesn't try to protect the OH */
2367
0
        op.op_type  = H5O_MESG_OP_LIB;
2368
0
        op.u.lib_op = H5SM__read_iter_op;
2369
0
        if ((ret_value = H5O__msg_iterate_real(f, oh, type, &op, &udata)) < 0)
2370
0
            HGOTO_ERROR(H5E_SOHM, H5E_BADITER, FAIL, "unable to iterate over object header messages");
2371
0
    } /* end if */
2372
0
    else {
2373
0
        assert(mesg->location == H5SM_IN_HEAP);
2374
2375
        /* Copy the message from the heap */
2376
0
        if (H5HF_op(fheap, &(mesg->u.heap_loc.fheap_id), H5SM__read_mesg_fh_cb, &udata) < 0)
2377
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTLOAD, FAIL, "can't read message from fractal heap.");
2378
0
    } /* end else */
2379
0
    assert(udata.encoding_buf);
2380
0
    assert(udata.buf_size);
2381
2382
    /* Record the returned values */
2383
0
    *encoded_mesg  = udata.encoding_buf;
2384
0
    *encoding_size = udata.buf_size;
2385
2386
0
done:
2387
    /* Close the object header if we opened one and had an error */
2388
0
    if (oh && oh != open_oh) {
2389
0
        if (oh && H5O_unprotect(&oloc, oh, H5AC__NO_FLAGS_SET) < 0)
2390
0
            HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to release object header");
2391
0
        if (H5O_close(&oloc, NULL) < 0)
2392
0
            HDONE_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "unable to close object header");
2393
0
    } /* end if */
2394
2395
    /* Release the encoding buffer on error */
2396
0
    if (ret_value < 0 && udata.encoding_buf)
2397
0
        udata.encoding_buf = H5MM_xfree(udata.encoding_buf);
2398
2399
0
    FUNC_LEAVE_NOAPI(ret_value)
2400
0
} /* end H5SM__read_mesg */
2401
2402
/*-------------------------------------------------------------------------
2403
 * Function:  H5SM__table_free
2404
 *
2405
 * Purpose: Frees memory used by the SOHM table.
2406
 *
2407
 * Return:  Non-negative on success/Negative on failure
2408
 *
2409
 *-------------------------------------------------------------------------
2410
 */
2411
herr_t
2412
H5SM__table_free(H5SM_master_table_t *table)
2413
0
{
2414
0
    FUNC_ENTER_PACKAGE_NOERR
2415
2416
    /* Sanity check */
2417
0
    assert(table);
2418
0
    assert(table->indexes);
2419
2420
0
    table->indexes = H5FL_ARR_FREE(H5SM_index_header_t, table->indexes);
2421
2422
0
    table = H5FL_FREE(H5SM_master_table_t, table);
2423
2424
0
    FUNC_LEAVE_NOAPI(SUCCEED)
2425
0
} /* end H5SM__table_free() */
2426
2427
/*-------------------------------------------------------------------------
2428
 * Function:  H5SM__list_free
2429
 *
2430
 * Purpose: Frees all memory used by the list.
2431
 *
2432
 * Return:  Non-negative on success/Negative on failure
2433
 *
2434
 *-------------------------------------------------------------------------
2435
 */
2436
herr_t
2437
H5SM__list_free(H5SM_list_t *list)
2438
0
{
2439
0
    FUNC_ENTER_PACKAGE_NOERR
2440
2441
0
    assert(list);
2442
0
    assert(list->messages);
2443
2444
0
    list->messages = H5FL_ARR_FREE(H5SM_sohm_t, list->messages);
2445
2446
0
    list = H5FL_FREE(H5SM_list_t, list);
2447
2448
0
    FUNC_LEAVE_NOAPI(SUCCEED)
2449
0
} /* end H5SM__list_free() */
2450
2451
/*-------------------------------------------------------------------------
2452
 * Function:    H5SM_table_debug
2453
 *
2454
 * Purpose:     Print debugging information for the master table.
2455
 *
2456
 *              If table_vers and num_indexes are not UINT_MAX, they are used
2457
 *              instead of the values in the superblock.
2458
 *
2459
 * Return:      Non-negative on success/Negative on failure
2460
 *
2461
 *-------------------------------------------------------------------------
2462
 */
2463
herr_t
2464
H5SM_table_debug(H5F_t *f, haddr_t table_addr, FILE *stream, int indent, int fwidth, unsigned table_vers,
2465
                 unsigned num_indexes)
2466
0
{
2467
0
    H5SM_master_table_t  *table = NULL;        /* SOHM master table */
2468
0
    H5SM_table_cache_ud_t cache_udata;         /* User-data for callback */
2469
0
    unsigned              x;                   /* Counter variable */
2470
0
    herr_t                ret_value = SUCCEED; /* Return value */
2471
2472
0
    FUNC_ENTER_NOAPI_TAG(H5AC__SOHM_TAG, FAIL)
2473
2474
0
    assert(f);
2475
0
    assert(table_addr != HADDR_UNDEF);
2476
0
    assert(stream);
2477
0
    assert(indent >= 0);
2478
0
    assert(fwidth >= 0);
2479
2480
    /* If table_vers and num_indexes are UINT_MAX, replace them with values from
2481
     * userblock
2482
     */
2483
0
    if (table_vers == UINT_MAX)
2484
0
        table_vers = H5F_SOHM_VERS(f);
2485
0
    else if (table_vers != H5F_SOHM_VERS(f))
2486
0
        fprintf(stream, "*** SOHM TABLE VERSION DOESN'T MATCH VERSION IN SUPERBLOCK!\n");
2487
0
    if (num_indexes == UINT_MAX)
2488
0
        num_indexes = H5F_SOHM_NINDEXES(f);
2489
0
    else if (num_indexes != H5F_SOHM_NINDEXES(f))
2490
0
        fprintf(stream, "*** NUMBER OF SOHM INDEXES DOESN'T MATCH VALUE IN SUPERBLOCK!\n");
2491
2492
    /* Check arguments.  Version must be 0, the only version implemented so far */
2493
0
    if (table_vers > HDF5_SHAREDHEADER_VERSION)
2494
0
        HGOTO_ERROR(H5E_SOHM, H5E_BADVALUE, FAIL, "unknown shared message table version");
2495
0
    if (num_indexes == 0 || num_indexes > H5O_SHMESG_MAX_NINDEXES)
2496
0
        HGOTO_ERROR(H5E_SOHM, H5E_BADVALUE, FAIL,
2497
0
                    "number of indexes must be between 1 and H5O_SHMESG_MAX_NINDEXES");
2498
2499
    /* Set up user data for callback */
2500
0
    cache_udata.f = f;
2501
2502
    /* Look up the master SOHM table */
2503
0
    if (NULL == (table = (H5SM_master_table_t *)H5AC_protect(f, H5AC_SOHM_TABLE, table_addr, &cache_udata,
2504
0
                                                             H5AC__READ_ONLY_FLAG)))
2505
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM master table");
2506
2507
0
    fprintf(stream, "%*sShared Message Master Table...\n", indent, "");
2508
0
    for (x = 0; x < num_indexes; ++x) {
2509
0
        fprintf(stream, "%*sIndex %d...\n", indent, "", x);
2510
0
        fprintf(stream, "%*s%-*s %s\n", indent + 3, "", fwidth, "SOHM Index Type:",
2511
0
                (table->indexes[x].index_type == H5SM_LIST
2512
0
                     ? "List"
2513
0
                     : (table->indexes[x].index_type == H5SM_BTREE ? "B-Tree" : "Unknown")));
2514
2515
0
        fprintf(stream, "%*s%-*s %" PRIuHADDR "\n", indent + 3, "", fwidth,
2516
0
                "Address of index:", table->indexes[x].index_addr);
2517
0
        fprintf(stream, "%*s%-*s %" PRIuHADDR "\n", indent + 3, "", fwidth,
2518
0
                "Address of index's heap:", table->indexes[x].heap_addr);
2519
0
        fprintf(stream, "%*s%-*s 0x%08x\n", indent + 3, "", fwidth,
2520
0
                "Message type flags:", table->indexes[x].mesg_types);
2521
0
        fprintf(stream, "%*s%-*s %zu\n", indent + 3, "", fwidth,
2522
0
                "Minimum size of messages:", table->indexes[x].min_mesg_size);
2523
0
        fprintf(stream, "%*s%-*s %zu\n", indent + 3, "", fwidth,
2524
0
                "Number of messages:", table->indexes[x].num_messages);
2525
0
        fprintf(stream, "%*s%-*s %zu\n", indent + 3, "", fwidth,
2526
0
                "Maximum list size:", table->indexes[x].list_max);
2527
0
        fprintf(stream, "%*s%-*s %zu\n", indent + 3, "", fwidth,
2528
0
                "Minimum B-tree size:", table->indexes[x].btree_min);
2529
0
    } /* end for */
2530
2531
0
done:
2532
0
    if (table && H5AC_unprotect(f, H5AC_SOHM_TABLE, table_addr, table, H5AC__NO_FLAGS_SET) < 0)
2533
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM master table");
2534
2535
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
2536
0
} /* end H5SM_table_debug() */
2537
2538
/*-------------------------------------------------------------------------
2539
 * Function:    H5SM_list_debug
2540
 *
2541
 * Purpose:     Print debugging information for a SOHM list.
2542
 *
2543
 *              Relies on the list version and number of messages passed in.
2544
 *
2545
 * Return:      Non-negative on success/Negative on failure
2546
 *
2547
 *-------------------------------------------------------------------------
2548
 */
2549
herr_t
2550
H5SM_list_debug(H5F_t *f, haddr_t list_addr, FILE *stream, int indent, int fwidth, haddr_t table_addr)
2551
0
{
2552
0
    H5SM_master_table_t  *table = NULL;        /* SOHM master table */
2553
0
    H5SM_list_t          *list  = NULL;        /* SOHM index list for message type (if in list form) */
2554
0
    H5SM_list_cache_ud_t  lst_cache_udata;     /* List user-data for metadata cache callback */
2555
0
    H5SM_table_cache_ud_t tbl_cache_udata;     /* Table user-data for metadata cache callback */
2556
0
    H5HF_t               *fh = NULL;           /* Fractal heap for SOHM messages */
2557
0
    unsigned              index_num;           /* Index of list, within master table */
2558
0
    unsigned              x;                   /* Counter variable */
2559
0
    herr_t                ret_value = SUCCEED; /* Return value */
2560
2561
0
    FUNC_ENTER_NOAPI_TAG(H5AC__SOHM_TAG, FAIL)
2562
2563
0
    assert(f);
2564
0
    assert(list_addr != HADDR_UNDEF);
2565
0
    assert(stream);
2566
0
    assert(indent >= 0);
2567
0
    assert(fwidth >= 0);
2568
2569
    /* Set up user data for callback */
2570
0
    tbl_cache_udata.f = f;
2571
2572
    /* Look up the master SOHM table */
2573
0
    if (NULL == (table = (H5SM_master_table_t *)H5AC_protect(f, H5AC_SOHM_TABLE, table_addr, &tbl_cache_udata,
2574
0
                                                             H5AC__READ_ONLY_FLAG)))
2575
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM master table");
2576
2577
    /* Determine which index the list is part of */
2578
0
    index_num = table->num_indexes;
2579
0
    for (x = 0; x < table->num_indexes; x++) {
2580
0
        if (H5_addr_eq(table->indexes[x].index_addr, list_addr)) {
2581
0
            index_num = x;
2582
0
            break;
2583
0
        } /* end if */
2584
0
    }     /* end for */
2585
0
    if (x == table->num_indexes)
2586
0
        HGOTO_ERROR(H5E_SOHM, H5E_BADVALUE, FAIL,
2587
0
                    "list address doesn't match address for any indices in table");
2588
2589
    /* Set up user data for metadata cache callback */
2590
0
    lst_cache_udata.f      = f;
2591
0
    lst_cache_udata.header = &(table->indexes[index_num]);
2592
2593
    /* Get the list from the cache */
2594
0
    if (NULL == (list = (H5SM_list_t *)H5AC_protect(f, H5AC_SOHM_LIST, list_addr, &lst_cache_udata,
2595
0
                                                    H5AC__READ_ONLY_FLAG)))
2596
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM index");
2597
2598
    /* Open the heap, if one exists */
2599
0
    if (H5_addr_defined(table->indexes[index_num].heap_addr))
2600
0
        if (NULL == (fh = H5HF_open(f, table->indexes[index_num].heap_addr)))
2601
0
            HGOTO_ERROR(H5E_SOHM, H5E_CANTOPENOBJ, FAIL, "unable to open SOHM heap");
2602
2603
0
    fprintf(stream, "%*sShared Message List Index...\n", indent, "");
2604
0
    for (x = 0; x < table->indexes[index_num].num_messages; ++x) {
2605
0
        fprintf(stream, "%*sShared Object Header Message %d...\n", indent, "", x);
2606
0
        fprintf(stream, "%*s%-*s %08lu\n", indent + 3, "", fwidth,
2607
0
                "Hash value:", (unsigned long)list->messages[x].hash);
2608
0
        if (list->messages[x].location == H5SM_IN_HEAP) {
2609
0
            assert(fh);
2610
2611
0
            fprintf(stream, "%*s%-*s %s\n", indent + 3, "", fwidth, "Location:", "in heap");
2612
0
            fprintf(stream, "%*s%-*s 0x%" PRIx64 "\n", indent + 3, "", fwidth,
2613
0
                    "Heap ID:", list->messages[x].u.heap_loc.fheap_id.val);
2614
0
            fprintf(stream, "%*s%-*s %" PRIuHSIZE "\n", indent + 3, "", fwidth,
2615
0
                    "Reference count:", list->messages[x].u.heap_loc.ref_count);
2616
0
        } /* end if */
2617
0
        else if (list->messages[x].location == H5SM_IN_OH) {
2618
0
            fprintf(stream, "%*s%-*s %s\n", indent + 3, "", fwidth, "Location:", "in object header");
2619
0
            fprintf(stream, "%*s%-*s %" PRIuHADDR "\n", indent + 3, "", fwidth,
2620
0
                    "Object header address:", list->messages[x].u.mesg_loc.oh_addr);
2621
0
            fprintf(stream, "%*s%-*s %" PRIuHADDR "\n", indent + 3, "", fwidth,
2622
0
                    "Message creation index:", list->messages[x].u.mesg_loc.oh_addr);
2623
0
            fprintf(stream, "%*s%-*s %u\n", indent + 3, "", fwidth,
2624
0
                    "Message type ID:", list->messages[x].msg_type_id);
2625
0
        } /* end if */
2626
0
        else
2627
0
            fprintf(stream, "%*s%-*s %s\n", indent + 3, "", fwidth, "Location:", "invalid");
2628
0
    } /* end for */
2629
2630
0
done:
2631
0
    if (fh && H5HF_close(fh) < 0)
2632
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "unable to close SOHM heap");
2633
0
    if (list && H5AC_unprotect(f, H5AC_SOHM_LIST, list_addr, list, H5AC__NO_FLAGS_SET) < 0)
2634
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM index");
2635
0
    if (table && H5AC_unprotect(f, H5AC_SOHM_TABLE, table_addr, table, H5AC__NO_FLAGS_SET) < 0)
2636
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM master table");
2637
2638
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
2639
0
} /* end H5SM_list_debug() */
2640
2641
/*-------------------------------------------------------------------------
2642
 * Function:    H5SM_ih_size
2643
 *
2644
 * Purpose:     Loop through the master SOHM table (if there is one) to:
2645
 *      1. collect storage used for header
2646
 *                      1. collect storage used for B-tree and List
2647
 *         (include btree storage used by huge objects in fractal heap)
2648
 *                      2. collect fractal heap storage
2649
 *
2650
 * Return:      Non-negative on success/Negative on failure
2651
 *
2652
 *-------------------------------------------------------------------------
2653
 */
2654
herr_t
2655
H5SM_ih_size(H5F_t *f, hsize_t *hdr_size, H5_ih_info_t *ih_info)
2656
0
{
2657
0
    H5SM_master_table_t  *table = NULL;        /* SOHM master table */
2658
0
    H5SM_table_cache_ud_t cache_udata;         /* User-data for callback */
2659
0
    H5HF_t               *fheap = NULL;        /* Fractal heap handle */
2660
0
    H5B2_t               *bt2   = NULL;        /* v2 B-tree handle for index */
2661
0
    unsigned              u;                   /* Local index variable */
2662
0
    herr_t                ret_value = SUCCEED; /* Return value */
2663
2664
0
    FUNC_ENTER_NOAPI_TAG(H5AC__SOHM_TAG, FAIL)
2665
2666
    /* Sanity check */
2667
0
    assert(f);
2668
0
    assert(H5_addr_defined(H5F_SOHM_ADDR(f)));
2669
0
    assert(hdr_size);
2670
0
    assert(ih_info);
2671
2672
    /* Set up user data for callback */
2673
0
    cache_udata.f = f;
2674
2675
    /* Look up the master SOHM table */
2676
0
    if (NULL == (table = (H5SM_master_table_t *)H5AC_protect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f),
2677
0
                                                             &cache_udata, H5AC__READ_ONLY_FLAG)))
2678
0
        HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load SOHM master table");
2679
2680
    /* Get SOHM header size */
2681
0
    *hdr_size = table->table_size;
2682
2683
    /* Loop over all the indices for shared messages */
2684
0
    for (u = 0; u < table->num_indexes; u++) {
2685
        /* Get index storage size (for either B-tree or list) */
2686
0
        if (table->indexes[u].index_type == H5SM_BTREE) {
2687
0
            if (H5_addr_defined(table->indexes[u].index_addr)) {
2688
                /* Open the index v2 B-tree */
2689
0
                if (NULL == (bt2 = H5B2_open(f, table->indexes[u].index_addr, f)))
2690
0
                    HGOTO_ERROR(H5E_SOHM, H5E_CANTOPENOBJ, FAIL, "unable to open v2 B-tree for SOHM index");
2691
2692
0
                if (H5B2_size(bt2, &(ih_info->index_size)) < 0)
2693
0
                    HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "can't retrieve B-tree storage info");
2694
2695
                /* Close the v2 B-tree */
2696
0
                if (H5B2_close(bt2) < 0)
2697
0
                    HGOTO_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close v2 B-tree for SOHM index");
2698
0
                bt2 = NULL;
2699
0
            } /* end if */
2700
0
        }     /* end if */
2701
0
        else {
2702
0
            assert(table->indexes[u].index_type == H5SM_LIST);
2703
0
            ih_info->index_size += table->indexes[u].list_size;
2704
0
        } /* end else */
2705
2706
        /* Check for heap for this index */
2707
0
        if (H5_addr_defined(table->indexes[u].heap_addr)) {
2708
            /* Open the fractal heap for this index */
2709
0
            if (NULL == (fheap = H5HF_open(f, table->indexes[u].heap_addr)))
2710
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTOPENOBJ, FAIL, "unable to open fractal heap");
2711
2712
            /* Get heap storage size */
2713
0
            if (H5HF_size(fheap, &(ih_info->heap_size)) < 0)
2714
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTGET, FAIL, "can't retrieve fractal heap storage info");
2715
2716
            /* Close the fractal heap */
2717
0
            if (H5HF_close(fheap) < 0)
2718
0
                HGOTO_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close fractal heap");
2719
0
            fheap = NULL;
2720
0
        } /* end if */
2721
0
    }     /* end for */
2722
2723
0
done:
2724
    /* Release resources */
2725
0
    if (fheap && H5HF_close(fheap) < 0)
2726
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close fractal heap");
2727
0
    if (bt2 && H5B2_close(bt2) < 0)
2728
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTCLOSEOBJ, FAIL, "can't close v2 B-tree for SOHM index");
2729
0
    if (table && H5AC_unprotect(f, H5AC_SOHM_TABLE, H5F_SOHM_ADDR(f), table, H5AC__NO_FLAGS_SET) < 0)
2730
0
        HDONE_ERROR(H5E_SOHM, H5E_CANTUNPROTECT, FAIL, "unable to close SOHM master table");
2731
2732
0
    FUNC_LEAVE_NOAPI_TAG(ret_value)
2733
0
} /* end H5SM_ih_size() */