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