Coverage Report

Created: 2026-01-09 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hdf5/src/H5B2internal.c
Line
Count
Source
1
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
 * Copyright by The HDF Group.                                               *
3
 * All rights reserved.                                                      *
4
 *                                                                           *
5
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
6
 * terms governing use, modification, and redistribution, is contained in    *
7
 * the LICENSE file, which can be found at the root of the source code       *
8
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
9
 * If you do not have access to either file, you may request a copy from     *
10
 * help@hdfgroup.org.                                                        *
11
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12
13
/*-------------------------------------------------------------------------
14
 *
15
 * Created:   H5B2internal.c
16
 *
17
 * Purpose:   Routines for managing v2 B-tree internal nodes.
18
 *
19
 *-------------------------------------------------------------------------
20
 */
21
22
/****************/
23
/* Module Setup */
24
/****************/
25
26
#include "H5B2module.h" /* This source code file is part of the H5B2 module */
27
28
/***********/
29
/* Headers */
30
/***********/
31
#include "H5private.h"   /* Generic Functions     */
32
#include "H5B2pkg.h"     /* v2 B-trees        */
33
#include "H5Eprivate.h"  /* Error handling        */
34
#include "H5FLprivate.h" /* Free Lists                               */
35
#include "H5MFprivate.h" /* File memory management    */
36
37
/****************/
38
/* Local Macros */
39
/****************/
40
41
/******************/
42
/* Local Typedefs */
43
/******************/
44
45
/********************/
46
/* Package Typedefs */
47
/********************/
48
49
/********************/
50
/* Local Prototypes */
51
/********************/
52
static herr_t H5B2__shadow_internal(H5B2_internal_t *internal, H5B2_node_ptr_t *curr_node_ptr);
53
54
/*********************/
55
/* Package Variables */
56
/*********************/
57
58
/* Declare a free list to manage the H5B2_internal_t struct */
59
H5FL_DEFINE(H5B2_internal_t);
60
61
/*****************************/
62
/* Library Private Variables */
63
/*****************************/
64
65
/*******************/
66
/* Local Variables */
67
/*******************/
68
69
/*-------------------------------------------------------------------------
70
 * Function:  H5B2__create_internal
71
 *
72
 * Purpose: Creates empty internal node of a B-tree and update node pointer
73
 *              to point to it.
74
 *
75
 * Return:  Non-negative on success/Negative on failure
76
 *
77
 *-------------------------------------------------------------------------
78
 */
79
herr_t
80
H5B2__create_internal(H5B2_hdr_t *hdr, void *parent, H5B2_node_ptr_t *node_ptr, uint16_t depth)
81
0
{
82
0
    H5B2_internal_t *internal  = NULL;    /* Pointer to new internal node created */
83
0
    bool             inserted  = false;   /* Whether the internal node was inserted into cache */
84
0
    herr_t           ret_value = SUCCEED; /* Return value */
85
86
0
    FUNC_ENTER_PACKAGE
87
88
    /* Check arguments. */
89
0
    assert(hdr);
90
0
    assert(node_ptr);
91
0
    assert(depth > 0);
92
93
    /* Allocate memory for internal node information */
94
0
    if (NULL == (internal = H5FL_CALLOC(H5B2_internal_t)))
95
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for B-tree internal info");
96
97
    /* Increment ref. count on B-tree header */
98
0
    if (H5B2__hdr_incr(hdr) < 0)
99
0
        HGOTO_ERROR(H5E_BTREE, H5E_CANTINC, FAIL, "can't increment ref. count on B-tree header");
100
101
    /* Share B-tree header information */
102
0
    internal->hdr = hdr;
103
104
    /* Allocate space for the native keys in memory */
105
0
    if (NULL == (internal->int_native = (uint8_t *)H5FL_FAC_MALLOC(hdr->node_info[depth].nat_rec_fac)))
106
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
107
0
                    "memory allocation failed for B-tree internal native keys");
108
0
    memset(internal->int_native, 0, hdr->cls->nrec_size * hdr->node_info[depth].max_nrec);
109
110
    /* Allocate space for the node pointers in memory */
111
0
    if (NULL ==
112
0
        (internal->node_ptrs = (H5B2_node_ptr_t *)H5FL_FAC_MALLOC(hdr->node_info[depth].node_ptr_fac)))
113
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
114
0
                    "memory allocation failed for B-tree internal node pointers");
115
0
    memset(internal->node_ptrs, 0, sizeof(H5B2_node_ptr_t) * (hdr->node_info[depth].max_nrec + 1));
116
117
    /* Set depth of the node */
118
0
    internal->depth = depth;
119
120
    /* Set parent */
121
0
    internal->parent = parent;
122
123
    /* Set shadowed epoch to header's epoch */
124
0
    internal->shadow_epoch = hdr->shadow_epoch;
125
126
    /* Allocate space on disk for the internal node */
127
0
    if (HADDR_UNDEF == (node_ptr->addr = H5MF_alloc(hdr->f, H5FD_MEM_BTREE, (hsize_t)hdr->node_size)))
128
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "file allocation failed for B-tree internal node");
129
130
    /* Cache the new B-tree node */
131
0
    if (H5AC_insert_entry(hdr->f, H5AC_BT2_INT, node_ptr->addr, internal, H5AC__NO_FLAGS_SET) < 0)
132
0
        HGOTO_ERROR(H5E_BTREE, H5E_CANTINIT, FAIL, "can't add B-tree internal node to cache");
133
0
    inserted = true;
134
135
    /* Add internal node as child of 'top' proxy */
136
0
    if (hdr->top_proxy) {
137
0
        if (H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, internal) < 0)
138
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTSET, FAIL, "unable to add v2 B-tree node as child of proxy");
139
0
        internal->top_proxy = hdr->top_proxy;
140
0
    } /* end if */
141
142
0
done:
143
0
    if (ret_value < 0) {
144
0
        if (internal) {
145
            /* Remove from cache, if inserted */
146
0
            if (inserted)
147
0
                if (H5AC_remove_entry(internal) < 0)
148
0
                    HDONE_ERROR(H5E_BTREE, H5E_CANTREMOVE, FAIL,
149
0
                                "unable to remove v2 B-tree internal node from cache");
150
151
            /* Release internal node's disk space */
152
0
            if (H5_addr_defined(node_ptr->addr) &&
153
0
                H5MF_xfree(hdr->f, H5FD_MEM_BTREE, node_ptr->addr, (hsize_t)hdr->node_size) < 0)
154
0
                HDONE_ERROR(H5E_BTREE, H5E_CANTFREE, FAIL,
155
0
                            "unable to release file space for v2 B-tree internal node");
156
157
            /* Destroy internal node */
158
0
            if (H5B2__internal_free(internal) < 0)
159
0
                HDONE_ERROR(H5E_BTREE, H5E_CANTFREE, FAIL, "unable to release v2 B-tree internal node");
160
0
        } /* end if */
161
0
    }     /* end if */
162
163
0
    FUNC_LEAVE_NOAPI(ret_value)
164
0
} /* H5B2__create_internal() */
165
166
/*-------------------------------------------------------------------------
167
 * Function:  H5B2__protect_internal
168
 *
169
 * Purpose: "Protect" an internal node in the metadata cache
170
 *
171
 * Return:  Pointer to internal node on success/NULL on failure
172
 *
173
 *-------------------------------------------------------------------------
174
 */
175
H5B2_internal_t *
176
H5B2__protect_internal(H5B2_hdr_t *hdr, void *parent, H5B2_node_ptr_t *node_ptr, uint16_t depth, bool shadow,
177
                       unsigned flags)
178
0
{
179
0
    H5B2_internal_cache_ud_t udata;            /* User data to pass through to cache 'deserialize' callback */
180
0
    H5B2_internal_t         *internal  = NULL; /* v2 B-tree internal node */
181
0
    H5B2_internal_t         *ret_value = NULL; /* Return value */
182
183
0
    FUNC_ENTER_PACKAGE
184
185
    /* Check arguments. */
186
0
    assert(hdr);
187
0
    assert(node_ptr);
188
0
    assert(H5_addr_defined(node_ptr->addr));
189
0
    assert(depth > 0);
190
191
    /* only H5AC__READ_ONLY_FLAG may appear in flags */
192
0
    assert((flags & (unsigned)(~H5AC__READ_ONLY_FLAG)) == 0);
193
194
    /* Set up user data for callback */
195
0
    udata.f      = hdr->f;
196
0
    udata.hdr    = hdr;
197
0
    udata.parent = parent;
198
0
    udata.nrec   = node_ptr->node_nrec;
199
0
    udata.depth  = depth;
200
201
    /* Protect the internal node */
202
0
    if (NULL ==
203
0
        (internal = (H5B2_internal_t *)H5AC_protect(hdr->f, H5AC_BT2_INT, node_ptr->addr, &udata, flags)))
204
0
        HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, NULL, "unable to protect B-tree internal node");
