Coverage Report

Created: 2026-01-08 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hdf5/src/H5RT.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
/* Module Setup */
15
/****************/
16
#include "H5RTmodule.h" /* This source code file is part of the H5RT module */
17
18
#include "H5RTpkg.h"
19
20
/***********/
21
/* Headers */
22
/***********/
23
#include "H5private.h"   /* Generic functions */
24
#include "H5Eprivate.h"  /* Error handling */
25
#include "H5FLprivate.h" /* Free lists */
26
27
H5FL_DEFINE_STATIC(H5RT_t);
28
H5FL_DEFINE_STATIC(H5RT_node_t);
29
30
static herr_t H5RT__bulk_load(H5RT_node_t *node, int rank, H5RT_leaf_t *leaves, size_t count,
31
                              int prev_sort_dim);
32
static herr_t H5RT__search_recurse(H5RT_node_t *node, int rank, hsize_t min[], hsize_t max[],
33
                                   H5RT_result_set_t *result_set);
34
static herr_t H5RT__node_copy(H5RT_node_t *dest_node, const H5RT_node_t *src_node,
35
                              const H5RT_leaf_t *old_leaves_base, H5RT_leaf_t *new_leaves_base);
36
static void   H5RT__free_recurse(H5RT_node_t *node);
37
38
/* Result buffer helper functions */
39
static herr_t H5RT__result_set_init(H5RT_result_set_t *result_set);
40
static herr_t H5RT__result_set_add(H5RT_result_set_t *result_set, H5RT_leaf_t *leaf);
41
static herr_t H5RT__result_set_grow(H5RT_result_set_t *result_set);
42
static int    H5RT__leaf_compare(const void *leaf1, const void *leaf2, void *dim);
43
44
/*-------------------------------------------------------------------------
45
 * Function:    H5RT_leaf_init
46
 *
47
 * Purpose:     Initialize an R-tree leaf with dynamic coordinate allocation
48
 *              based on the specified rank. The leaf structure itself must
49
 *              already be allocated by the caller.
50
 *
51
 * Return:      Non-negative on success/Negative on failure
52
 *
53
 *-------------------------------------------------------------------------
54
 */
55
herr_t
56
H5RT_leaf_init(H5RT_leaf_t *leaf, int rank, void *record)
57
0
{
58
0
    herr_t ret_value = SUCCEED;
59
60
0
    FUNC_ENTER_NOAPI(FAIL)
61
62
0
    if (!leaf)
63
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid leaf pointer");
64
65
0
    if (rank < 1 || rank > H5S_MAX_RANK)
66
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid rank");
67
68
    /* Zero out the leaf structure */
69
0
    memset(leaf, 0, sizeof(H5RT_leaf_t));
70
71
    /* Allocate coordinate arrays as single block: 3 * rank * sizeof(hsize_t) */
72
0
    if (NULL == (leaf->_coords = (hsize_t *)malloc(3 * (size_t)rank * sizeof(hsize_t))))
73
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "failed to allocate leaf coordinates");
74
75
    /* Set up pointers to sections of the coordinate block */
76
0
    leaf->min = leaf->_coords;
77
0
    leaf->max = leaf->_coords + rank;
78
0
    leaf->mid = leaf->_coords + (2 * rank);
79
80
0
    leaf->rank   = rank;
81
0
    leaf->record = record;
82
83
0
done:
84
0
    if (ret_value < 0 && leaf) {
85
0
        if (H5RT_leaf_cleanup(leaf) < 0)
86
0
            HDONE_ERROR(H5E_RTREE, H5E_CANTRELEASE, FAIL, "failed to clean up leaf on error");
87
0
    }
88
89
0
    FUNC_LEAVE_NOAPI(ret_value)
90
0
} /* end H5RT_leaf_init() */
91
92
/*-------------------------------------------------------------------------
93
 * Function:    H5RT_leaf_cleanup
94
 *
95
 * Purpose:     Clean up a leaf's coordinate arrays. The leaf structure
96
 *              itself is not freed - that's the caller's responsibility.
97
 *
98
 * Return:      Non-negative on success/Negative on failure
99
 *
100
 *-------------------------------------------------------------------------
101
 */
