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() */ |