205
206
    /* Create top proxy, if it doesn't exist */
207
0
    if (hdr->top_proxy && NULL == internal->top_proxy) {
208
        /* Add internal node as child of 'top' proxy */
209
0
        if (H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, internal) < 0)
210
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTSET, NULL,
211
0
                        "unable to add v2 B-tree internal node as child of proxy");
212
0
        internal->top_proxy = hdr->top_proxy;
213
0
    } /* end if */
214
215
    /* Shadow the node, if requested */
216
0
    if (shadow)
217
0
        if (H5B2__shadow_internal(internal, node_ptr) < 0)
218
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTCOPY, NULL, "unable to shadow internal node");
219
220
    /* Set return value */
221
0
    ret_value = internal;
222
223
0
done:
224
    /* Clean up on error */
225
0
    if (!ret_value) {
226
        /* Release the internal node, if it was protected */
227
0
        if (internal) {
228
            /* Remove from v2 B-tree's proxy, if added */
229
0
            if (internal->top_proxy) {
230
0
                if (H5AC_proxy_entry_remove_child(internal->top_proxy, internal) < 0)
231
0
                    HDONE_ERROR(
232
0
                        H5E_BTREE, H5E_CANTUNDEPEND, NULL,
233
0
                        "unable to destroy flush dependency between internal node and v2 B-tree 'top' proxy");
234
0
                internal->top_proxy = NULL;
235
0
            } /* end if */
236
237
            /* Unprotect internal node */
238
0
            if (H5AC_unprotect(hdr->f, H5AC_BT2_INT, node_ptr->addr, internal, H5AC__NO_FLAGS_SET) < 0)
239
0
                HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, NULL,
240
0
                            "unable to unprotect v2 B-tree internal node, address = %llu",
241
0
                            (unsigned long long)node_ptr->addr);
242
0
        } /* end if */
243
0
    }     /* end if */
244
245
0
    FUNC_LEAVE_NOAPI(ret_value)
246
0
} /* H5B2__protect_internal() */
247
248
/*-------------------------------------------------------------------------
249
 * Function:  H5B2__neighbor_internal
250
 *
251
 * Purpose: Locate a record relative to the specified information in a
252
 *              B-tree internal node and return that information by filling in
253
 *              fields of the
254
 *              caller-supplied UDATA pointer depending on the type of leaf node
255
 *    requested.  The UDATA can point to additional data passed
256
 *    to the key comparison function.
257
 *
258
 *              The 'OP' routine is called with the record found and the
259
 *              OP_DATA pointer, to allow caller to return information about
260
 *              the record.
261
 *
262
 *              The RANGE indicates whether to search for records less than or
263
 *              equal to, or greater than or equal to the information passed
264
 *              in with UDATA.
265
 *
266
 * Return:  Non-negative on success, negative on failure.
267
 *
268
 *-------------------------------------------------------------------------
269
 */
270
herr_t
271
H5B2__neighbor_internal(H5B2_hdr_t *hdr, uint16_t depth, H5B2_node_ptr_t *curr_node_ptr, void *neighbor_loc,
272
                        H5B2_compare_t comp, void *parent, void *udata, H5B2_found_t op, void *op_data)
273
0
{
274
0
    H5B2_internal_t *internal;            /* Pointer to internal node */
275
0
    unsigned         idx       = 0;       /* Location of record which matches key */
276
0
    int              cmp       = 0;       /* Comparison value of records */
277
0
    herr_t           ret_value = SUCCEED; /* Return value */
278
279
0
    FUNC_ENTER_PACKAGE
280
281
    /* Check arguments. */
282
0
    assert(hdr);
283
0
    assert(depth > 0);
284
0
    assert(curr_node_ptr);
285
0
    assert(H5_addr_defined(curr_node_ptr->addr));
286
0
    assert(op);
287
288
    /* Lock current B-tree node */
289
0
    if (NULL ==
290
0
        (internal = H5B2__protect_internal(hdr, parent, curr_node_ptr, depth, false, H5AC__READ_ONLY_FLAG)))
291
0
        HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node");
292
293
    /* Locate node pointer for child */
294
0
    if (H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata, &idx, &cmp) <
295
0
        0)
296
0
        HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records");
297
0
    if (cmp > 0)
298
0
        idx++;
299
300
    /* Set the neighbor location, if appropriate */
301
0
    if (comp == H5B2_COMPARE_LESS) {
302
0
        if (idx > 0)
303
0
            neighbor_loc = H5B2_INT_NREC(internal, hdr, idx - 1);
304
0
    } /* end if */
305
0
    else {
306
0
        assert(comp == H5B2_COMPARE_GREATER);
307
308
0
        if (idx < internal->nrec)
309
0
            neighbor_loc = H5B2_INT_NREC(internal, hdr, idx);
310
0
    } /* end else */
311
312
    /* Attempt to find neighboring record */
313
0
    if (depth > 1) {
314
0
        if (H5B2__neighbor_internal(hdr, (uint16_t)(depth - 1), &internal->node_ptrs[idx], neighbor_loc, comp,
315
0
                                    internal, udata, op, op_data) < 0)
316
0
            HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL,
317
0
                        "unable to find neighbor record in B-tree internal node");
318
0
    } /* end if */
319
0
    else {
320
0
        if (H5B2__neighbor_leaf(hdr, &internal->node_ptrs[idx], neighbor_loc, comp, internal, udata, op,
321
0
                                op_data) < 0)
322
0
            HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "unable to find neighbor record in B-tree leaf node");
323
0
    } /* end else */
324
325
0
done:
326
    /* Release the B-tree internal node */
327
0
    if (internal &&
328
0
        H5AC_unprotect(hdr->f, H5AC_BT2_INT, curr_node_ptr->addr, internal, H5AC__NO_FLAGS_SET) < 0)
329
0
        HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node");
330
331
0
    FUNC_LEAVE_NOAPI(ret_value)
332
0
} /* H5B2__neighbor_internal() */
333
334
/*-------------------------------------------------------------------------
335
 * Function:  H5B2__insert_internal
336
 *
337
 * Purpose: Adds a new record to a B-tree node.
338
 *
339
 * Return:  Non-negative on success/Negative on failure
340
 *
341
 *-------------------------------------------------------------------------
342
 */
343
herr_t
344
H5B2__insert_internal(H5B2_hdr_t *hdr, uint16_t depth, unsigned *parent_cache_info_flags_ptr,
345
                      H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos, void *parent, void *udata)