102
herr_t
103
H5RT_leaf_cleanup(H5RT_leaf_t *leaf)
104
0
{
105
0
    herr_t ret_value = SUCCEED;
106
107
0
    FUNC_ENTER_NOAPI(FAIL)
108
109
0
    if (!leaf)
110
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid leaf");
111
112
    /* Free coordinate arrays */
113
0
    if (leaf->_coords) {
114
0
        free(leaf->_coords);
115
0
        leaf->_coords = NULL;
116
0
        leaf->min     = NULL;
117
0
        leaf->max     = NULL;
118
0
        leaf->mid     = NULL;
119
0
    }
120
121
0
done:
122
0
    FUNC_LEAVE_NOAPI(ret_value)
123
0
} /* end H5RT_leaf_cleanup() */
124
125
/*-------------------------------------------------------------------------
126
 * Function:    H5RT__leaf_compare
127
 *
128
 * Purpose:     Compare two R-tree leaves for sorting based on their midpoint
129
 *              coordinates in the specified dimension.
130
 *
131
 *              Uses GNU qsort_r signature (context parameter last) for
132
 *              compatibility with HDqsort_r macro.
133
 *
134
 * Return:      -1 if leaf1 < leaf2
135
 *               0 if leaf1 == leaf2
136
 *               1 if leaf1 > leaf2
137
 *
138
 *-------------------------------------------------------------------------
139
 */
140
static int
141
H5RT__leaf_compare(const void *leaf1, const void *leaf2, void *dim)
142
0
{
143
0
    const H5RT_leaf_t *l1       = (const H5RT_leaf_t *)leaf1;
144
0
    const H5RT_leaf_t *l2       = (const H5RT_leaf_t *)leaf2;
145
0
    int                sort_dim = 0;
146
147
0
    assert(leaf1);
148
0
    assert(leaf2);
149
0
    assert(dim);
150
151
0
    sort_dim = *(int *)dim;
152
153
0
    assert(sort_dim <= l1->rank - 1);
154
155
    /* Compare based on the midpoint of the specified dimension */
156
0
    if (l1->mid[sort_dim] < l2->mid[sort_dim])
157
0
        return -1;
158
0
    if (l1->mid[sort_dim] > l2->mid[sort_dim])
159
0
        return 1;
160
0
    return 0;
161
0
} /* end H5RT__leaf_compare() */
162
163
/*-------------------------------------------------------------------------
164
 * Function:    H5RT__compute_slabs
165
 *
166
 * Purpose:     Compute the number of slabs and slab size to use when partitioning
167
 *              leaves into slabs for bulk-loading the r-tree.
168
 *
169
 * Return:      Non-negative on success/Negative on failure
170
 *
171
 *-------------------------------------------------------------------------
172
 */
173
static herr_t
174
H5RT__compute_slabs(size_t node_capacity, size_t leaf_count, size_t *slab_count_out, size_t *slab_size_out)
175
0
{
176
0
    assert(node_capacity > 0);
177
0
    assert(leaf_count > 0);
178
0
    assert(slab_count_out);
179
0
    assert(slab_size_out);
180
0
    herr_t ret_value = SUCCEED;
181
182
0
    FUNC_ENTER_PACKAGE
183
184
0
    double num_slabs_d = -1.0;
185
0
    size_t num_slabs   = 0;
186
0
    double slab_size_d = -1.0;
187
0
    size_t slab_size   = 0;
188
189
0
    if (leaf_count <= node_capacity) {
190
        /* All leaves will fit into a single node */
191
0
        num_slabs = 1;
192
0
        slab_size = leaf_count;
193
0
    }
194
0
    else {
195
        /* Use intermediate variable to avoid warnings */
196
0
        slab_size_d = ceil((double)leaf_count / (double)node_capacity);
197
198
0
        if (slab_size_d > (double)SIZE_MAX)
199
0
            HGOTO_ERROR(H5E_RTREE, H5E_OVERFLOW, FAIL, "slab size overflows size_t");
200
0
        assert(slab_size_d > 0.0);
201
0
        slab_size = (size_t)slab_size_d;
202
0
        assert(slab_size > 0);
203
204
0
        num_slabs_d = ceil((double)leaf_count / (double)slab_size);
205
0
        if (num_slabs_d > (double)SIZE_MAX)
206
0
            HGOTO_ERROR(H5E_RTREE, H5E_OVERFLOW, FAIL, "number of slabs overflows size_t");
207
0
        assert(num_slabs_d > 0.0);
208
0
        num_slabs = (size_t)num_slabs_d;
209
0
    }
210
211
0
    assert(slab_size > 0);
212
0
    assert(slab_size <= leaf_count);
213
214
0
    assert(num_slabs > 0);
215
0
    assert(num_slabs <= node_capacity);
216
0
done:
217
0
    if (ret_value == SUCCEED) {
218
0
        *slab_count_out = num_slabs;
219
0
        *slab_size_out  = slab_size;
220
0
    }
221
0
    FUNC_LEAVE_NOAPI(ret_value)
222
0
} /* end H5RT__compute_slabs() */
223
224
/*-------------------------------------------------------------------------
225
 * Function:    H5RT__result_set_init
226
 *
227
 * Purpose:     Initialize a result buffer with initial capacity
228
 *
229
 * Return:      Non-negative on success/Negative on failure
230
 *
231
 *-------------------------------------------------------------------------
232
 */