346
0
{
347
0
    H5B2_internal_t *internal       = NULL; /* Pointer to internal node */
348
0
    unsigned         internal_flags = H5AC__NO_FLAGS_SET;
349
0
    unsigned         idx            = 0;               /* Location of record which matches key */
350
0
    H5B2_nodepos_t   next_pos       = H5B2_POS_MIDDLE; /* Position of node */
351
0
    herr_t           ret_value      = SUCCEED;         /* Return value */
352
353
0
    FUNC_ENTER_PACKAGE
354
355
    /* Check arguments. */
356
0
    assert(hdr);
357
0
    assert(depth > 0);
358
0
    assert(curr_node_ptr);
359
0
    assert(H5_addr_defined(curr_node_ptr->addr));
360
361
    /* Lock current B-tree node */
362
0
    if (NULL ==
363
0
        (internal = H5B2__protect_internal(hdr, parent, curr_node_ptr, depth, false, H5AC__NO_FLAGS_SET)))
364
0
        HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node");
365
366
    /* Sanity check number of records */
367
0
    assert(internal->nrec == curr_node_ptr->node_nrec);
368
369
    /* Split or redistribute child node pointers, if necessary */
370
0
    {
371
0
        int      cmp;        /* Comparison value of records */
372
0
        unsigned retries;    /* Number of times to attempt redistribution */
373
0
        size_t   split_nrec; /* Number of records to split node at */
374
375
        /* Locate node pointer for child */
376
0
        if (H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata, &idx,
377
0
                                &cmp) < 0)
378
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records");
379
0
        if (cmp == 0)
380
0
            HGOTO_ERROR(H5E_BTREE, H5E_EXISTS, FAIL, "record is already in B-tree");
381
0
        if (cmp > 0)
382
0
            idx++;
383
384
        /* Set the number of redistribution retries */
385
        /* This takes care of the case where a B-tree node needs to be
386
         * redistributed, but redistributing the node causes the index
387
         * for insertion to move to another node, which also needs to be
388
         * redistributed.  Now, we loop trying to redistribute and then
389
         * eventually force a split */
390
0
        retries = 2;
391
392
        /* Determine the correct number of records to split child node at */
393
0
        split_nrec = hdr->node_info[depth - 1].split_nrec;
394
395
        /* Preemptively split/redistribute a node we will enter */
396
0
        while (internal->node_ptrs[idx].node_nrec == split_nrec) {
397
            /* Attempt to redistribute records among children */
398
0
            if (idx == 0) { /* Left-most child */
399
0
                if (retries > 0 && (internal->node_ptrs[idx + 1].node_nrec < split_nrec)) {
400
0
                    if (H5B2__redistribute2(hdr, depth, internal, idx) < 0)
401
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL,
402
0
                                    "unable to redistribute child node records");
403
0
                } /* end if */
404
0
                else {
405
0
                    if (H5B2__split1(hdr, depth, curr_node_ptr, parent_cache_info_flags_ptr, internal,
406
0
                                     &internal_flags, idx) < 0)
407
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to split child node");
408
0
                }                             /* end else */
409
0
            }                                 /* end if */
410
0
            else if (idx == internal->nrec) { /* Right-most child */
411
0
                if (retries > 0 && (internal->node_ptrs[idx - 1].node_nrec < split_nrec)) {
412
0
                    if (H5B2__redistribute2(hdr, depth, internal, (idx - 1)) < 0)
413
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL,
414
0
                                    "unable to redistribute child node records");
415
0
                } /* end if */
416
0
                else {
417
0
                    if (H5B2__split1(hdr, depth, curr_node_ptr, parent_cache_info_flags_ptr, internal,
418
0
                                     &internal_flags, idx) < 0)
419
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to split child node");
420
0
                }  /* end else */
421
0
            }      /* end if */
422
0
            else { /* Middle child */
423
0
                if (retries > 0 && ((internal->node_ptrs[idx + 1].node_nrec < split_nrec) ||
424
0
                                    (internal->node_ptrs[idx - 1].node_nrec < split_nrec))) {
425
0
                    if (H5B2__redistribute3(hdr, depth, internal, &internal_flags, idx) < 0)
426
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL,
427
0
                                    "unable to redistribute child node records");
428
0
                } /* end if */
429
0
                else {
430
0
                    if (H5B2__split1(hdr, depth, curr_node_ptr, parent_cache_info_flags_ptr, internal,
431
0
                                     &internal_flags, idx) < 0)
432
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to split child node");
433
0
                } /* end else */
434
0
            }     /* end else */
435
436
            /* Locate node pointer for child (after split/redistribute) */
437
            /* Actually, this can be easily updated (for 2-node redistrib.) and shouldn't require re-searching
438
             */
439
0
            if (H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata, &idx,
440
0
                                    &cmp) < 0)
441
0
                HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records");
442
0
            if (cmp == 0)
443
0
                HGOTO_ERROR(H5E_BTREE, H5E_EXISTS, FAIL, "record is already in B-tree");
444
0
            if (cmp > 0)
445
0
                idx++;
446
447
            /* Decrement the number of redistribution retries left */
448
0
            retries--;
449
0
        } /* end while */
450
0
    }     /* end block */
451
452
    /* Check if this node is left/right-most */
453
0
    if (H5B2_POS_MIDDLE != curr_pos) {
454
0
        if (idx == 0) {
455
0
            if (H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos)
456
0
                next_pos = H5B2_POS_LEFT;
457
0
        } /* end if */
458
0
        else if (idx == internal->nrec) {
459
0
            if (H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos)
460
0
                next_pos = H5B2_POS_RIGHT;
461
0
        } /* end else */
462
0
    }     /* end if */
463
464
    /* Attempt to insert node */
465
0
    if (depth > 1) {
466
0
        if (H5B2__insert_internal(hdr, (uint16_t)(depth - 1), &internal_flags, &internal->node_ptrs[idx],
467
0
                                  next_pos, internal, udata) < 0)
468
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into B-tree internal node");
469
0
    } /* end if */
470
0
    else {
471
0
        if (H5B2__insert_leaf(hdr, &internal->node_ptrs[idx], next_pos, internal, udata) < 0)
472
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into B-tree leaf node");
473
0
    } /* end else */
474
475
    /* Update record count for node pointer to current node */
476
0
    curr_node_ptr->all_nrec++;
477
478
    /* Mark node as dirty */
479
0
    internal_flags |= H5AC__DIRTIED_FLAG;
480
481
0
done:
482
    /* Release the B-tree internal node */
483
0
    if (internal) {
484
        /* Shadow the node if doing SWMR writes */
485
0
        if (hdr->swmr_write && (internal_flags & H5AC__DIRTIED_FLAG))
486
0
            if (H5B2__shadow_internal(internal, curr_node_ptr) < 0)
487
0
                HDONE_ERROR(H5E_BTREE, H5E_CANTCOPY, FAIL, "unable to shadow internal B-tree node");
488
489
        /* Unprotect node */
490
0
        if (H5AC_unprotect(hdr->f, H5AC_BT2_INT, curr_node_ptr->addr, internal, internal_flags) < 0)
491
0
            HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node");
492
0
    } /* end if */
493
494
0
    FUNC_LEAVE_NOAPI(ret_value)
495
0
} /* H5B2__insert_internal() */
496
497
/*-------------------------------------------------------------------------
498
 * Function:  H5B2__update_internal
499
 *
500
 * Purpose: Insert or modify a record in a B-tree internal node.
501
 *    If the record exists already, it is modified as if H5B2_modify
502
 *    was called).  If it doesn't exist, it is inserted as if
503
 *    H5B2_insert was called.
504
 *
505
 * Return:  Non-negative on success/Negative on failure
506
 *
507
 *-------------------------------------------------------------------------
508
 */
509
herr_t
510
H5B2__update_internal(H5B2_hdr_t *hdr, uint16_t depth, unsigned *parent_cache_info_flags_ptr,
511
                      H5B2_node_ptr_t *curr_node_ptr, H5B2_update_status_t *status, H5B2_nodepos_t curr_pos,
512
                      void *parent, void *udata, H5B2_modify_t op, void *op_data)
513
0
{
514
0
    H5B2_internal_t *internal       = NULL; /* Pointer to internal node */
515
0
    unsigned         internal_flags = H5AC__NO_FLAGS_SET;
516
0
    int              cmp;                         /* Comparison value of records */
517
0
    unsigned         idx       = 0;               /* Location of record which matches key */
518
0
    H5B2_nodepos_t   next_pos  = H5B2_POS_MIDDLE; /* Position of node */
519
0
    herr_t           ret_value = SUCCEED;         /* Return value */
520
521
0
    FUNC_ENTER_PACKAGE
522
523
    /* Check arguments. */
524
0
    assert(hdr);
525
0
    assert(depth > 0);
526
0
    assert(curr_node_ptr);
527
0
    assert(H5_addr_defined(curr_node_ptr->addr));
528
529
    /* Lock current B-tree node */
530
0
    if (NULL ==
531
0
        (internal = H5B2__protect_internal(hdr, parent, curr_node_ptr, depth, false, H5AC__NO_FLAGS_SET)))
532
0
        HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node");
533
534
    /* Sanity check number of records */
535
0
    assert(internal->nrec == curr_node_ptr->node_nrec);
536
537
    /* Locate node pointer for child */
538
0
    if (H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata, &idx, &cmp) <
539
0
        0)
540
0
        HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records");
541
542
    /* Check for modifying existing record */
543
0
    if (0 == cmp) {
544
0
        bool changed = false; /* Whether the 'modify' callback changed the record */
545
546
        /* Make callback for current record */
547
0
        if ((op)(H5B2_INT_NREC(internal, hdr, idx), op_data, &changed) < 0) {
548
            /* Make certain that the callback didn't modify the value if it failed */
549
0
            assert(changed == false);
550
551
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTMODIFY, FAIL,
552
0
                        "'modify' callback failed for B-tree update operation");
553
0
        } /* end if */
554
555
        /* Mark the node as dirty if it changed */
556
0
        internal_flags |= (changed ? H5AC__DIRTIED_FLAG : 0);
557
558
        /* Indicate that the record was modified */
559
0
        *status = H5B2_UPDATE_MODIFY_DONE;
560
0
    } /* end if */
561
0
    else {
562
        /* Adjust index to leave room for node to insert */
563
0
        if (cmp > 0)
564
0
            idx++;
565
566
        /* Check if this node is left/right-most */
567
0
        if (H5B2_POS_MIDDLE != curr_pos) {
568
0
            if (idx == 0) {
569
0
                if (H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos)
570
0
                    next_pos = H5B2_POS_LEFT;
571
0
            } /* end if */
572
0
            else if (idx == internal->nrec) {
573
0
                if (H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos)
574
0
                    next_pos = H5B2_POS_RIGHT;
575
0
            } /* end else */
576
0
        }     /* end if */
577
578
        /* Attempt to update record in child */
579
0
        if (depth > 1) {
580
0
            if (H5B2__update_internal(hdr, (uint16_t)(depth - 1), &internal_flags, &internal->node_ptrs[idx],
581
0
                                      status, next_pos, internal, udata, op, op_data) < 0)
582
0
                HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL,
583
0
                            "unable to update record in internal B-tree node");
584
0
        } /* end if */
585
0
        else {
586
0
            if (H5B2__update_leaf(hdr, &internal->node_ptrs[idx], status, next_pos, internal, udata, op,
587
0
                                  op_data) < 0)
588
0
                HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update record in leaf B-tree node");
589
0
        } /* end else */
590
591
        /* Take actions based on child's status report */
592
0
        switch (*status) {
593
0
            case H5B2_UPDATE_MODIFY_DONE:
594
                /* No action */
595
0
                break;
596
597
0
            case H5B2_UPDATE_SHADOW_DONE:
598
                /* If child node was shadowed (if SWMR is enabled), mark this node dirty */
599
0
                if (hdr->swmr_write)
600
0
                    internal_flags |= H5AC__DIRTIED_FLAG;
601
602
                /* No further modifications up the tree are necessary though, so downgrade to merely
603
                 * "modified" */
604
0
                *status = H5B2_UPDATE_MODIFY_DONE;
605
0
                break;
606
607
0
            case H5B2_UPDATE_INSERT_DONE:
608
                /* Mark node as dirty */
609
0
                internal_flags |= H5AC__DIRTIED_FLAG;
610
611
                /* Update total record count for node pointer to current node */
612
0
                curr_node_ptr->all_nrec++;
613
0
                break;
614
615
0
            case H5B2_UPDATE_INSERT_CHILD_FULL:
616
                /* Split/redistribute this node */
617
0
                if (internal->nrec == hdr->node_info[depth].split_nrec) {
618
0
                    bool could_split = false; /* Whether the child node could split */
619
620
0
                    if (idx == 0) { /* Left-most child */
621
                        /* Check for left-most child and its neighbor being close to full */
622
0
                        if ((internal->node_ptrs[idx].node_nrec + internal->node_ptrs[idx + 1].node_nrec) >=
623
0
                            (unsigned)((hdr->node_info[depth - 1].split_nrec * 2) - 1))
624
0
                            could_split = true;
625
0
                    }
626
0
                    else if (idx == internal->nrec) { /* Right-most child */
627
                        /* Check for right-most child and its neighbor being close to full */
628
0
                        if ((internal->node_ptrs[idx - 1].node_nrec + internal->node_ptrs[idx].node_nrec) >=
629
0
                            (unsigned)((hdr->node_info[depth - 1].split_nrec * 2) - 1))
630
0
                            could_split = true;
631
0
                    }
632
0
                    else { /* Middle child */
633
                        /* Check for middle child and its left neighbor being close to full */
634
0
                        if ((internal->node_ptrs[idx - 1].node_nrec + internal->node_ptrs[idx].node_nrec) >=
635
0
                            (unsigned)((hdr->node_info[depth - 1].split_nrec * 2) - 1))
636
0
                            could_split = true;
637
                        /* Check for middle child and its right neighbor being close to full */
638
0
                        else if ((internal->node_ptrs[idx].node_nrec +
639
0
                                  internal->node_ptrs[idx + 1].node_nrec) >=
640
0
                                 (unsigned)((hdr->node_info[depth - 1].split_nrec * 2) - 1))
641
0
                            could_split = true;
642
0
                    }
643
644
                    /* If this node is full and the child node insertion could
645
                     *  cause a split, punt back up to caller, leaving the
646
                     *  "insert child full" status.
647
                     */
648
0
                    if (could_split) {
649
                        /* Release the internal B-tree node */
650
0
                        if (H5AC_unprotect(hdr->f, H5AC_BT2_INT, curr_node_ptr->addr, internal,
651
0
                                           internal_flags) < 0)
652
0
                            HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL,
653
0
                                        "unable to release internal B-tree node");
654
0
                        internal = NULL;
655
656
                        /* Punt back to caller */
657
0
                        HGOTO_DONE(SUCCEED);
658
0
                    }
659
0
                }
660
661
                /* Release the internal B-tree node */
662
0
                if (H5AC_unprotect(hdr->f, H5AC_BT2_INT, curr_node_ptr->addr, internal, internal_flags) < 0)
663
0
                    HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node");
664
0
                internal = NULL;
665
666
                /* Indicate that the record was inserted */
667
0
                *status = H5B2_UPDATE_INSERT_DONE;
668
669
                /* Dodge sideways into inserting a record into this node */
670
0
                if (H5B2__insert_internal(hdr, depth, parent_cache_info_flags_ptr, curr_node_ptr, curr_pos,
671
0
                                          parent, udata) < 0)
672
0
                    HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL,
673
0
                                "unable to insert record into internal B-tree node");
674
0
                break;
675
676
0
            case H5B2_UPDATE_UNKNOWN:
677
0
            default:
678
0
                assert(0 && "Invalid update status");
679
0
                HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "invalid update status");
680
0
        } /* end switch */
681
0
    }     /* end else */
682
683
0
done:
684
    /* Release the internal B-tree node */
685
0
    if (internal) {
686
        /* Check if we should shadow this node */
687
0
        if (hdr->swmr_write && (internal_flags & H5AC__DIRTIED_FLAG)) {
688
            /* Attempt to shadow the node if doing SWMR writes */
689
0
            if (H5B2__shadow_internal(internal, curr_node_ptr) < 0)
690
0
                HDONE_ERROR(H5E_BTREE, H5E_CANTCOPY, FAIL, "unable to shadow internal B-tree node");
691
692
            /* Change the state to "shadowed" if only modified currently */
693
            /* (Triggers parent to be marked dirty) */
694
0
            if (*status == H5B2_UPDATE_MODIFY_DONE)
695
0
                *status = H5B2_UPDATE_SHADOW_DONE;
696
0
        } /* end if */
697
698
        /* Unprotect node */
699
0
        if (H5AC_unprotect(hdr->f, H5AC_BT2_INT, curr_node_ptr->addr, internal, internal_flags) < 0)
700
0
            HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node");
701
0
    } /* end if */
702
703
0
    FUNC_LEAVE_NOAPI(ret_value)
704
0
} /* H5B2__update_internal() */
705
706
/*-------------------------------------------------------------------------
707
 * Function:    H5B2__shadow_internal
708
 *
709
 * Purpose:     "Shadow" an internal node - copy it to a new location,
710
 *              leaving the data in the old location intact (for now).
711
 *              This is done when writing in SWMR mode to ensure that
712
 *              readers do not see nodes that are out of date with
713
 *              respect to each other and thereby inconsistent.
714
 *
715
 * Return:      Non-negative on success/Negative on failure
716
 *
717
 *-------------------------------------------------------------------------
718
 */