233
static herr_t
234
H5RT__result_set_init(H5RT_result_set_t *result_set)
235
0
{
236
0
    herr_t ret_value = SUCCEED;
237
238
0
    FUNC_ENTER_PACKAGE
239
240
0
    assert(result_set);
241
242
0
    result_set->capacity = 32; /* Initial power-of-2 size */
243
0
    result_set->count    = 0;
244
245
    /* Allocate initial buffer */
246
0
    result_set->results = (H5RT_leaf_t **)malloc(result_set->capacity * sizeof(H5RT_leaf_t *));
247
0
    if (!result_set->results)
248
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "failed to allocate result buffer");
249
250
0
done:
251
0
    FUNC_LEAVE_NOAPI(ret_value)
252
0
} /* end H5RT__result_set_init() */
253
254
/*-------------------------------------------------------------------------
255
 * Function:    H5RT__result_set_grow
256
 *
257
 * Purpose:     Double the capacity of the result buffer and fix next pointers
258
 *
259
 * Return:      Non-negative on success/Negative on failure
260
 *
261
 *-------------------------------------------------------------------------
262
 */
263
static herr_t
264
H5RT__result_set_grow(H5RT_result_set_t *result_set)
265
0
{
266
0
    herr_t        ret_value    = SUCCEED;
267
0
    size_t        new_capacity = 0;
268
0
    H5RT_leaf_t **new_results  = NULL;
269
270
0
    FUNC_ENTER_PACKAGE
271
272
0
    assert(result_set);
273
0
    assert(result_set->results);
274
0
    assert(result_set->count == result_set->capacity);
275
276
0
    new_capacity = result_set->capacity * 2;
277
278
    /* Overflow check */
279
0
    if (new_capacity < result_set->capacity || new_capacity > (SIZE_MAX / sizeof(H5RT_leaf_t *)))
280
0
        HGOTO_ERROR(H5E_RTREE, H5E_OVERFLOW, FAIL, "result buffer capacity overflow");
281
282
    /* Reallocate the buffer */
283
0
    new_results = (H5RT_leaf_t **)realloc(result_set->results, new_capacity * sizeof(H5RT_leaf_t *));
284
0
    if (!new_results)
285
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "failed to grow result buffer");
286
287
0
    result_set->results  = new_results;
288
0
    result_set->capacity = new_capacity;
289
290
0
done:
291
0
    FUNC_LEAVE_NOAPI(ret_value)
292
0
} /* end H5RT__result_set_grow() */
293
294
/*-------------------------------------------------------------------------
295
 * Function:    H5RT__result_set_add
296
 *
297
 * Purpose:     Add a leaf to the result buffer, growing if necessary
298
 *
299
 * Return:      Non-negative on success/Negative on failure
300
 *
301
 *-------------------------------------------------------------------------
302
 */
303
static herr_t
304
H5RT__result_set_add(H5RT_result_set_t *result_set, H5RT_leaf_t *leaf)
305
0
{
306
0
    herr_t ret_value = SUCCEED;
307
308
0
    FUNC_ENTER_PACKAGE
309
310
0
    assert(result_set);
311
0
    assert(leaf);
312
313
    /* Grow buffer if full */
314
0
    if (result_set->count >= result_set->capacity) {
315
0
        if (H5RT__result_set_grow(result_set) < 0)
316
0
            HGOTO_ERROR(H5E_RTREE, H5E_CANTALLOC, FAIL, "failed to grow result buffer");
317
0
    }
318
319
    /* Add the new result */
320
0
    result_set->results[result_set->count] = leaf;
321
0
    result_set->count++;
322
323
0
done:
324
0
    FUNC_LEAVE_NOAPI(ret_value)
325
0
} /* end H5RT__result_set_add() */
326
327
/*-------------------------------------------------------------------------
328
 * Function:    H5RT__bulk_load
329
 *
330
 * Purpose:     Load the provided leaves into the r-tree in an efficient manner.
331
 *              This is an implementation of the sort-tile-recursive (STR) algorithm.
332
 *              See "STR: A Simple and Efficient Algorithm for R-Tree Packing"
333
 *              https://archive.org/details/nasa_techdoc_19970016975/page/n9
334
 *
335
 * Parameters:  node          - The node to fill
336
 *              rank          - The rank of the hyper-rectangles
337
 *              leaves        - A pointer to the first leaf in this block
338
 *              count         - The number of leaves in this block
339
 *              prev_sort_dim - The dimension that was last sorted on (or -1 if none)
340
 *
341
 * Return:      Non-negative on success/Negative on failure
342
 *
343
 *-------------------------------------------------------------------------
344
 */