719
static herr_t
720
H5B2__shadow_internal(H5B2_internal_t *internal, H5B2_node_ptr_t *curr_node_ptr)
721
0
{
722
0
    H5B2_hdr_t *hdr;                 /* B-tree header */
723
0
    herr_t      ret_value = SUCCEED; /* Return value */
724
725
0
    FUNC_ENTER_PACKAGE
726
727
    /*
728
     * Check arguments.
729
     */
730
0
    assert(internal);
731
0
    assert(curr_node_ptr);
732
0
    assert(H5_addr_defined(curr_node_ptr->addr));
733
0
    hdr = internal->hdr;
734
0
    assert(hdr);
735
0
    assert(hdr->swmr_write);
736
737
    /* We only need to shadow the node if it has not been shadowed since the
738
     * last time the header was flushed, as otherwise it will be unreachable by
739
     * the readers so there will be no need to shadow.  To check if it has been
740
     * shadowed, compare the epoch of this node and the header.  If this node's
741
     * epoch is <= to the header's, it hasn't been shadowed yet. */
742
0
    if (internal->shadow_epoch <= hdr->shadow_epoch) {
743
0
        haddr_t new_node_addr; /* Address to move node to */
744
745
        /*
746
         * We must clone the old node so readers with an out-of-date version of
747
         * the parent can still see the correct number of children, via the
748
         * shadowed node.  Remove it from cache but do not mark it free on disk.
749
         */
750
        /* Allocate space for the cloned node */
751
0
        if (HADDR_UNDEF == (new_node_addr = H5MF_alloc(hdr->f, H5FD_MEM_BTREE, (hsize_t)hdr->node_size)))
752
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTALLOC, FAIL, "unable to allocate file space to move B-tree node");
753
754
        /* Move the location of the node on the disk */
755
0
        if (H5AC_move_entry(hdr->f, H5AC_BT2_INT, curr_node_ptr->addr, new_node_addr) < 0)
756
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTMOVE, FAIL, "unable to move B-tree node");
757
0
        curr_node_ptr->addr = new_node_addr;
758
759
        /* Should free the space in the file, but this is not supported by
760
         * SWMR_WRITE code yet - QAK, 2016/12/01
761
         */
762
763
        /* Set shadow epoch for node ahead of header */
764
0
        internal->shadow_epoch = hdr->shadow_epoch + 1;
765
0
    } /* end if */
766
767
0
done:
768
0
    FUNC_LEAVE_NOAPI(ret_value)
769
0
} /* end H5B2__shadow_internal() */
770
771
/*-------------------------------------------------------------------------
772
 * Function:  H5B2__remove_internal
773
 *
774
 * Purpose: Removes a record from a B-tree node.
775
 *
776
 * Return:  Non-negative on success/Negative on failure
777
 *
778
 *-------------------------------------------------------------------------
779
 */
780
herr_t
781
H5B2__remove_internal(H5B2_hdr_t *hdr, bool *depth_decreased, void *swap_loc, void *swap_parent,
782
                      uint16_t depth, H5AC_info_t *parent_cache_info, unsigned *parent_cache_info_flags_ptr,
783
                      H5B2_nodepos_t curr_pos, H5B2_node_ptr_t *curr_node_ptr, void *udata, H5B2_remove_t op,
784
                      void *op_data)
785
0
{
786
0
    H5AC_info_t     *new_cache_info; /* Pointer to new cache info */
787
0
    unsigned        *new_cache_info_flags_ptr = NULL;
788
0
    H5B2_node_ptr_t *new_node_ptr;                     /* Pointer to new node pointer */
789
0
    H5B2_internal_t *internal;                         /* Pointer to internal node */
790
0
    H5B2_nodepos_t   next_pos       = H5B2_POS_MIDDLE; /* Position of next node */
791
0
    unsigned         internal_flags = H5AC__NO_FLAGS_SET;
792
0
    haddr_t          internal_addr  = HADDR_UNDEF; /* Address of internal node */
793
0
    size_t           merge_nrec;                   /* Number of records to merge node at */
794
0
    bool             collapsed_root = false;       /* Whether the root was collapsed */
795
0
    herr_t           ret_value      = SUCCEED;     /* Return value */
796
797
0
    FUNC_ENTER_PACKAGE
798
799
    /* Check arguments. */
800
0
    assert(hdr);
801
0
    assert(depth > 0);
802
0
    assert(parent_cache_info);
803
0
    assert(curr_node_ptr);
804
0
    assert(H5_addr_defined(curr_node_ptr->addr));
805
806
    /* Lock current B-tree node */
807
0
    if (NULL == (internal = H5B2__protect_internal(hdr, parent_cache_info, curr_node_ptr, depth, false,
808
0
                                                   H5AC__NO_FLAGS_SET)))
809
0
        HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node");
810
0
    internal_addr = curr_node_ptr->addr;
811
812
    /* Determine the correct number of records to merge at */
813
0
    merge_nrec = hdr->node_info[depth - 1].merge_nrec;
814
815
    /* Check for needing to collapse the root node */
816
    /* (The root node is the only internal node allowed to have 1 record) */
817
0
    if (internal->nrec == 1 &&
818
0
        ((internal->node_ptrs[0].node_nrec + internal->node_ptrs[1].node_nrec) <= ((merge_nrec * 2) + 1))) {
819
820
        /* Merge children of root node */
821
0
        if (H5B2__merge2(hdr, depth, curr_node_ptr, parent_cache_info_flags_ptr, internal, &internal_flags,
822
0
                         0) < 0)
823
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node");
824
825
        /* Let the cache know that the object is deleted */
826
0
        internal_flags |= H5AC__DELETED_FLAG;
827
0
        if (!hdr->swmr_write)
828
0
            internal_flags |= H5AC__FREE_FILE_SPACE_FLAG;
829
830
        /* Reset information in header's root node pointer */
831
0
        curr_node_ptr->addr      = internal->node_ptrs[0].addr;
832
0
        curr_node_ptr->node_nrec = internal->node_ptrs[0].node_nrec;
833
834
        /* Update flush dependency for child, if using SWMR */
835
0
        if (hdr->swmr_write)
836
0
            if (H5B2__update_flush_depend(hdr, depth, curr_node_ptr, internal, hdr) < 0)
837
0
                HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child node to new parent");
838
839
        /* Indicate that the level of the B-tree decreased */
840
0
        *depth_decreased = true;
841
842
        /* Set pointers for advancing to child node */
843
0
        new_cache_info           = parent_cache_info;
844
0
        new_cache_info_flags_ptr = parent_cache_info_flags_ptr;
845
0
        new_node_ptr             = curr_node_ptr;
846
847
        /* Set flag to indicate root was collapsed */
848
0
        collapsed_root = true;
849
850
        /* Indicate position of next node */
851
0
        next_pos = H5B2_POS_ROOT;
852
0
    } /* end if */
853
    /* Merge or redistribute child node pointers, if necessary */
854
0
    else {
855
0
        unsigned idx = 0; /* Location of record which matches key */
856
0
        int      cmp = 0; /* Comparison value of records */
857
0
        unsigned retries; /* Number of times to attempt redistribution */
858
859
        /* Shadow the node if doing SWMR writes */
860
0
        if (hdr->swmr_write) {
861
0
            if (H5B2__shadow_internal(internal, curr_node_ptr) < 0)
862
0
                HGOTO_ERROR(H5E_BTREE, H5E_CANTCOPY, FAIL, "unable to shadow internal node");
863
0
            internal_addr = curr_node_ptr->addr;
864
0
        } /* end if */
865
866
        /* Locate node pointer for child */
867
0
        if (swap_loc)
868
0
            idx = 0;
869
0
        else {
870
0
            if (H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata, &idx,
871
0
                                    &cmp) < 0)
872
0
                HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records");
873
0
            if (cmp >= 0)
874
0
                idx++;
875
0
        } /* end else */
876
877
        /* Set the number of redistribution retries */
878
        /* This takes care of the case where a B-tree node needs to be
879
         * redistributed, but redistributing the node causes the index
880
         * for removal to move to another node, which also needs to be
881
         * redistributed.  Now, we loop trying to redistribute and then
882
         * eventually force a merge */
883
0
        retries = 2;
884
885
        /* Preemptively merge/redistribute a node we will enter */
886
0
        while (internal->node_ptrs[idx].node_nrec == merge_nrec) {
887
            /* Attempt to redistribute records among children */
888
            /* (NOTE: These 2-node redistributions should actually get the
889
             *  record to promote from the node with more records. - QAK)
890
             */
891
            /* (NOTE: This code is the same in both H5B2__remove_internal() and
892
             *  H5B2__remove_internal_by_idx(), fix bugs in both places! - QAK)
893
             */
894
0
            if (idx == 0) { /* Left-most child */
895
0
                if (retries > 0 && (internal->node_ptrs[idx + 1].node_nrec > merge_nrec)) {
896
0
                    if (H5B2__redistribute2(hdr, depth, internal, idx) < 0)
897
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL,
898
0
                                    "unable to redistribute child node records");
899
0
                } /* end if */
900
0
                else {
901
0
                    if (H5B2__merge2(hdr, depth, curr_node_ptr, parent_cache_info_flags_ptr, internal,
902
0
                                     &internal_flags, idx) < 0)
903
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node");
904
0
                }                             /* end else */
905
0
            }                                 /* end if */
906
0
            else if (idx == internal->nrec) { /* Right-most child */
907
0
                if (retries > 0 && (internal->node_ptrs[idx - 1].node_nrec > merge_nrec)) {
908
0
                    if (H5B2__redistribute2(hdr, depth, internal, (idx - 1)) < 0)
909
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL,
910
0
                                    "unable to redistribute child node records");
911
0
                } /* end if */
912
0
                else {
913
0
                    if (H5B2__merge2(hdr, depth, curr_node_ptr, parent_cache_info_flags_ptr, internal,
914
0
                                     &internal_flags, (idx - 1)) < 0)
915
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node");
916
0
                }  /* end else */
917
0
            }      /* end if */