345
static herr_t
346
H5RT__bulk_load(H5RT_node_t *node, int rank, H5RT_leaf_t *leaves, size_t count, int prev_sort_dim)
347
0
{
348
0
    herr_t       ret_value        = SUCCEED;
349
0
    size_t       leaves_left      = 0; /* Leaves left to partition */
350
0
    size_t       child_leaf_count = 0;
351
0
    H5RT_leaf_t *child_leaf_start = NULL;
352
353
0
    int sort_dim = -1;
354
355
0
    size_t num_slabs = 0;
356
0
    size_t slab_size = 0;
357
358
0
    FUNC_ENTER_PACKAGE
359
360
0
    assert(node);
361
0
    assert(leaves);
362
0
    assert(count > 0);
363
0
    assert(prev_sort_dim >= -1);
364
0
    assert(rank >= 1 && rank <= H5S_MAX_RANK);
365
366
    /* Compute the max/min bounds of the provided node */
367
    /* Initial values */
368
0
    for (int i = 0; i < rank; i++) {
369
0
        node->min[i] = leaves[0].min[i];
370
0
        node->max[i] = leaves[0].max[i];
371
0
    }
372
    /* Compute max/min from leaves */
373
0
    for (size_t i = 0; i < count; i++) {
374
0
        for (int d = 0; d < rank; d++) {
375
0
            if (leaves[i].min[d] < node->min[d])
376
0
                node->min[d] = leaves[i].min[d];
377
0
            if (leaves[i].max[d] > node->max[d])
378
0
                node->max[d] = leaves[i].max[d];
379
0
        }
380
0
    }
381
382
0
    if (count <= H5RT_MAX_NODE_SIZE) {
383
        /* Base Case - All leaves will fit into this node */
384
0
        node->nchildren           = (int)count;
385
0
        node->children_are_leaves = true;
386
0
        node->children.leaves     = leaves;
387
0
    }
388
0
    else {
389
        /* Recursive case - there will be child nodes */
390
0
        node->children_are_leaves = false;
391
392
        /* If we haven't sorted along every dimension yet, sort the hyper-rectangles in this region
393
         * by the first unsorted coordinate of their midpoints */
394
0
        if (prev_sort_dim != rank - 1) {
395
0
            assert(prev_sort_dim < rank - 1);
396
0
            sort_dim = prev_sort_dim + 1;
397
0
            if (H5_UNLIKELY(HDqsort_r((void *)leaves, count, sizeof(H5RT_leaf_t), H5RT__leaf_compare,
398
0
                                      (void *)&sort_dim) < 0))
399
0
                HGOTO_ERROR(H5E_INTERNAL, H5E_CANTSORT, FAIL, "failed to sort R-tree leaves");
400
0
        }
401
0
        else {
402
0
            sort_dim = prev_sort_dim;
403
0
        }
404
405
        /* After leaves are sorted in the current dimension, partition the hyper-rectangles into slabs */
406
407
        /* Compute number of slabs and slab size for partitioning */
408
0
        H5RT__compute_slabs(H5RT_MAX_NODE_SIZE, count, &num_slabs, &slab_size);
409
410
0
        node->nchildren = (int)num_slabs;
411
412
        /* Persistent pointer that is moved forward after each assignment
413
         * of a region leaves to a child node */
414
0
        child_leaf_start = leaves;
415
0
        leaves_left      = count;
416
417
        /* Recurse down to the next dimension to process each slab/region */
418
0
        for (int i = 0; i < node->nchildren; i++) {
419
            /* The final slab should exactly contain the last leaf */
420
0
            assert(leaves_left > 0);
421
422
            /* Allocate this child node */
423
0
            if (NULL == (node->children.nodes[i] = H5FL_MALLOC(H5RT_node_t)))
424
0
                HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "failed to allocate memory for R-tree node");
425
426
0
            child_leaf_count = (leaves_left < slab_size) ? leaves_left : slab_size;
427
428
            /* Recursively fill this child node with leaves from 'child_leaf_start' to 'child_leaf_start' +
429
             * 'child_leaf_count' */
430
0
            if (H5RT__bulk_load(node->children.nodes[i], rank, child_leaf_start, child_leaf_count, sort_dim) <
431
0
                0)
432
0
                HGOTO_ERROR(H5E_RTREE, H5E_CANTINIT, FAIL, "failed to fill R-tree");
433
434
            /* The next 'child_leaf_count' leaves are now assigned */
435
0
            child_leaf_start += child_leaf_count;
436
0
            leaves_left -= child_leaf_count;
437
0
        }
438
0
    }
439
440
0
done:
441
0
    if (ret_value < 0) {
442
        /* Free any nodes that were allocated at this level */
443
0
        if (node && !node->children_are_leaves) {
444
0
            for (int i = 0; i < node->nchildren; i++) {
445
0
                if (node->children.nodes[i]) {
446
0
                    H5FL_FREE(H5RT_node_t, node->children.nodes[i]);
447
0
                    node->children.nodes[i] = NULL;
448
0
                }
449
0
            }
450
0
        }
451
0
    }
452
0
    FUNC_LEAVE_NOAPI(ret_value);
453
0
} /* end H5RT__bulk_load() */
454
455
/*-------------------------------------------------------------------------
456
 * Function:    H5RT_create
457
 *
458
 * Purpose:     Create a new R-tree from the provided array of 'count'
459
 *               leaves, each with 'rank' spatial dimensions.
460
 *
461
 *              On success, the R-tree takes ownership of the caller-allocated
462
 *               leaves array.
463
 *
464
 *              NOTE: This routine uses a global variable internally, and
465
 *                is therefore not thread-safe. See the 'qsort_r_threadsafe'
466
 *                branch of the HDF5 GitHub repository for a beta
467
 *                implementation that is threadsafe.
468
 *
469
 * Return:      A valid pointer to the new R-tree on success/NULL on failure
470
 *
471
 *-------------------------------------------------------------------------
472
 */
473
H5RT_t *
474
H5RT_create(int rank, H5RT_leaf_t *leaves, size_t count)
475
0
{
476
0
    H5RT_t *rtree     = NULL;
477
0
    H5RT_t *ret_value = NULL;
478
479
0
    FUNC_ENTER_NOAPI(NULL)
480
481
0
    if (rank < 1 || rank > H5S_MAX_RANK)
482
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid rank");
483
484
0
    if (count == 0)
485
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "r-tree must have at least one leaf");
486
487
0
    if (NULL == (rtree = H5FL_MALLOC(H5RT_t)))
488
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "failed to allocate memory for R-tree");
489
490
0
    rtree->rank    = rank;
491
0
    rtree->nleaves = count;
492
493
    /* Take ownership of leaves array */
494
0
    rtree->leaves = leaves;
495
496
    /* Populate the r-tree with nodes containing the provided leaves */
497
0
    if (H5RT__bulk_load(&rtree->root, rank, rtree->leaves, count, -1) < 0)
498
0
        HGOTO_ERROR(H5E_RTREE, H5E_CANTINIT, NULL, "failed to fill R-tree");
499
500
0
    ret_value = rtree;
501
502
0
done:
503
0
    if (!ret_value && rtree)
504
0
        H5RT_free(rtree);
505
506
0
    FUNC_LEAVE_NOAPI(ret_value);
507
0
} /* end H5RT_create() */
508
509
/*-------------------------------------------------------------------------
510
 * Function:    H5RT__search_recurse
511
 *
512
 * Purpose:     Recursively search the r-tree for leaves whose bounding boxes
513
 *              intersect with the provided search region.
514
 *
515
 * Parameters:  node (in)   - Node from which to begin the search
516
 *              rank (in)   - Rank of the hyper-rectangles
517
 *              min (in)    - Minimum bounds of spatial search, should have 'rank' dims
518
 *              max (in)    - Maximum bounds of spatial search, should have 'rank' dims
519
 *              buffer (io) - Dynamic result buffer to add matches to
520
 *
521
 * Return:      Non-negative on success/Negative on failure
522
 *
523
 *-------------------------------------------------------------------------
524
 */