918
0
            else { /* Middle child */
919
0
                if (retries > 0 && ((internal->node_ptrs[idx + 1].node_nrec > merge_nrec) ||
920
0
                                    (internal->node_ptrs[idx - 1].node_nrec > merge_nrec))) {
921
0
                    if (H5B2__redistribute3(hdr, depth, internal, &internal_flags, idx) < 0)
922
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL,
923
0
                                    "unable to redistribute child node records");
924
0
                } /* end if */
925
0
                else {
926
0
                    if (H5B2__merge3(hdr, depth, curr_node_ptr, parent_cache_info_flags_ptr, internal,
927
0
                                     &internal_flags, idx) < 0)
928
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node");
929
0
                } /* end else */
930
0
            }     /* end else */
931
932
            /* Locate node pointer for child (after merge/redistribute) */
933
0
            if (swap_loc)
934
0
                idx = 0;
935
0
            else {
936
                /* Actually, this can be easily updated (for 2-node redistrib.) and shouldn't require
937
                 * re-searching */
938
0
                if (H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata,
939
0
                                        &idx, &cmp) < 0)
940
0
                    HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records");
941
0
                if (cmp >= 0)
942
0
                    idx++;
943
0
            } /* end else */
944
945
            /* Decrement the number of redistribution retries left */
946
0
            retries--;
947
0
        } /* end while */
948
949
        /* Handle deleting a record from an internal node */
950
0
        if (!swap_loc && cmp == 0) {
951
0
            swap_loc    = H5B2_INT_NREC(internal, hdr, idx - 1);
952
0
            swap_parent = internal;
953
0
        } /* end if */
954
955
        /* Swap record to delete with record from leaf, if we are the last internal node */
956
0
        if (swap_loc && depth == 1)
957
0
            if (H5B2__swap_leaf(hdr, depth, internal, &internal_flags, idx, swap_loc) < 0)
958
0
                HGOTO_ERROR(H5E_BTREE, H5E_CANTSWAP, FAIL, "Can't swap records in B-tree");
959
960
        /* Set pointers for advancing to child node */
961
0
        new_cache_info_flags_ptr = &internal_flags;
962
0
        new_cache_info           = &internal->cache_info;
963
0
        new_node_ptr             = &internal->node_ptrs[idx];
964
965
        /* Indicate position of next node */
966
0
        if (H5B2_POS_MIDDLE != curr_pos) {
967
0
            if (idx == 0) {
968
0
                if (H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos)
969
0
                    next_pos = H5B2_POS_LEFT;
970
0
            } /* end if */
971
0
            else if (idx == internal->nrec) {
972
0
                if (H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos)
973
0
                    next_pos = H5B2_POS_RIGHT;
974
0
            } /* end if */
975
0
        }     /* end if */
976
0
    }         /* end else */
977
978
    /* Attempt to remove record from child node */
979
0
    if (depth > 1) {
980
0
        if (H5B2__remove_internal(hdr, depth_decreased, swap_loc, swap_parent, (uint16_t)(depth - 1),
981
0
                                  new_cache_info, new_cache_info_flags_ptr, next_pos, new_node_ptr, udata, op,
982
0
                                  op_data) < 0)
983
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree internal node");
984
0
    } /* end if */
985
0
    else {
986
0
        if (H5B2__remove_leaf(hdr, new_node_ptr, next_pos, new_cache_info, udata, op, op_data) < 0)
987
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree leaf node");
988
0
    } /* end else */
989
990
    /* Update record count for node pointer to current node */
991
0
    if (!collapsed_root)
992
0
        new_node_ptr->all_nrec--;
993
994
    /* Mark node as dirty */
995
0
    if (!(hdr->swmr_write && collapsed_root))
996
0
        internal_flags |= H5AC__DIRTIED_FLAG;
997
998
#ifdef H5B2_DEBUG
999
    H5B2__assert_internal((!collapsed_root ? (curr_node_ptr->all_nrec - 1) : new_node_ptr->all_nrec), hdr,
1000
                          internal);
1001
#endif /* H5B2_DEBUG */
1002
1003
0
done:
1004
    /* Release the B-tree internal node */
1005
0
    if (internal && H5AC_unprotect(hdr->f, H5AC_BT2_INT, internal_addr, internal, internal_flags) < 0)
1006
0
        HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node");
1007
1008
0
    FUNC_LEAVE_NOAPI(ret_value)
1009
0
} /* H5B2__remove_internal() */
1010
1011
/*-------------------------------------------------------------------------
1012
 * Function:  H5B2__remove_internal_by_idx
1013
 *
1014
 * Purpose: Removes a record from a B-tree node, according to the offset
1015
 *              in the B-tree records
1016
 *
1017
 * Return:  Non-negative on success/Negative on failure
1018
 *
1019
 *-------------------------------------------------------------------------
1020
 */
1021
herr_t
1022
H5B2__remove_internal_by_idx(H5B2_hdr_t *hdr, bool *depth_decreased, void *swap_loc, void *swap_parent,
1023
                             uint16_t depth, H5AC_info_t *parent_cache_info,
1024
                             unsigned *parent_cache_info_flags_ptr, H5B2_node_ptr_t *curr_node_ptr,
1025
                             H5B2_nodepos_t curr_pos, hsize_t n, H5B2_remove_t op, void *op_data)