525
static herr_t
526
H5RT__search_recurse(H5RT_node_t *node, int rank, hsize_t min[], hsize_t max[], H5RT_result_set_t *result_set)
527
0
{
528
0
    hsize_t *curr_min  = NULL;
529
0
    hsize_t *curr_max  = NULL;
530
0
    herr_t   ret_value = SUCCEED;
531
532
0
    FUNC_ENTER_PACKAGE
533
534
0
    assert(node);
535
0
    assert(result_set);
536
537
    /* Check all children for intersection */
538
0
    if (node->children_are_leaves)
539
0
        for (int i = 0; i < node->nchildren; i++) {
540
0
            H5RT_leaf_t *curr_leaf = node->children.leaves + i;
541
0
            curr_min               = curr_leaf->min;
542
0
            curr_max               = curr_leaf->max;
543
544
0
            if (H5RT__leaves_intersect(rank, min, max, curr_min, curr_max)) {
545
                /* We found an intersecting leaf, add it to the result set */
546
0
                if (H5RT__result_set_add(result_set, curr_leaf) < 0)
547
0
                    HGOTO_ERROR(H5E_RTREE, H5E_CANTALLOC, FAIL, "failed to add result to result set");
548
0
            }
549
0
        }
550
0
    else
551
0
        for (int i = 0; i < node->nchildren; i++) {
552
            /* This is an internal node in the r-tree */
553
0
            H5RT_node_t *curr_node = node->children.nodes[i];
554
0
            curr_min               = curr_node->min;
555
0
            curr_max               = curr_node->max;
556
557
            /* Only recurse into child node if its bounding box overlaps with the search region */
558
0
            if (H5RT__leaves_intersect(rank, min, max, curr_min, curr_max)) {
559
                /* We found an intersecting internal node, recurse into it */
560
0
                if (H5RT__search_recurse(curr_node, rank, min, max, result_set) < 0)
561
0
                    HGOTO_ERROR(H5E_RTREE, H5E_CANTGET, FAIL, "recursive search failed");
562
0
            }
563
0
        }
564
565
0
done:
566
0
    FUNC_LEAVE_NOAPI(ret_value)
567
0
} /* end H5RT__search_recurse() */
568
569
/*-------------------------------------------------------------------------
570
 * Function:    H5RT_search
571
 *
572
 * Purpose:     Search the r-tree for leaves whose bounding boxes
573
 *              intersect with the provided min and max bounds.
574
 *
575
 *              Returns a linked list of H5RT_leaf_t * structures.
576
 *              The caller must call H5RT_free_results() to free the
577
 *              returned result list.
578
 *
579
 * Return:      Non-negative on success/Negative on failure
580
 *
581
 *-------------------------------------------------------------------------
582
 */
583
herr_t
584
H5RT_search(H5RT_t *rtree, hsize_t min[], hsize_t max[], H5RT_result_set_t **results_out)
585
0
{
586
0
    H5RT_result_set_t *result_set = NULL;
587
0
    herr_t             ret_value  = SUCCEED;
588
589
0
    FUNC_ENTER_NOAPI(FAIL)
590
591
0
    assert((hsize_t *)min);
592
0
    assert((hsize_t *)max);
593
594
0
    if (!rtree)
595
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid r-tree");
596
597
0
    if (!results_out)
598
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid results output pointer");
599
600
    /* Initialize result buffer */
601
0
    if ((result_set = (H5RT_result_set_t *)malloc(sizeof(H5RT_result_set_t))) == NULL)
602
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "failed to allocate result set");
603
604
0
    if (H5RT__result_set_init(result_set) < 0)
605
0
        HGOTO_ERROR(H5E_RTREE, H5E_CANTINIT, FAIL, "failed to initialize result buffer");
606
607
    /* Perform the actual search */
608
0
    if (H5RT__search_recurse(&rtree->root, rtree->rank, min, max, result_set) < 0)
609
0
        HGOTO_ERROR(H5E_RTREE, H5E_CANTGET, FAIL, "search failed");
610
611
    /* Don't cleanup result set on success - caller owns it now */
612
0
    *results_out = result_set;
613
614
0
done:
615
0
    if (ret_value < 0) {
616
        /* Clean up buffer on failure */
617
0
        if (H5RT_free_results(result_set) < 0)
618
0
            HDONE_ERROR(H5E_RTREE, H5E_CANTFREE, FAIL, "unable to free result set on error");
619
0
        *results_out = NULL;
620
0
    }
621
0
    FUNC_LEAVE_NOAPI(ret_value)
622
0
} /* end H5RT_search() */
623
624
/*-------------------------------------------------------------------------
625
 * Function:    H5RT_free_results
626
 *
627
 * Purpose:     Free search results returned by H5RT_search.
628
 *
629
 *              Frees both the result set structure and the underlying
630
 *              results array buffer.
631
 *
632
 * Return:      Non-negative on success/Negative on failure
633
 *
634
 *-------------------------------------------------------------------------
635
 */
636
herr_t
637
H5RT_free_results(H5RT_result_set_t *result_set)
638
0
{
639
0
    herr_t ret_value = SUCCEED;
640
641
0
    assert(result_set);
642
643
    /* Free the results array buffer */
644
0
    if (result_set->results)
645
0
        free(result_set->results);
646
647
    /* Free the result set structure itself */
648
0
    free(result_set);
649
650
0
    return ret_value;
651
0
} /* end H5RT_free_results() */
652
653
/*-------------------------------------------------------------------------
654
 * Function:    H5RT__node_copy
655
 *
656
 * Purpose:     Deep copy a node from source tree to destination tree,
657
 *              recursively copying all child nodes and remapping leaf
658
 *              pointers to the new leaves array.
659
 *
660
 * Parameters:  dest_node       - Pre-allocated destination node to copy into
661
 *              src_node        - Source node to copy from
662
 *              old_leaves_base - Base pointer of original tree's leaves array
663
 *              new_leaves_base - Base pointer of new tree's leaves array
664
 *
665
 * Return:      Non-negative on success/Negative on failure
666
 *
667
 *-------------------------------------------------------------------------
668
 */