1026
0
{
1027
0
    H5AC_info_t     *new_cache_info; /* Pointer to new cache info */
1028
0
    unsigned        *new_cache_info_flags_ptr = NULL;
1029
0
    H5B2_node_ptr_t *new_node_ptr;                     /* Pointer to new node pointer */
1030
0
    H5B2_internal_t *internal;                         /* Pointer to internal node */
1031
0
    H5B2_nodepos_t   next_pos       = H5B2_POS_MIDDLE; /* Position of next node */
1032
0
    unsigned         internal_flags = H5AC__NO_FLAGS_SET;
1033
0
    haddr_t          internal_addr  = HADDR_UNDEF; /* Address of internal node */
1034
0
    size_t           merge_nrec;                   /* Number of records to merge node at */
1035
0
    bool             collapsed_root = false;       /* Whether the root was collapsed */
1036
0
    herr_t           ret_value      = SUCCEED;     /* Return value */
1037
1038
0
    FUNC_ENTER_PACKAGE
1039
1040
    /* Check arguments. */
1041
0
    assert(hdr);
1042
0
    assert(depth > 0);
1043
0
    assert(parent_cache_info);
1044
0
    assert(curr_node_ptr);
1045
0
    assert(H5_addr_defined(curr_node_ptr->addr));
1046
1047
    /* Lock current B-tree node */
1048
0
    if (NULL == (internal = H5B2__protect_internal(hdr, parent_cache_info, curr_node_ptr, depth, false,
1049
0
                                                   H5AC__NO_FLAGS_SET)))
1050
0
        HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node");
1051
0
    internal_addr = curr_node_ptr->addr;
1052
0
    assert(internal->nrec == curr_node_ptr->node_nrec);
1053
0
    assert(depth == hdr->depth || internal->nrec > 1);
1054
1055
    /* Determine the correct number of records to merge at */
1056
0
    merge_nrec = hdr->node_info[depth - 1].merge_nrec;
1057
1058
    /* Check for needing to collapse the root node */
1059
    /* (The root node is the only internal node allowed to have 1 record) */
1060
0
    if (internal->nrec == 1 &&
1061
0
        ((internal->node_ptrs[0].node_nrec + internal->node_ptrs[1].node_nrec) <= ((merge_nrec * 2) + 1))) {
1062
0
        assert(depth == hdr->depth);
1063
1064
        /* Merge children of root node */
1065
0
        if (H5B2__merge2(hdr, depth, curr_node_ptr, parent_cache_info_flags_ptr, internal, &internal_flags,
1066
0
                         0) < 0)
1067
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node");
1068
1069
        /* Let the cache know that the object is deleted */
1070
0
        internal_flags |= H5AC__DELETED_FLAG;
1071
0
        if (!hdr->swmr_write)
1072
0
            internal_flags |= H5AC__FREE_FILE_SPACE_FLAG;
1073
1074
        /* Reset information in header's root node pointer */
1075
0
        curr_node_ptr->addr      = internal->node_ptrs[0].addr;
1076
0
        curr_node_ptr->node_nrec = internal->node_ptrs[0].node_nrec;
1077
1078
        /* Update flush dependency for child, if using SWMR */
1079
0
        if (hdr->swmr_write)
1080
0
            if (H5B2__update_flush_depend(hdr, depth, curr_node_ptr, internal, hdr) < 0)
1081
0
                HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child node to new parent");
1082
1083
        /* Indicate that the level of the B-tree decreased */
1084
0
        *depth_decreased = true;
1085
1086
        /* Set pointers for advancing to child node */
1087
0
        new_cache_info           = parent_cache_info;
1088
0
        new_cache_info_flags_ptr = parent_cache_info_flags_ptr;
1089
0
        new_node_ptr             = curr_node_ptr;
1090
1091
        /* Set flag to indicate root was collapsed */
1092
0
        collapsed_root = true;
1093
1094
        /* Indicate position of next node */
1095
0
        next_pos = H5B2_POS_ROOT;
1096
0
    } /* end if */
1097
    /* Merge or redistribute child node pointers, if necessary */
1098
0
    else {
1099
0
        hsize_t  orig_n = n;    /* Original index looked for */
1100
0
        unsigned idx;           /* Location of record which matches key */
1101
0
        bool     found = false; /* Comparison value of records */
1102
0
        unsigned retries;       /* Number of times to attempt redistribution */
1103
1104
        /* Shadow the node if doing SWMR writes */
1105
0
        if (hdr->swmr_write) {
1106
0
            if (H5B2__shadow_internal(internal, curr_node_ptr) < 0)
1107
0
                HGOTO_ERROR(H5E_BTREE, H5E_CANTCOPY, FAIL, "unable to shadow internal node");
1108
0
            internal_addr = curr_node_ptr->addr;
1109
0
        } /* end if */
1110
1111
        /* Locate node pointer for child */
1112
0
        if (swap_loc)
1113
0
            idx = 0;
1114
0
        else {
1115
            /* Search for record with correct index */
1116
0
            for (idx = 0; idx < internal->nrec; idx++) {
1117
                /* Check which child node contains indexed record */
1118
0
                if (internal->node_ptrs[idx].all_nrec >= n) {
1119
                    /* Check if record is in this node */
1120
0
                    if (internal->node_ptrs[idx].all_nrec == n) {
1121
                        /* Indicate the record was found and that the index
1122
                         *      in child nodes is zero from now on
1123
                         */
1124
0
                        found = true;
1125
0
                        n     = 0;
1126
1127
                        /* Increment to next record */
1128
0
                        idx++;
1129
0
                    } /* end if */
1130
1131
                    /* Break out of loop early */
1132
0
                    break;
1133
0
                } /* end if */
1134
1135
                /* Decrement index we are looking for to account for the node we
1136
                 * just advanced past.
1137
                 */
1138
0
                n -= (internal->node_ptrs[idx].all_nrec + 1);
1139
0
            } /* end for */
1140
0
        }     /* end else */
1141
1142
        /* Set the number of redistribution retries */
1143
        /* This takes care of the case where a B-tree node needs to be
1144
         * redistributed, but redistributing the node causes the index
1145
         * for removal to move to another node, which also needs to be
1146
         * redistributed.  Now, we loop trying to redistribute and then
1147
         * eventually force a merge */
1148
0
        retries = 2;
1149
1150
        /* Preemptively merge/redistribute a node we will enter */
1151
0
        while (internal->node_ptrs[idx].node_nrec == merge_nrec) {
1152
            /* Attempt to redistribute records among children */
1153
            /* (NOTE: These 2-node redistributions should actually get the
1154
             *  record to promote from the node with more records. - QAK)
1155
             */
1156
            /* (NOTE: This code is the same in both H5B2__remove_internal() and
1157
             *  H5B2__remove_internal_by_idx(), fix bugs in both places! - QAK)
1158
             */
1159
0
            if (idx == 0) { /* Left-most child */
1160
0
                if (retries > 0 && (internal->node_ptrs[idx + 1].node_nrec > merge_nrec)) {
1161
0
                    if (H5B2__redistribute2(hdr, depth, internal, idx) < 0)
1162
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL,
1163
0
                                    "unable to redistribute child node records");
1164
0
                } /* end if */
1165
0
                else {
1166
0
                    if (H5B2__merge2(hdr, depth, curr_node_ptr, parent_cache_info_flags_ptr, internal,
1167
0
                                     &internal_flags, idx) < 0)
1168
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node");
1169
0
                }                             /* end else */
1170
0
            }                                 /* end if */
1171
0
            else if (idx == internal->nrec) { /* Right-most child */
1172
0
                if (retries > 0 && (internal->node_ptrs[idx - 1].node_nrec > merge_nrec)) {
1173
0
                    if (H5B2__redistribute2(hdr, depth, internal, (idx - 1)) < 0)
1174
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL,
1175
0
                                    "unable to redistribute child node records");
1176
0
                } /* end if */
1177
0
                else {
1178
0
                    if (H5B2__merge2(hdr, depth, curr_node_ptr, parent_cache_info_flags_ptr, internal,
1179
0
                                     &internal_flags, (idx - 1)) < 0)
1180
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node");
1181
0
                }  /* end else */
1182
0
            }      /* end if */
1183
0
            else { /* Middle child */
1184
0
                if (retries > 0 && ((internal->node_ptrs[idx + 1].node_nrec > merge_nrec) ||
1185
0
                                    (internal->node_ptrs[idx - 1].node_nrec > merge_nrec))) {
1186
0
                    if (H5B2__redistribute3(hdr, depth, internal, &internal_flags, idx) < 0)
1187
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL,
1188
0
                                    "unable to redistribute child node records");
1189
0
                } /* end if */
1190
0
                else {
1191
0
                    if (H5B2__merge3(hdr, depth, curr_node_ptr, parent_cache_info_flags_ptr, internal,
1192
0
                                     &internal_flags, idx) < 0)
1193
0
                        HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node");
1194
0
                } /* end else */
1195
0
            }     /* end else */
1196
1197
            /* Locate node pointer for child (after merge/redistribute) */
1198
0
            if (swap_loc)
1199
0
                idx = 0;
1200
0
            else {
1201
                /* Count from the original index value again */
1202
0
                n = orig_n;
1203
1204
                /* Reset "found" flag - the record may have shifted during the
1205
                 *      redistribute/merge
1206
                 */
1207
0
                found = false;
1208
1209
                /* Search for record with correct index */
1210
0
                for (idx = 0; idx < internal->nrec; idx++) {
1211
                    /* Check which child node contains indexed record */
1212
0
                    if (internal->node_ptrs[idx].all_nrec >= n) {
1213
                        /* Check if record is in this node */
1214
0
                        if (internal->node_ptrs[idx].all_nrec == n) {
1215
                            /* Indicate the record was found and that the index
1216
                             *      in child nodes is zero from now on
1217
                             */
1218
0
                            found = true;
1219
0
                            n     = 0;
1220
1221
                            /* Increment to next record */
1222
0
                            idx++;
1223
0
                        } /* end if */
1224
1225
                        /* Break out of loop early */
1226
0
                        break;
1227
0
                    } /* end if */
1228
1229
                    /* Decrement index we are looking for to account for the node we
1230
                     * just advanced past.
1231
                     */
1232
0
                    n -= (internal->node_ptrs[idx].all_nrec + 1);
1233
0
                } /* end for */
1234
0
            }     /* end else */
1235
1236
            /* Decrement the number of redistribution retries left */
1237
0
            retries--;
1238
0
        } /* end while */
1239
1240
        /* Handle deleting a record from an internal node */
1241
0
        if (!swap_loc && found) {
1242
0
            swap_loc    = H5B2_INT_NREC(internal, hdr, idx - 1);
1243
0
            swap_parent = internal;
1244
0
        } /* end if */
1245
1246
        /* Swap record to delete with record from leaf, if we are the last internal node */
1247
0
        if (swap_loc && depth == 1)
1248
0
            if (H5B2__swap_leaf(hdr, depth, internal, &internal_flags, idx, swap_loc) < 0)
1249
0
                HGOTO_ERROR(H5E_BTREE, H5E_CANTSWAP, FAIL, "can't swap records in B-tree");
1250
1251
        /* Set pointers for advancing to child node */
1252
0
        new_cache_info_flags_ptr = &internal_flags;
1253
0
        new_cache_info           = &internal->cache_info;
1254
0
        new_node_ptr             = &internal->node_ptrs[idx];
1255
1256
        /* Indicate position of next node */
1257
0
        if (H5B2_POS_MIDDLE != curr_pos) {
1258
0
            if (idx == 0) {
1259
0
                if (H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos)
1260
0
                    next_pos = H5B2_POS_LEFT;
1261
0
            } /* end if */
1262
0
            else if (idx == internal->nrec) {
1263
0
                if (H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos)
1264
0
                    next_pos = H5B2_POS_RIGHT;
1265
0
            } /* end if */
1266
0
        }     /* end if */
1267
0
    }         /* end else */
1268
1269
    /* Attempt to remove record from child node */
1270
0
    if (depth > 1) {
1271
0
        if (H5B2__remove_internal_by_idx(hdr, depth_decreased, swap_loc, swap_parent, (uint16_t)(depth - 1),
1272
0
                                         new_cache_info, new_cache_info_flags_ptr, new_node_ptr, next_pos, n,
1273
0
                                         op, op_data) < 0)
1274
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree internal node");
1275
0
    } /* end if */
1276
0
    else {
1277
0
        if (H5B2__remove_leaf_by_idx(hdr, new_node_ptr, next_pos, new_cache_info, (unsigned)n, op, op_data) <
1278
0
            0)
1279
0
            HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree leaf node");
1280
0
    } /* end else */
1281
1282
    /* Update record count for node pointer to child node */
1283
0
    if (!collapsed_root)
1284
0
        new_node_ptr->all_nrec--;
1285
1286
    /* Mark node as dirty */
1287
0
    if (!(hdr->swmr_write && collapsed_root))
1288
0
        internal_flags |= H5AC__DIRTIED_FLAG;
1289
1290
#ifdef H5B2_DEBUG
1291
    H5B2__assert_internal((!collapsed_root ? (curr_node_ptr->all_nrec - 1) : new_node_ptr->all_nrec), hdr,
1292
                          internal);
1293
#endif /* H5B2_DEBUG */
1294
1295
0
done:
1296
    /* Release the B-tree internal node */
1297
0
    if (internal && H5AC_unprotect(hdr->f, H5AC_BT2_INT, internal_addr, internal, internal_flags) < 0)
1298
0
        HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node");
1299
1300
0
    FUNC_LEAVE_NOAPI(ret_value)
1301
0
} /* H5B2__remove_internal_by_idx() */
1302
1303
/*-------------------------------------------------------------------------
1304
 * Function:  H5B2__internal_free
1305
 *
1306
 * Purpose: Destroys a B-tree internal node in memory.
1307
 *
1308
 * Return:  Non-negative on success/Negative on failure
1309
 *
1310
 *-------------------------------------------------------------------------
1311
 */
1312
herr_t
1313
H5B2__internal_free(H5B2_internal_t *internal)
1314
0
{
1315
0
    herr_t ret_value = SUCCEED; /* Return value */
1316
1317
0
    FUNC_ENTER_PACKAGE
1318
1319
    /*
1320
     * Check arguments.
1321
     */
1322
0
    assert(internal);
1323
1324
    /* Release internal node's native key buffer */
1325
0
    if (internal->int_native)
1326
0
        internal->int_native = (uint8_t *)H5FL_FAC_FREE(internal->hdr->node_info[internal->depth].nat_rec_fac,
1327
0
                                                        internal->int_native);
1328
1329
    /* Release internal node's node pointer buffer */
1330
0
    if (internal->node_ptrs)
1331
0
        internal->node_ptrs = (H5B2_node_ptr_t *)H5FL_FAC_FREE(
1332
0
            internal->hdr->node_info[internal->depth].node_ptr_fac, internal->node_ptrs);
1333
1334
    /* Decrement ref. count on B-tree header */
1335
0
    if (H5B2__hdr_decr(internal->hdr) < 0)
1336
0
        HGOTO_ERROR(H5E_BTREE, H5E_CANTDEC, FAIL, "can't decrement ref. count on B-tree header");
1337
1338
    /* Sanity check */
1339
0
    assert(NULL == internal->top_proxy);
1340
1341
    /* Free B-tree internal node info */
1342
0
    internal = H5FL_FREE(H5B2_internal_t, internal);
1343
1344
0
done:
1345
0
    FUNC_LEAVE_NOAPI(ret_value)
1346
0
} /* end H5B2__internal_free() */
1347
1348
#ifdef H5B2_DEBUG
1349
1350
/*-------------------------------------------------------------------------
1351
 * Function:  H5B2__assert_internal
1352
 *
1353
 * Purpose: Verify than an internal node is mostly sane
1354
 *
1355
 * Return:  Non-negative on success, negative on failure
1356
 *
1357
 *-------------------------------------------------------------------------
1358
 */
1359
H5_ATTR_PURE herr_t
1360
H5B2__assert_internal(hsize_t parent_all_nrec, const H5B2_hdr_t H5_ATTR_NDEBUG_UNUSED *hdr,
1361
                      const H5B2_internal_t *internal)
1362
{
1363
    hsize_t  tot_all_nrec; /* Total number of records at or below this node */
1364
    uint16_t u, v;         /* Local index variables */
1365
1366
    /* General sanity checking on node */
1367
    assert(internal->nrec <= hdr->node_info->split_nrec);
1368
1369
    /* Sanity checking on node pointers */
1370
    tot_all_nrec = internal->nrec;
1371
    for (u = 0; u < internal->nrec + 1; u++) {
1372
        tot_all_nrec += internal->node_ptrs[u].all_nrec;
1373
1374
        assert(H5_addr_defined(internal->node_ptrs[u].addr));
1375
        assert(internal->node_ptrs[u].addr > 0);
1376
        for (v = 0; v < u; v++)
1377
            assert(internal->node_ptrs[u].addr != internal->node_ptrs[v].addr);
1378
    } /* end for */
1379
1380
    /* Sanity check all_nrec total in parent */
1381
    if (parent_all_nrec > 0)
1382
        assert(tot_all_nrec == parent_all_nrec);
1383
1384
    return (0);
1385
} /* end H5B2__assert_internal() */
1386
1387
/*-------------------------------------------------------------------------
1388
 * Function:  H5B2__assert_internal2
1389
 *
1390
 * Purpose: Verify than internal nodes are mostly sane
1391
 *
1392
 * Return:  Non-negative on success, negative on failure
1393
 *
1394
 *-------------------------------------------------------------------------
1395
 */
1396
H5_ATTR_PURE herr_t
1397
H5B2__assert_internal2(hsize_t parent_all_nrec, const H5B2_hdr_t H5_ATTR_NDEBUG_UNUSED *hdr,
1398
                       const H5B2_internal_t *internal, const H5B2_internal_t *internal2)
1399
{
1400
    hsize_t  tot_all_nrec; /* Total number of records at or below this node */
1401
    uint16_t u, v;         /* Local index variables */
1402
1403
    /* General sanity checking on node */
1404
    assert(internal->nrec <= hdr->node_info->split_nrec);
1405
1406
    /* Sanity checking on node pointers */
1407
    tot_all_nrec = internal->nrec;
1408
    for (u = 0; u < internal->nrec + 1; u++) {
1409
        tot_all_nrec += internal->node_ptrs[u].all_nrec;
1410
1411
        assert(H5_addr_defined(internal->node_ptrs[u].addr));
1412
        assert(internal->node_ptrs[u].addr > 0);
1413
        for (v = 0; v < u; v++)
1414
            assert(internal->node_ptrs[u].addr != internal->node_ptrs[v].addr);
1415
        for (v = 0; v < internal2->nrec + 1; v++)
1416
            assert(internal->node_ptrs[u].addr != internal2->node_ptrs[v].addr);
1417
    } /* end for */
1418
1419
    /* Sanity check all_nrec total in parent */
1420
    if (parent_all_nrec > 0)
1421
        assert(tot_all_nrec == parent_all_nrec);
1422
1423
    return (0);
1424
} /* end H5B2__assert_internal2() */
1425
#endif /* H5B2_DEBUG */