669
static herr_t
670
H5RT__node_copy(H5RT_node_t *dest_node, const H5RT_node_t *src_node, const H5RT_leaf_t *old_leaves_base,
671
                H5RT_leaf_t *new_leaves_base)
672
0
{
673
0
    herr_t ret_value = SUCCEED;
674
675
0
    FUNC_ENTER_PACKAGE
676
677
0
    assert(dest_node);
678
0
    assert(src_node);
679
0
    assert(old_leaves_base);
680
0
    assert(new_leaves_base);
681
682
    /* Copy basic node fields */
683
0
    memcpy(dest_node->min, src_node->min, H5S_MAX_RANK * sizeof(hsize_t));
684
0
    memcpy(dest_node->max, src_node->max, H5S_MAX_RANK * sizeof(hsize_t));
685
0
    dest_node->nchildren           = src_node->nchildren;
686
0
    dest_node->children_are_leaves = src_node->children_are_leaves;
687
688
0
    if (src_node->children_are_leaves) {
689
        /* Leaf node: remap the leaves pointer to the corresponding position in new array */
690
0
        ptrdiff_t offset           = src_node->children.leaves - old_leaves_base;
691
0
        dest_node->children.leaves = new_leaves_base + offset;
692
0
    }
693
0
    else {
694
        /* Internal node: recursively copy each child node */
695
0
        for (int i = 0; i < src_node->nchildren; i++) {
696
            /* Allocate new child node */
697
0
            if (NULL == (dest_node->children.nodes[i] = H5FL_MALLOC(H5RT_node_t)))
698
0
                HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "failed to allocate child node");
699
700
            /* Recursively copy the child */
701
0
            if (H5RT__node_copy(dest_node->children.nodes[i], src_node->children.nodes[i], old_leaves_base,
702
0
                                new_leaves_base) < 0)
703
0
                HGOTO_ERROR(H5E_RTREE, H5E_CANTCOPY, FAIL, "failed to copy child node");
704
0
        }
705
0
    }
706
707
0
done:
708
0
    if (ret_value < 0 && dest_node && !dest_node->children_are_leaves) {
709
        /* Clean up any partially allocated child nodes */
710
0
        for (int i = 0; i < dest_node->nchildren; i++) {
711
0
            if (dest_node->children.nodes[i]) {
712
0
                H5FL_FREE(H5RT_node_t, dest_node->children.nodes[i]);
713
0
                dest_node->children.nodes[i] = NULL;
714
0
            }
715
0
        }
716
0
    }
717
718
0
    FUNC_LEAVE_NOAPI(ret_value)
719
0
} /* end H5RT__node_copy() */
720
721
/*-------------------------------------------------------------------------
722
 * Function:    H5RT__free_recurse
723
 *
724
 * Purpose:     Recursively free the provided node and its child nodes.
725
 *              Does not free the leaves.
726
 *
727
 * Return:      Non-negative on success/Negative on failure
728
 *
729
 *-------------------------------------------------------------------------
730
 */
731
static void
732
H5RT__free_recurse(H5RT_node_t *node)
733
0
{
734
0
    FUNC_ENTER_PACKAGE_NOERR
735
736
0
    assert(node);
737
738
    /* Only recurse if the children are more internal nodes */
739
0
    if (!node->children_are_leaves)
740
0
        for (int i = 0; i < node->nchildren; i++) {
741
0
            if (node->children.nodes[i]) {
742
0
                H5RT__free_recurse(node->children.nodes[i]);
743
0
                H5FL_FREE(H5RT_node_t, node->children.nodes[i]);
744
0
            }
745
0
        }
746
747
0
    FUNC_LEAVE_NOAPI_VOID
748
0
} /* end H5RT__free_recurse() */
749
750
/*-------------------------------------------------------------------------
751
 * Function:    H5RT_free
752
 *
753
 * Purpose:     Release the memory associated with an r-tree.
754
 *              The data pointed to by the leaves is left as-is.
755
 *
756
 * Return:      Non-negative on success/Negative on failure
757
 *
758
 *-------------------------------------------------------------------------
759
 */
760
herr_t
761
H5RT_free(H5RT_t *rtree)
762
0
{
763
0
    herr_t ret_value = SUCCEED;
764
765
0
    FUNC_ENTER_NOAPI(FAIL);
766
767
0
    if (!rtree)
768
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid r-tree");
769
770
0
    H5RT__free_recurse(&rtree->root);
771
772
    /* Free each leaf's coordinates (leaf structures are in array, not individually allocated) */
773
0
    for (size_t i = 0; i < rtree->nleaves; i++) {
774
0
        if (rtree->leaves[i]._coords)
775
0
            free(rtree->leaves[i]._coords);
776
0
    }
777
778
    /* Free the leaves array */
779
0
    free(rtree->leaves);
780
0
    H5FL_FREE(H5RT_t, rtree);
781
782
0
done:
783
0
    FUNC_LEAVE_NOAPI(ret_value);
784
0
} /* end H5RT_free() */
785
786
/*-------------------------------------------------------------------------
787
 * Function:    H5RT_copy
788
 *
789
 * Purpose:     Deep-copy the provided r-tree
790
 *
791
 *              NOTE:  The 'record' pointers in the leaves are shallow-copied.
792
 *
793
 * Return:      A valid pointer to the new r-tree on success/NULL on failure
794
 *
795
 *-------------------------------------------------------------------------
796
 */
797
H5RT_t *
798
H5RT_copy(const H5RT_t *rtree)
799
0
{
800
0
    H5RT_t *ret_value = NULL;
801
0
    H5RT_t *new_tree  = NULL;
802
803
0
    H5RT_leaf_t *new_leaves = NULL;
804
805
0
    FUNC_ENTER_NOAPI(NULL);
806
807
0
    if (!rtree)
808
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid r-tree");
809
810
0
    assert(rtree->leaves);
811
0
    assert(rtree->nleaves > 0);
812
813
    /* Deep copy the array of leaves */
814
0
    if (NULL == (new_leaves = (H5RT_leaf_t *)malloc(rtree->nleaves * sizeof(H5RT_leaf_t))))
815
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "failed to allocate memory for R-tree leaves");
816
817
    /* Deep copy each leaf manually */
818
0
    for (size_t i = 0; i < rtree->nleaves; i++) {
819
0
        const H5RT_leaf_t *src_leaf = &rtree->leaves[i];
820
821
        /* Allocate coordinate arrays for this leaf */
822
0
        new_leaves[i]._coords = (hsize_t *)malloc(3 * (size_t)src_leaf->rank * sizeof(hsize_t));
823
0
        if (!new_leaves[i]._coords) {
824
            /* Clean up already copied leaves */
825
0
            for (size_t j = 0; j < i; j++) {
826
0
                if (new_leaves[j]._coords)
827
0
                    free(new_leaves[j]._coords);
828
0
            }
829
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "failed to allocate leaf coordinates");
830
0
        }
831
832
        /* Set up the leaf structure */
833
0
        new_leaves[i].record = src_leaf->record;
834
0
        new_leaves[i].rank   = src_leaf->rank;
835
0
        new_leaves[i].min    = new_leaves[i]._coords;
836
0
        new_leaves[i].max    = new_leaves[i]._coords + src_leaf->rank;
837
0
        new_leaves[i].mid    = new_leaves[i]._coords + (2 * src_leaf->rank);
838
839
        /* Copy coordinate data */
840
0
        memcpy(new_leaves[i]._coords, src_leaf->_coords, 3 * (size_t)src_leaf->rank * sizeof(hsize_t));
841
0
    }
842
843
    /* Allocate new tree structure */
844
0
    if (NULL == (new_tree = H5FL_MALLOC(H5RT_t)))
845
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "failed to allocate memory for R-tree copy");
846
847
    /* Copy basic tree fields */
848
0
    new_tree->rank    = rtree->rank;
849
0
    new_tree->nleaves = rtree->nleaves;
850
0
    new_tree->leaves  = new_leaves;
851
852
    /* Deep copy the root node structure */
853
0
    if (H5RT__node_copy(&new_tree->root, &rtree->root, rtree->leaves, new_leaves) < 0)
854
0
        HGOTO_ERROR(H5E_RTREE, H5E_CANTCOPY, NULL, "failed to copy r-tree structure");
855
856
0
    ret_value = new_tree;
857
858
0
done:
859
0
    if (!ret_value) {
860
0
        if (new_tree) {
861
0
            if (H5RT_free(new_tree) < 0)
862
0
                HDONE_ERROR(H5E_RTREE, H5E_CANTFREE, NULL, "unable to free partially copied r-tree");
863
0
        }
864
0
        else if (new_leaves) {
865
            /* Free copied leaves and their coordinates */
866
0
            for (size_t i = 0; i < rtree->nleaves; i++) {
867
0
                if (new_leaves[i]._coords)
868
0
                    free(new_leaves[i]._coords);
869
0
            }
870
0
            free(new_leaves);
871
0
        }
872
0
    }
873
874
0
    FUNC_LEAVE_NOAPI(ret_value);
875
0
} /* end H5RT_copy() */