SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
HexFiend/HFBTree.m
1 //
2 // HFBTree.m
3 // BTree
4 //
5 // Created by peter on 2/6/09.
6 // Copyright 2009 ridiculous_fish. All rights reserved.
7 //
8
9 #import "HFBTree.h"
10 #include <malloc/malloc.h>
11
12 #define FIXUP_LENGTHS 0
13
14 #define BTREE_BRANCH_ORDER 10
15 #define BTREE_LEAF_ORDER 10
16
17 #define BTREE_ORDER 10
18 #define BTREE_NODE_MINIMUM_VALUE_COUNT (BTREE_ORDER / 2)
19
20 #define BTREE_LEAF_MINIMUM_VALUE_COUNT (BTREE_LEAF_ORDER / 2)
21
22 #define BAD_INDEX ((ChildIndex_t)(-1))
23 typedef unsigned int ChildIndex_t;
24
25 /* How deep can our tree get? 128 is huge. */
26 #define MAX_DEPTH 128
27 #define BAD_DEPTH ((TreeDepth_t)(-1))
28 typedef unsigned int TreeDepth_t;
29
30 #define TreeEntry NSObject <HFBTreeEntry>
31 #define HFBTreeLength(x) [(TreeEntry *)(x) length]
32
33
34 @class HFBTreeNode, HFBTreeBranch, HFBTreeLeaf;
35
36 static TreeEntry *btree_search(HFBTree *tree, HFBTreeIndex offset, HFBTreeIndex *outBeginningOffset);
37 static id btree_insert_returning_retained_value_for_parent(HFBTree *tree, TreeEntry *entry, HFBTreeIndex offset);
38 static BOOL btree_remove(HFBTree *tree, HFBTreeIndex offset);
39 static void __attribute__((unused)) btree_recursive_check_integrity(HFBTree *tree, HFBTreeNode *branchOrLeaf, TreeDepth_t depth, HFBTreeNode **linkHelper);
40 #if FIXUP_LENGTHS
41 static HFBTreeIndex btree_recursive_fixup_cached_lengths(HFBTree *tree, HFBTreeNode *branchOrLeaf);
42 #endif
43 static HFBTreeIndex __attribute__((unused)) btree_recursive_check_integrity_of_cached_lengths(HFBTreeNode *branchOrLeaf);
44 static BOOL btree_are_cached_lengths_correct(HFBTreeNode *branchOrLeaf, HFBTreeIndex *outLength);
45 #if FIXUP_LENGTHS
46 static NSUInteger btree_entry_count(HFBTreeNode *branchOrLeaf);
47 #endif
48 static ChildIndex_t count_node_values(HFBTreeNode *node);
49 static HFBTreeIndex sum_child_lengths(const id *children, const BOOL isLeaf);
50 static HFBTreeNode *mutable_copy_node(HFBTreeNode *node, TreeDepth_t depth, HFBTreeNode **linkingHelper);
51
52 #if NDEBUG
53 #define VERIFY_LENGTH(a)
54 #else
55 #define VERIFY_LENGTH(a) btree_recursive_check_integrity_of_cached_lengths((a))
56 #endif
57
58 #define IS_BRANCH(a) [(a) isKindOfClass:[HFBTreeBranch class]]
59 #define IS_LEAF(a) [(a) isKindOfClass:[HFBTreeLeaf class]]
60
61 #define ASSERT_IS_BRANCH(a) HFASSERT(IS_BRANCH(a))
62 #define ASSERT_IS_LEAF(a) HFASSERT(IS_LEAF(a))
63
64 #define GET_LENGTH(node, parentIsLeaf) ((parentIsLeaf) ? HFBTreeLength(node) : CHECK_CAST((node), HFBTreeNode)->subtreeLength)
65
66 #define CHECK_CAST(a, b) ({HFASSERT([(a) isKindOfClass:[b class]]); (b *)(a);})
67 #define CHECK_CAST_OR_NULL(a, b) ({HFASSERT((a == nil) || [(a) isKindOfClass:[b class]]); (b *)(a);})
68
69 #define DEFEAT_INLINE 1
70
71 #if DEFEAT_INLINE
72 #define FORCE_STATIC_INLINE static
73 #else
74 #define FORCE_STATIC_INLINE static __inline__ __attribute__((always_inline))
75 #endif
76
77 @interface HFBTreeEnumerator : NSEnumerator {
78 HFBTreeLeaf *currentLeaf;
79 ChildIndex_t childIndex;
80 }
81
82 - (instancetype)initWithLeaf:(HFBTreeLeaf *)leaf;
83
84 @end
85
86 @interface HFBTreeNode : NSObject {
87 @public
88 NSUInteger rc;
89 HFBTreeIndex subtreeLength;
90 HFBTreeNode *left, *right;
91 id children[BTREE_ORDER];
92 }
93
94 @end
95
96 @implementation HFBTreeNode
97
98 - (id)retain {
99 HFAtomicIncrement(&rc, NO);
100 return self;
101 }
102
103 - (oneway void)release {
104 NSUInteger result = HFAtomicDecrement(&rc, NO);
105 if (result == (NSUInteger)(-1)) {
106 [self dealloc];
107 }
108 }
109
110 - (NSUInteger)retainCount {
111 return 1 + rc;
112 }
113
114 - (void)dealloc {
115 for (ChildIndex_t i=0; i < BTREE_BRANCH_ORDER; i++) {
116 if (! children[i]) break;
117 [children[i] release];
118 }
119 [super dealloc];
120 }
121
122 - (NSString *)shortDescription {
123 return [NSString stringWithFormat:@"<%@: %p (%llu)>", [self class], self, subtreeLength];
124 }
125
126 @end
127
128 @interface HFBTreeBranch : HFBTreeNode
129 @end
130
131 @implementation HFBTreeBranch
132
133 - (NSString *)description {
134 const char *lengthsMatchString = (subtreeLength == sum_child_lengths(children, NO) ? "" : " INCONSISTENT ");
135 NSMutableString *s = [NSMutableString stringWithFormat:@"<%@: %p (length: %llu%s) (children: %u) (", [self class], self, subtreeLength, lengthsMatchString, count_node_values(self)];
136 NSUInteger i;
137 for (i=0; i < BTREE_ORDER; i++) {
138 if (children[i] == nil) break;
139 [s appendFormat:@"%s%@", (i == 0 ? "" : ", "), [children[i] shortDescription]];
140 }
141 [s appendString:@")>"];
142 return s;
143 }
144
145 @end
146
147 @interface HFBTreeLeaf : HFBTreeNode
148 @end
149
150 @implementation HFBTreeLeaf
151
152 - (NSString *)description {
153 NSMutableString *s = [NSMutableString stringWithFormat:@"<%@: %p (%u) (", [self class], self, count_node_values(self)];
154 NSUInteger i;
155 for (i=0; i < BTREE_ORDER; i++) {
156 if (children[i] == nil) break;
157 [s appendFormat:@"%s%@", (i == 0 ? "" : ", "), children[i]];
158 }
159 [s appendString:@")>"];
160 return s;
161 }
162
163 @end
164
165 @implementation HFBTree
166
167 - (instancetype)init {
168 self = [super init];
169 depth = BAD_DEPTH;
170 root = nil;
171 return self;
172 }
173
174 - (void)dealloc {
175 [root release];
176 [super dealloc];
177 }
178
179 #if HFUNIT_TESTS
180 - (void)checkIntegrityOfCachedLengths {
181 if (root == nil) {
182 /* nothing */
183 }
184 else {
185 btree_recursive_check_integrity_of_cached_lengths(root);
186 }
187 }
188
189 - (void)checkIntegrityOfBTreeStructure {
190 if (depth == BAD_DEPTH) {
191 HFASSERT(root == nil);
192 }
193 else {
194 HFBTreeNode *linkHelper[MAX_DEPTH + 1] = {};
195 btree_recursive_check_integrity(self, root, depth, linkHelper);
196 }
197 }
198 #endif
199
200 - (HFBTreeIndex)length {
201 if (root == nil) return 0;
202 return ((HFBTreeNode *)root)->subtreeLength;
203 }
204
205 - (void)insertEntry:(id)entryObj atOffset:(HFBTreeIndex)offset {
206 TreeEntry *entry = (TreeEntry *)entryObj; //avoid a conflicting types warning
207 HFASSERT(entry);
208 HFASSERT(offset <= [self length]);
209 if (! root) {
210 HFASSERT([self length] == 0);
211 HFASSERT(depth == BAD_DEPTH);
212 HFBTreeLeaf *leaf = [[HFBTreeLeaf alloc] init];
213 leaf->children[0] = [entry retain];
214 leaf->subtreeLength = HFBTreeLength(entry);
215 root = leaf;
216 depth = 0;
217 }
218 else {
219 HFBTreeNode *newParentValue = btree_insert_returning_retained_value_for_parent(self, entry, offset);
220 if (newParentValue) {
221 HFBTreeBranch *newRoot = [[HFBTreeBranch alloc] init];
222 newRoot->children[0] = root; //transfer our retain
223 newRoot->children[1] = newParentValue; //transfer the retain we got from the function
224 newRoot->subtreeLength = HFSum(root->subtreeLength, newParentValue->subtreeLength);
225 root = newRoot;
226 depth++;
227 HFASSERT(depth <= MAX_DEPTH);
228 }
229 #if FIXUP_LENGTHS
230 HFBTreeIndex outLength = -1;
231 if (! btree_are_cached_lengths_correct(root, &outLength)) {
232 puts("Fixed up length after insertion");
233 btree_recursive_fixup_cached_lengths(self, root);
234 }
235 #endif
236 }
237 }
238
239 - (TreeEntry *)entryContainingOffset:(HFBTreeIndex)offset beginningOffset:(HFBTreeIndex *)outBeginningOffset {
240 HFASSERT(root != nil);
241 return btree_search(self, offset, outBeginningOffset);
242 }
243
244 - (void)removeAllEntries {
245 [root release];
246 root = nil;
247 depth = BAD_DEPTH;
248 }
249
250 - (void)removeEntryAtOffset:(HFBTreeIndex)offset {
251 HFASSERT(root != nil);
252 #if FIXUP_LENGTHS
253 const NSUInteger beforeCount = btree_entry_count(root);
254 #endif
255 BOOL deleteRoot = btree_remove(self, offset);
256 if (deleteRoot) {
257 HFASSERT(count_node_values(root) <= 1);
258 id newRoot = [root->children[0] retain]; //may be nil!
259 [root release];
260 root = newRoot;
261 depth--;
262 }
263 #if FIXUP_LENGTHS
264 const NSUInteger afterCount = btree_entry_count(root);
265 if (beforeCount != afterCount + 1) {
266 NSLog(@"Bad counts: before %lu, after %lu", beforeCount, afterCount);
267 }
268 HFBTreeIndex outLength = -1;
269 static NSUInteger fixupCount;
270 if (! btree_are_cached_lengths_correct(root, &outLength)) {
271 fixupCount++;
272 printf("Fixed up length after deletion (%lu)\n", (unsigned long)fixupCount);
273 btree_recursive_fixup_cached_lengths(self, root);
274 }
275 else {
276 //printf("Length post-deletion was OK! (%lu)\n", fixupCount);
277 }
278 #endif
279 }
280
281 - (id)mutableCopyWithZone:(NSZone *)zone {
282 USE(zone);
283 HFBTree *result = [[[self class] alloc] init];
284 result->depth = depth;
285 HFBTreeNode *linkingHelper[MAX_DEPTH + 1];
286 bzero(linkingHelper, (1 + depth) * sizeof *linkingHelper);
287 result->root = mutable_copy_node(root, depth, linkingHelper);
288 return result;
289 }
290
291 FORCE_STATIC_INLINE ChildIndex_t count_node_values(HFBTreeNode *node) {
292 ChildIndex_t count;
293 for (count=0; count < BTREE_LEAF_ORDER; count++) {
294 if (node->children[count] == nil) break;
295 }
296 return count;
297 }
298
299 FORCE_STATIC_INLINE HFBTreeIndex sum_child_lengths(const id *children, const BOOL isLeaf) {
300 HFBTreeIndex result = 0;
301 for (ChildIndex_t childIndex = 0; childIndex < BTREE_ORDER; childIndex++) {
302 id child = children[childIndex];
303 if (! child) break;
304 HFBTreeIndex childLength = GET_LENGTH(child, isLeaf);
305 result = HFSum(result, childLength);
306 }
307 return result;
308 }
309
310 FORCE_STATIC_INLINE HFBTreeIndex sum_N_child_lengths(const id *children, ChildIndex_t numChildren, const BOOL isLeaf) {
311 HFBTreeIndex result = 0;
312 for (ChildIndex_t childIndex = 0; childIndex < numChildren; childIndex++) {
313 id child = children[childIndex];
314 HFASSERT(child != NULL);
315 HFBTreeIndex childLength = GET_LENGTH(child, isLeaf);
316 result = HFSum(result, childLength);
317 }
318 return result;
319 }
320
321 FORCE_STATIC_INLINE ChildIndex_t index_containing_offset(HFBTreeNode *node, HFBTreeIndex offset, HFBTreeIndex * restrict outOffset, const BOOL isLeaf) {
322 ChildIndex_t childIndex;
323 HFBTreeIndex previousSum = 0;
324 const id *children = node->children;
325 for (childIndex = 0; childIndex < BTREE_ORDER; childIndex++) {
326 HFASSERT(children[childIndex] != nil);
327 HFBTreeIndex childLength = GET_LENGTH(children[childIndex], isLeaf);
328 HFBTreeIndex newSum = HFSum(childLength, previousSum);
329 if (newSum > offset) {
330 break;
331 }
332 previousSum = newSum;
333 }
334 *outOffset = previousSum;
335 return childIndex;
336 }
337
338 FORCE_STATIC_INLINE id child_containing_offset(HFBTreeNode *node, HFBTreeIndex offset, HFBTreeIndex * restrict outOffset, const BOOL isLeaf) {
339 return node->children[index_containing_offset(node, offset, outOffset, isLeaf)];
340 }
341
342 FORCE_STATIC_INLINE ChildIndex_t index_for_child_at_offset(HFBTreeNode *node, HFBTreeIndex offset, const BOOL isLeaf) {
343 ChildIndex_t childIndex;
344 HFBTreeIndex previousSum = 0;
345 id *const children = node->children;
346 for (childIndex = 0; childIndex < BTREE_ORDER; childIndex++) {
347 if (previousSum == offset) break;
348 HFASSERT(children[childIndex] != nil);
349 HFBTreeIndex childLength = GET_LENGTH(children[childIndex], isLeaf);
350 previousSum = HFSum(childLength, previousSum);
351 HFASSERT(previousSum <= offset);
352 }
353 HFASSERT(childIndex <= BTREE_ORDER); //note we allow the child index to be one past the end (in which case we are sure to split the node)
354 HFASSERT(previousSum == offset); //but we still require the offset to be the sum of all the lengths of this node
355 return childIndex;
356 }
357
358 FORCE_STATIC_INLINE ChildIndex_t child_index_for_insertion_at_offset(HFBTreeBranch *branch, HFBTreeIndex insertionOffset, HFBTreeIndex *outPriorCombinedOffset) {
359 ChildIndex_t indexForInsertion;
360 HFBTreeIndex priorCombinedOffset = 0;
361 id *const children = branch->children;
362 for (indexForInsertion = 0; indexForInsertion < BTREE_BRANCH_ORDER; indexForInsertion++) {
363 if (! children[indexForInsertion]) break;
364 HFBTreeNode *childNode = CHECK_CAST(children[indexForInsertion], HFBTreeNode);
365 HFBTreeIndex subtreeLength = childNode->subtreeLength;
366 HFASSERT(subtreeLength > 0);
367 HFBTreeIndex newOffset = HFSum(priorCombinedOffset, subtreeLength);
368 if (newOffset >= insertionOffset) {
369 break;
370 }
371 priorCombinedOffset = newOffset;
372 }
373 *outPriorCombinedOffset = priorCombinedOffset;
374 return indexForInsertion;
375 }
376
377 FORCE_STATIC_INLINE ChildIndex_t child_index_for_deletion_at_offset(HFBTreeBranch *branch, HFBTreeIndex deletionOffset, HFBTreeIndex *outPriorCombinedOffset) {
378 ChildIndex_t indexForDeletion;
379 HFBTreeIndex priorCombinedOffset = 0;
380 for (indexForDeletion = 0; indexForDeletion < BTREE_BRANCH_ORDER; indexForDeletion++) {
381 HFASSERT(branch->children[indexForDeletion] != nil);
382 HFBTreeNode *childNode = CHECK_CAST(branch->children[indexForDeletion], HFBTreeNode);
383 HFBTreeIndex subtreeLength = childNode->subtreeLength;
384 HFASSERT(subtreeLength > 0);
385 HFBTreeIndex newOffset = HFSum(priorCombinedOffset, subtreeLength);
386 if (newOffset > deletionOffset) {
387 /* Key difference between insertion and deletion: insertion uses >=, while deletion uses > */
388 break;
389 }
390 priorCombinedOffset = newOffset;
391 }
392 *outPriorCombinedOffset = priorCombinedOffset;
393 return indexForDeletion;
394 }
395
396 FORCE_STATIC_INLINE void insert_value_into_array(id value, NSUInteger insertionIndex, id *array, NSUInteger arrayCount) {
397 HFASSERT(insertionIndex <= arrayCount);
398 HFASSERT(arrayCount > 0);
399 NSUInteger pushingIndex = arrayCount - 1;
400 while (pushingIndex > insertionIndex) {
401 array[pushingIndex] = array[pushingIndex - 1];
402 pushingIndex--;
403 }
404 array[insertionIndex] = [value retain];
405 }
406
407
408 FORCE_STATIC_INLINE void remove_value_from_array(NSUInteger removalIndex, id *array, NSUInteger arrayCount) {
409 HFASSERT(removalIndex < arrayCount);
410 HFASSERT(arrayCount > 0);
411 HFASSERT(array[removalIndex] != nil);
412 [array[removalIndex] release];
413 for (NSUInteger pullingIndex = removalIndex + 1; pullingIndex < arrayCount; pullingIndex++) {
414 array[pullingIndex - 1] = array[pullingIndex];
415 }
416 array[arrayCount - 1] = nil;
417 }
418
419 static void split_array(const restrict id *values, ChildIndex_t valueCount, restrict id *left, restrict id *right, ChildIndex_t leftArraySizeForClearing) {
420 const ChildIndex_t midPoint = valueCount/2;
421 ChildIndex_t inputIndex = 0, outputIndex = 0;
422 while (inputIndex < midPoint) {
423 left[outputIndex++] = values[inputIndex++];
424 }
425
426 /* Clear the remainder of our left array. Right array does not have to be cleared. */
427 HFASSERT(outputIndex <= leftArraySizeForClearing);
428 while (outputIndex < leftArraySizeForClearing) {
429 left[outputIndex++] = nil;
430 }
431
432 /* Move the second half of our values into the right array */
433 outputIndex = 0;
434 while (inputIndex < valueCount) {
435 right[outputIndex++] = values[inputIndex++];
436 }
437 }
438
439 FORCE_STATIC_INLINE HFBTreeNode *add_child_to_node_possibly_creating_split(HFBTreeNode *node, id value, ChildIndex_t insertionLocation, BOOL isLeaf) {
440 ChildIndex_t childCount = count_node_values(node);
441 HFASSERT(insertionLocation <= childCount);
442 if (childCount < BTREE_ORDER) {
443 /* No need to make a split */
444 insert_value_into_array(value, insertionLocation, node->children, childCount + 1);
445 node->subtreeLength = HFSum(node->subtreeLength, GET_LENGTH(value, isLeaf));
446 return nil;
447 }
448
449 HFASSERT(node->children[BTREE_ORDER - 1] != nil); /* we require that it be full */
450 id allEntries[BTREE_ORDER + 1];
451 memcpy(allEntries, node->children, BTREE_ORDER * sizeof *node->children);
452 allEntries[BTREE_ORDER] = nil;
453
454 /* insert_value_into_array applies a retain, so allEntries owns a retain on its values */
455 insert_value_into_array(value, insertionLocation, allEntries, BTREE_ORDER + 1);
456 HFBTreeNode *newNode = [[[node class] alloc] init];
457
458 /* figure out our total length */
459 HFBTreeIndex totalLength = HFSum(node->subtreeLength, GET_LENGTH(value, isLeaf));
460
461 /* Distribute half our values to the new leaf */
462 split_array(allEntries, sizeof allEntries / sizeof *allEntries, node->children, newNode->children, BTREE_ORDER);
463
464 /* figure out how much is in the new array */
465 HFBTreeIndex newNodeLength = sum_child_lengths(newNode->children, isLeaf);
466
467 /* update our lengths */
468 HFASSERT(newNodeLength < totalLength);
469 newNode->subtreeLength = newNodeLength;
470 node->subtreeLength = totalLength - newNodeLength;
471
472 /* Link it in */
473 HFBTreeNode *rightNode = node->right;
474 newNode->right = rightNode;
475 if (rightNode) rightNode->left = newNode;
476 newNode->left = node;
477 node->right = newNode;
478 return newNode;
479 }
480
481 FORCE_STATIC_INLINE void add_values_to_array(const id * restrict srcValues, NSUInteger amountToCopy, id * restrict targetValues, NSUInteger amountToPush) {
482 // a pushed value at index X goes to index X + amountToCopy
483 NSUInteger pushIndex = amountToPush;
484 while (pushIndex--) {
485 targetValues[amountToCopy + pushIndex] = targetValues[pushIndex];
486 }
487 for (NSUInteger i = 0; i < amountToCopy; i++) {
488 targetValues[i] = [srcValues[i] retain];
489 }
490 }
491
492 FORCE_STATIC_INLINE void remove_values_from_array(id * restrict array, NSUInteger amountToRemove, NSUInteger totalArrayLength) {
493 HFASSERT(totalArrayLength >= amountToRemove);
494 /* Release existing values */
495 NSUInteger i;
496 for (i=0; i < amountToRemove; i++) {
497 [array[i] release];
498 }
499 /* Move remaining values */
500 for (i=amountToRemove; i < totalArrayLength; i++) {
501 array[i - amountToRemove] = array[i];
502 }
503 /* Clear the end */
504 for (i=totalArrayLength - amountToRemove; i < totalArrayLength; i++) {
505 array[i] = nil;
506 }
507 }
508
509 FORCE_STATIC_INLINE BOOL rebalance_node_by_distributing_to_neighbors(HFBTreeNode *node, ChildIndex_t childCount, BOOL isLeaf, BOOL * restrict modifiedLeftNeighbor, BOOL *restrict modifiedRightNeighbor) {
510 HFASSERT(childCount < BTREE_NODE_MINIMUM_VALUE_COUNT);
511 BOOL result = NO;
512 HFBTreeNode *leftNeighbor = node->left, *rightNeighbor = node->right;
513 const ChildIndex_t leftSpaceAvailable = (leftNeighbor ? BTREE_ORDER - count_node_values(leftNeighbor) : 0);
514 const ChildIndex_t rightSpaceAvailable = (rightNeighbor ? BTREE_ORDER - count_node_values(rightNeighbor) : 0);
515 if (leftSpaceAvailable + rightSpaceAvailable >= childCount) {
516 /* We have enough space to redistribute. Try to do it in such a way that both neighbors end up with the same number of items. */
517 ChildIndex_t itemCountForLeft = 0, itemCountForRight = 0, itemCountRemaining = childCount;
518 if (leftSpaceAvailable > rightSpaceAvailable) {
519 ChildIndex_t amountForLeft = MIN(leftSpaceAvailable - rightSpaceAvailable, itemCountRemaining);
520 itemCountForLeft += amountForLeft;
521 itemCountRemaining -= amountForLeft;
522 }
523 else if (rightSpaceAvailable > leftSpaceAvailable) {
524 ChildIndex_t amountForRight = MIN(rightSpaceAvailable - leftSpaceAvailable, itemCountRemaining);
525 itemCountForRight += amountForRight;
526 itemCountRemaining -= amountForRight;
527 }
528 /* Now distribute the remainder (if any) evenly, preferring the remainder to go left, because it is slightly cheaper to append to the left than prepend to the right */
529 itemCountForRight += itemCountRemaining / 2;
530 itemCountForLeft += itemCountRemaining - (itemCountRemaining / 2);
531 HFASSERT(itemCountForLeft <= leftSpaceAvailable);
532 HFASSERT(itemCountForRight <= rightSpaceAvailable);
533 HFASSERT(itemCountForLeft + itemCountForRight == childCount);
534
535 if (itemCountForLeft > 0) {
536 /* append to the end */
537 HFBTreeIndex additionalLengthForLeft = sum_N_child_lengths(node->children, itemCountForLeft, isLeaf);
538 leftNeighbor->subtreeLength = HFSum(leftNeighbor->subtreeLength, additionalLengthForLeft);
539 add_values_to_array(node->children, itemCountForLeft, leftNeighbor->children + BTREE_ORDER - leftSpaceAvailable, 0);
540 HFASSERT(leftNeighbor->subtreeLength == sum_child_lengths(leftNeighbor->children, isLeaf));
541 *modifiedLeftNeighbor = YES;
542 }
543 if (itemCountForRight > 0) {
544 /* append to the beginning */
545 HFBTreeIndex additionalLengthForRight = sum_N_child_lengths(node->children + itemCountForLeft, itemCountForRight, isLeaf);
546 rightNeighbor->subtreeLength = HFSum(rightNeighbor->subtreeLength, additionalLengthForRight);
547 add_values_to_array(node->children + itemCountForLeft, itemCountForRight, rightNeighbor->children, BTREE_ORDER - rightSpaceAvailable);
548 HFASSERT(rightNeighbor->subtreeLength == sum_child_lengths(rightNeighbor->children, isLeaf));
549 *modifiedRightNeighbor = YES;
550 }
551 /* Remove ourself from the linked list */
552 if (leftNeighbor) {
553 leftNeighbor->right = rightNeighbor;
554 }
555 if (rightNeighbor) {
556 rightNeighbor->left = leftNeighbor;
557 }
558 /* Even though we've essentially orphaned ourself, we need to force ourselves consistent (by making ourselves empty) because our parent still references us, and we don't want to make our parent inconsistent. */
559 for (ChildIndex_t childIndex = 0; node->children[childIndex] != nil; childIndex++) {
560 [node->children[childIndex] release];
561 node->children[childIndex] = nil;
562 }
563 node->subtreeLength = 0;
564
565 result = YES;
566 }
567 return result;
568 }
569
570
571 FORCE_STATIC_INLINE BOOL share_children(HFBTreeNode *node, ChildIndex_t childCount, HFBTreeNode *neighbor, BOOL isRightNeighbor, BOOL isLeaf) {
572 ChildIndex_t neighborCount = count_node_values(neighbor);
573 ChildIndex_t totalChildren = (childCount + neighborCount);
574 BOOL result = NO;
575 if (totalChildren <= 2 * BTREE_LEAF_ORDER && totalChildren >= 2 * BTREE_LEAF_MINIMUM_VALUE_COUNT) {
576 ChildIndex_t finalMyCount = totalChildren / 2;
577 ChildIndex_t finalNeighborCount = totalChildren - finalMyCount;
578 HFASSERT(finalNeighborCount < neighborCount);
579 HFASSERT(finalMyCount > childCount);
580 ChildIndex_t amountToTransfer = finalMyCount - childCount;
581 HFBTreeIndex lengthChange;
582 if (isRightNeighbor) {
583 /* Transfer from left end of right neighbor to this right end of this leaf. This retains the values. */
584 add_values_to_array(neighbor->children, amountToTransfer, node->children + childCount, 0);
585 /* Remove from beginning of right neighbor. This releases them. */
586 remove_values_from_array(neighbor->children, amountToTransfer, neighborCount);
587 lengthChange = sum_N_child_lengths(node->children + childCount, amountToTransfer, isLeaf);
588 }
589 else {
590 /* Transfer from right end of left neighbor to left end of this leaf */
591 add_values_to_array(neighbor->children + neighborCount - amountToTransfer, amountToTransfer, node->children, childCount);
592 /* Remove from end of left neighbor */
593 remove_values_from_array(neighbor->children + neighborCount - amountToTransfer, amountToTransfer, amountToTransfer);
594 lengthChange = sum_N_child_lengths(node->children, amountToTransfer, isLeaf);
595 }
596 HFASSERT(lengthChange <= neighbor->subtreeLength);
597 neighbor->subtreeLength -= lengthChange;
598 node->subtreeLength = HFSum(node->subtreeLength, lengthChange);
599 HFASSERT(count_node_values(node) == finalMyCount);
600 HFASSERT(count_node_values(neighbor) == finalNeighborCount);
601 result = YES;
602 }
603 return result;
604 }
605
606 static BOOL rebalance_node_by_sharing_with_neighbors(HFBTreeNode *node, ChildIndex_t childCount, BOOL isLeaf, BOOL * restrict modifiedLeftNeighbor, BOOL *restrict modifiedRightNeighbor) {
607 HFASSERT(childCount < BTREE_LEAF_MINIMUM_VALUE_COUNT);
608 BOOL result = NO;
609 HFBTreeNode *leftNeighbor = node->left, *rightNeighbor = node->right;
610 if (leftNeighbor) {
611 result = share_children(node, childCount, leftNeighbor, NO, isLeaf);
612 if (result) *modifiedLeftNeighbor = YES;
613 }
614 if (! result && rightNeighbor) {
615 result = share_children(node, childCount, rightNeighbor, YES, isLeaf);
616 if (result) *modifiedRightNeighbor = YES;
617 }
618 return result;
619 }
620
621 /* Return YES if this leaf should be removed after rebalancing. Other nodes are never removed. */
622 FORCE_STATIC_INLINE BOOL rebalance_node_after_deletion(HFBTreeNode *node, ChildIndex_t childCount, BOOL isLeaf, BOOL * restrict modifiedLeftNeighbor, BOOL *restrict modifiedRightNeighbor) {
623 HFASSERT(childCount < BTREE_LEAF_MINIMUM_VALUE_COUNT);
624 /* We may only delete this leaf, and not adjacent leaves. Thus our rebalancing strategy is:
625 If the items to the left or right have sufficient space to hold us, then push our values left or right, and delete this node.
626 Otherwise, steal items from the left until we have the same number of items. */
627 BOOL deleteNode = NO;
628 if (rebalance_node_by_distributing_to_neighbors(node, childCount, isLeaf, modifiedLeftNeighbor, modifiedRightNeighbor)) {
629 deleteNode = YES;
630 //puts("rebalance_node_by_distributing_to_neighbors");
631 }
632 else if (rebalance_node_by_sharing_with_neighbors(node, childCount, isLeaf, modifiedLeftNeighbor, modifiedRightNeighbor)) {
633 deleteNode = NO;
634 //puts("rebalance_node_by_sharing_with_neighbors");
635 }
636 else {
637 [NSException raise:NSInternalInconsistencyException format:@"Unable to rebalance after deleting node %@", node];
638 }
639 return deleteNode;
640 }
641
642
643 FORCE_STATIC_INLINE BOOL remove_value_from_node_with_possible_rebalance(HFBTreeNode *node, ChildIndex_t childIndex, BOOL isRootNode, BOOL isLeaf, BOOL * restrict modifiedLeftNeighbor, BOOL *restrict modifiedRightNeighbor) {
644 HFASSERT(childIndex < BTREE_ORDER);
645 HFASSERT(node != nil);
646 HFASSERT(node->children[childIndex] != nil);
647 HFBTreeIndex entryLength = GET_LENGTH(node->children[childIndex], isLeaf);
648 HFASSERT(entryLength <= node->subtreeLength);
649 node->subtreeLength -= entryLength;
650 BOOL deleteInputNode = NO;
651
652 #if ! NDEBUG
653 const id savedChild = node->children[childIndex];
654 NSUInteger childMultiplicity = 0;
655 NSUInteger v;
656 for (v = 0; v < BTREE_ORDER; v++) {
657 if (node->children[v] == savedChild) childMultiplicity++;
658 if (node->children[v] == nil) break;
659 }
660
661 #endif
662
663 /* Figure out how many children we have; start at one more than childIndex since we know that childIndex is a valid index */
664 ChildIndex_t childCount;
665 for (childCount = childIndex + 1; childCount < BTREE_ORDER; childCount++) {
666 if (! node->children[childCount]) break;
667 }
668
669 /* Remove our value at childIndex; this sends it a release message */
670 remove_value_from_array(childIndex, node->children, childCount);
671 HFASSERT(childCount > 0);
672 childCount--;
673
674 #if ! NDEBUG
675 for (v = 0; v < childCount; v++) {
676 if (node->children[v] == savedChild) childMultiplicity--;
677 }
678 HFASSERT(childMultiplicity == 1);
679 #endif
680
681 if (childCount < BTREE_LEAF_MINIMUM_VALUE_COUNT && ! isRootNode) {
682 /* We have too few items; try to rebalance (this will always be possible except from the root node) */
683 deleteInputNode = rebalance_node_after_deletion(node, childCount, isLeaf, modifiedLeftNeighbor, modifiedRightNeighbor);
684 }
685 else {
686 //NSLog(@"Deletion from %@ with %u remaining, %s root node, so no need to rebalance\n", node, childCount, isRootNode ? "is" : "is not");
687 }
688
689 return deleteInputNode;
690 }
691
692 FORCE_STATIC_INLINE void update_node_having_changed_size_of_child(HFBTreeNode *node, BOOL isLeaf) {
693 HFBTreeIndex newLength = sum_child_lengths(node->children, isLeaf);
694 /* This should only be called if the length actually changes - so assert as such */
695 /* I no longer think the above line is true. It's possible that we can delete a node, and then after a rebalance, we can become the same size we were before. */
696 //HFASSERT(node->subtreeLength != newLength);
697 node->subtreeLength = newLength;
698 }
699
700 struct SubtreeInfo_t {
701 HFBTreeBranch *branch;
702 ChildIndex_t childIndex; //childIndex is the index of the child of branch, not branch's index in its parent
703 };
704
705 static HFBTreeLeaf *btree_descend(HFBTree *tree, struct SubtreeInfo_t *outDescentInfo, HFBTreeIndex *insertionOffset, BOOL isForDelete) {
706 TreeDepth_t maxDepth = tree->depth;
707 HFASSERT(maxDepth != BAD_DEPTH && maxDepth <= MAX_DEPTH);
708 id currentBranchOrLeaf = tree->root;
709 HFBTreeIndex offsetForSubtree = *insertionOffset;
710 for (TreeDepth_t currentDepth = 0; currentDepth < maxDepth; currentDepth++) {
711 ASSERT_IS_BRANCH(currentBranchOrLeaf);
712 HFBTreeBranch *currentBranch = currentBranchOrLeaf;
713 HFBTreeIndex priorCombinedOffset = (HFBTreeIndex)-1;
714 ChildIndex_t nextChildIndex = (isForDelete ? child_index_for_deletion_at_offset : child_index_for_insertion_at_offset)(currentBranch, offsetForSubtree, &priorCombinedOffset);
715 outDescentInfo[currentDepth].branch = currentBranch;
716 outDescentInfo[currentDepth].childIndex = nextChildIndex;
717 offsetForSubtree -= priorCombinedOffset;
718 currentBranchOrLeaf = currentBranch->children[nextChildIndex];
719 if (isForDelete) {
720 HFBTreeNode *node = currentBranchOrLeaf;
721 HFASSERT(node->subtreeLength > offsetForSubtree);
722 }
723 }
724 ASSERT_IS_LEAF(currentBranchOrLeaf);
725 *insertionOffset = offsetForSubtree;
726 return currentBranchOrLeaf;
727 }
728
729 struct LeafInfo_t {
730 HFBTreeLeaf *leaf;
731 ChildIndex_t entryIndex;
732 HFBTreeIndex offsetOfEntryInTree;
733 };
734
735 static struct LeafInfo_t btree_find_leaf(HFBTree *tree, HFBTreeIndex offset) {
736 TreeDepth_t depth = tree->depth;
737 HFBTreeNode *currentNode = tree->root;
738 HFBTreeIndex remainingOffset = offset;
739 while (depth--) {
740 HFBTreeIndex beginningOffsetOfNode;
741 currentNode = child_containing_offset(currentNode, remainingOffset, &beginningOffsetOfNode, NO);
742 HFASSERT(beginningOffsetOfNode <= remainingOffset);
743 remainingOffset = remainingOffset - beginningOffsetOfNode;
744 }
745 ASSERT_IS_LEAF(currentNode);
746 HFBTreeIndex startOffsetOfEntry;
747 ChildIndex_t entryIndex = index_containing_offset(currentNode, remainingOffset, &startOffsetOfEntry, YES);
748 /* The offset of this entry is the requested offset minus the difference between its starting offset within the leaf and the requested offset within the leaf */
749 HFASSERT(remainingOffset >= startOffsetOfEntry);
750 HFBTreeIndex offsetIntoEntry = remainingOffset - startOffsetOfEntry;
751 HFASSERT(offset >= offsetIntoEntry);
752 HFBTreeIndex beginningOffset = offset - offsetIntoEntry;
753 return (struct LeafInfo_t){.leaf = CHECK_CAST(currentNode, HFBTreeLeaf), .entryIndex = entryIndex, .offsetOfEntryInTree = beginningOffset};
754 }
755
756 static TreeEntry *btree_search(HFBTree *tree, HFBTreeIndex offset, HFBTreeIndex *outBeginningOffset) {
757 struct LeafInfo_t leafInfo = btree_find_leaf(tree, offset);
758 *outBeginningOffset = leafInfo.offsetOfEntryInTree;
759 return leafInfo.leaf->children[leafInfo.entryIndex];
760 }
761
762 static id btree_insert_returning_retained_value_for_parent(HFBTree *tree, TreeEntry *entry, HFBTreeIndex insertionOffset) {
763 struct SubtreeInfo_t descentInfo[MAX_DEPTH];
764 #if ! NDEBUG
765 memset(descentInfo, -1, sizeof descentInfo);
766 #endif
767 HFBTreeIndex subtreeOffset = insertionOffset;
768 HFBTreeLeaf *leaf = btree_descend(tree, descentInfo, &subtreeOffset, NO);
769 ASSERT_IS_LEAF(leaf);
770
771 ChildIndex_t insertionLocation = index_for_child_at_offset(leaf, subtreeOffset, YES);
772 HFBTreeNode *retainedValueToInsertIntoParentBranch = add_child_to_node_possibly_creating_split(leaf, entry, insertionLocation, YES);
773
774 /* Walk up */
775 TreeDepth_t depth = tree->depth;
776 HFASSERT(depth != BAD_DEPTH);
777 HFBTreeIndex entryLength = HFBTreeLength(entry);
778 while (depth--) {
779 HFBTreeBranch *branch = descentInfo[depth].branch;
780 branch->subtreeLength = HFSum(branch->subtreeLength, entryLength);
781 ChildIndex_t childIndex = descentInfo[depth].childIndex;
782 if (retainedValueToInsertIntoParentBranch) {
783 HFASSERT(branch->subtreeLength > retainedValueToInsertIntoParentBranch->subtreeLength);
784 /* Since we copied some stuff out from under ourselves, subtract its length */
785 branch->subtreeLength -= retainedValueToInsertIntoParentBranch->subtreeLength;
786 HFBTreeNode *newRetainedValueToInsertIntoParentBranch = add_child_to_node_possibly_creating_split(branch, retainedValueToInsertIntoParentBranch, childIndex + 1, NO);
787 [retainedValueToInsertIntoParentBranch release];
788 retainedValueToInsertIntoParentBranch = newRetainedValueToInsertIntoParentBranch;
789 }
790 }
791 return retainedValueToInsertIntoParentBranch;
792 }
793
794 static BOOL btree_remove(HFBTree *tree, HFBTreeIndex deletionOffset) {
795 struct SubtreeInfo_t descentInfo[MAX_DEPTH];
796 #if ! NDEBUG
797 memset(descentInfo, -1, sizeof descentInfo);
798 #endif
799 HFBTreeIndex subtreeOffset = deletionOffset;
800 HFBTreeLeaf *leaf = btree_descend(tree, descentInfo, &subtreeOffset, YES);
801 ASSERT_IS_LEAF(leaf);
802
803 HFBTreeIndex previousOffsetSum = 0;
804 ChildIndex_t childIndex;
805 for (childIndex = 0; childIndex < BTREE_LEAF_ORDER; childIndex++) {
806 if (previousOffsetSum == subtreeOffset) break;
807 TreeEntry *entry = leaf->children[childIndex];
808 HFASSERT(entry != nil); //if it were nil, then the offset is too large
809 HFBTreeIndex childLength = HFBTreeLength(entry);
810 previousOffsetSum = HFSum(childLength, previousOffsetSum);
811 }
812 HFASSERT(childIndex < BTREE_LEAF_ORDER);
813 HFASSERT(previousOffsetSum == subtreeOffset);
814
815 TreeDepth_t depth = tree->depth;
816 HFASSERT(depth != BAD_DEPTH);
817 BOOL modifiedLeft = NO, modifiedRight = NO;
818 BOOL deleteNode = remove_value_from_node_with_possible_rebalance(leaf, childIndex, depth==0/*isRootNode*/, YES, &modifiedLeft, &modifiedRight);
819 HFASSERT(btree_are_cached_lengths_correct(leaf, NULL));
820 while (depth--) {
821 HFBTreeBranch *branch = descentInfo[depth].branch;
822 ChildIndex_t branchChildIndex = descentInfo[depth].childIndex;
823 BOOL leftNeighborNeedsUpdating = modifiedLeft && branchChildIndex == 0; //if our child tweaked its left neighbor, and its left neighbor is not also a child of us, we need to inform its parent (which is our left neighbor)
824 BOOL rightNeighborNeedsUpdating = modifiedRight && (branchChildIndex + 1 == BTREE_BRANCH_ORDER || branch->children[branchChildIndex + 1] == NULL); //same goes for right
825 if (leftNeighborNeedsUpdating) {
826 HFASSERT(branch->left != NULL);
827 // NSLog(@"Updating lefty %p", branch->left);
828 update_node_having_changed_size_of_child(branch->left, NO);
829 }
830 #if ! NDEBUG
831 if (branch->left) HFASSERT(btree_are_cached_lengths_correct(branch->left, NULL));
832 #endif
833 if (rightNeighborNeedsUpdating) {
834 HFASSERT(branch->right != NULL);
835 // NSLog(@"Updating righty %p", branch->right);
836 update_node_having_changed_size_of_child(branch->right, NO);
837 }
838 #if ! NDEBUG
839 if (branch->right) HFASSERT(btree_are_cached_lengths_correct(branch->right, NULL));
840 #endif
841 update_node_having_changed_size_of_child(branch, NO);
842 modifiedLeft = NO;
843 modifiedRight = NO;
844 if (deleteNode) {
845 deleteNode = remove_value_from_node_with_possible_rebalance(branch, branchChildIndex, depth==0/*isRootNode*/, NO, &modifiedLeft, &modifiedRight);
846 }
847 else {
848 // update_node_having_changed_size_of_child(branch, NO);
849 // no need to delete parent nodes, so leave deleteNode as NO
850 }
851 /* Our parent may have to modify its left or right neighbor if we had to modify our left or right neighbor or if one of our children modified a neighbor that is not also a child of us. */
852 modifiedLeft = modifiedLeft || leftNeighborNeedsUpdating;
853 modifiedRight = modifiedRight || rightNeighborNeedsUpdating;
854 }
855
856 if (! deleteNode) {
857 /* Delete the root if it has one node and a depth of at least 1, or zero nodes and a depth of 0 */
858 deleteNode = (tree->depth >= 1 && tree->root->children[1] == nil) || (tree->depth == 0 && tree->root->children[0] == nil);
859 }
860 return deleteNode;
861 }
862
863 /* linkingHelper stores the last seen node for each depth. */
864 static HFBTreeNode *mutable_copy_node(HFBTreeNode *node, TreeDepth_t depth, HFBTreeNode **linkingHelper) {
865 if (node == nil) return nil;
866 HFASSERT(depth != BAD_DEPTH);
867 Class class = (depth == 0 ? [HFBTreeLeaf class] : [HFBTreeBranch class]);
868 HFBTreeNode *result = [[class alloc] init];
869 result->subtreeLength = node->subtreeLength;
870
871 /* Link us in */
872 HFBTreeNode *leftNeighbor = linkingHelper[0];
873 if (leftNeighbor != nil) {
874 leftNeighbor->right = result;
875 result->left = leftNeighbor;
876 }
877
878 /* Leave us for our future right neighbor to find */
879 linkingHelper[0] = (void *)result;
880
881 HFBTreeIndex index;
882 for (index = 0; index < BTREE_ORDER; index++) {
883 id child = node->children[index];
884 if (! node->children[index]) break;
885 if (depth > 0) {
886 result->children[index] = mutable_copy_node(child, depth - 1, linkingHelper + 1);
887 }
888 else {
889 result->children[index] = [(TreeEntry *)child retain];
890 }
891 }
892 return result;
893 }
894
895 __attribute__((unused))
896 static BOOL non_nulls_are_grouped_at_start(const id *ptr, NSUInteger count) {
897 BOOL hasSeenNull = NO;
898 for (NSUInteger i=0; i < count; i++) {
899 BOOL ptrIsNull = (ptr[i] == nil);
900 hasSeenNull = hasSeenNull || ptrIsNull;
901 if (hasSeenNull && ! ptrIsNull) {
902 return NO;
903 }
904 }
905 return YES;
906 }
907
908
909 static void btree_recursive_check_integrity(HFBTree *tree, HFBTreeNode *branchOrLeaf, TreeDepth_t depth, HFBTreeNode **linkHelper) {
910 HFASSERT(linkHelper[0] == branchOrLeaf->left);
911 if (linkHelper[0]) HFASSERT(linkHelper[0]->right == branchOrLeaf);
912 linkHelper[0] = branchOrLeaf;
913
914 if (depth == 0) {
915 HFBTreeLeaf *leaf = CHECK_CAST(branchOrLeaf, HFBTreeLeaf);
916 HFASSERT(non_nulls_are_grouped_at_start(leaf->children, BTREE_LEAF_ORDER));
917 }
918 else {
919 HFBTreeBranch *branch = CHECK_CAST(branchOrLeaf, HFBTreeBranch);
920 HFASSERT(non_nulls_are_grouped_at_start(branch->children, BTREE_BRANCH_ORDER));
921 for (ChildIndex_t i = 0; i < BTREE_BRANCH_ORDER; i++) {
922 if (! branch->children[i]) break;
923 btree_recursive_check_integrity(tree, branch->children[i], depth - 1, linkHelper + 1);
924 }
925 }
926 ChildIndex_t childCount = count_node_values(branchOrLeaf);
927 if (depth < tree->depth) { // only the root may have fewer than BTREE_NODE_MINIMUM_VALUE_COUNT
928 HFASSERT(childCount >= BTREE_NODE_MINIMUM_VALUE_COUNT);
929 }
930 HFASSERT(childCount <= BTREE_ORDER);
931 }
932
933 static HFBTreeIndex btree_recursive_check_integrity_of_cached_lengths(HFBTreeNode *branchOrLeaf) {
934 HFBTreeIndex result = 0;
935 if (IS_LEAF(branchOrLeaf)) {
936 HFBTreeLeaf *leaf = CHECK_CAST(branchOrLeaf, HFBTreeLeaf);
937 for (ChildIndex_t i = 0; i < BTREE_LEAF_ORDER; i++) {
938 if (! leaf->children[i]) break;
939 result = HFSum(result, HFBTreeLength(leaf->children[i]));
940 }
941 }
942 else {
943 HFBTreeBranch *branch = CHECK_CAST(branchOrLeaf, HFBTreeBranch);
944 for (ChildIndex_t i = 0; i < BTREE_BRANCH_ORDER; i++) {
945 if (branch->children[i]) {
946 HFBTreeIndex subtreeLength = btree_recursive_check_integrity_of_cached_lengths(branch->children[i]);
947 result = HFSum(result, subtreeLength);
948 }
949 }
950 }
951 HFASSERT(result == branchOrLeaf->subtreeLength);
952 return result;
953 }
954
955 static BOOL btree_are_cached_lengths_correct(HFBTreeNode *branchOrLeaf, HFBTreeIndex *outLength) {
956 if (! branchOrLeaf) {
957 if (outLength) *outLength = 0;
958 return YES;
959 }
960 HFBTreeIndex length = 0;
961 if (IS_LEAF(branchOrLeaf)) {
962 HFBTreeLeaf *leaf = CHECK_CAST(branchOrLeaf, HFBTreeLeaf);
963 for (ChildIndex_t i=0; i < BTREE_LEAF_ORDER; i++) {
964 if (! leaf->children[i]) break;
965 length = HFSum(length, HFBTreeLength(leaf->children[i]));
966 }
967 }
968 else {
969 HFBTreeBranch *branch = CHECK_CAST(branchOrLeaf, HFBTreeBranch);
970 for (ChildIndex_t i=0; i < BTREE_BRANCH_ORDER; i++) {
971 if (! branch->children[i]) break;
972 HFBTreeIndex subLength = (HFBTreeIndex)-1;
973 if (! btree_are_cached_lengths_correct(branch->children[i], &subLength)) {
974 return NO;
975 }
976 length = HFSum(length, subLength);
977 }
978 }
979 if (outLength) *outLength = length;
980 return length == branchOrLeaf->subtreeLength;
981 }
982
983 #if FIXUP_LENGTHS
984 static NSUInteger btree_entry_count(HFBTreeNode *branchOrLeaf) {
985 NSUInteger result = 0;
986 if (branchOrLeaf == nil) {
987 // do nothing
988 }
989 else if (IS_LEAF(branchOrLeaf)) {
990 HFBTreeLeaf *leaf = CHECK_CAST(branchOrLeaf, HFBTreeLeaf);
991 for (ChildIndex_t i=0; i < BTREE_LEAF_ORDER; i++) {
992 if (! leaf->children[i]) break;
993 result++;
994 }
995 }
996 else {
997 HFBTreeBranch *branch = CHECK_CAST(branchOrLeaf, HFBTreeBranch);
998 for (ChildIndex_t i=0; i < BTREE_LEAF_ORDER; i++) {
999 if (! branch->children[i]) break;
1000 result += btree_entry_count(branch->children[i]);
1001 }
1002 }
1003 return result;
1004 }
1005
1006 static HFBTreeIndex btree_recursive_fixup_cached_lengths(HFBTree *tree, HFBTreeNode *branchOrLeaf) {
1007 HFBTreeIndex result = 0;
1008 if (IS_LEAF(branchOrLeaf)) {
1009 HFBTreeLeaf *leaf = CHECK_CAST(branchOrLeaf, HFBTreeLeaf);
1010 for (ChildIndex_t i = 0; i < BTREE_LEAF_ORDER; i++) {
1011 if (! leaf->children[i]) break;
1012 result = HFSum(result, HFBTreeLength(leaf->children[i]));
1013 }
1014 }
1015 else {
1016 HFBTreeBranch *branch = CHECK_CAST(branchOrLeaf, HFBTreeBranch);
1017 for (ChildIndex_t i = 0; i < BTREE_BRANCH_ORDER; i++) {
1018 if (! branch->children[i]) break;
1019 btree_recursive_fixup_cached_lengths(tree, branch->children[i]);
1020 result = HFSum(result, CHECK_CAST(branch->children[i], HFBTreeNode)->subtreeLength);
1021 }
1022 }
1023 branchOrLeaf->subtreeLength = result;
1024 return result;
1025 }
1026 #endif
1027
1028 FORCE_STATIC_INLINE void btree_apply_function_to_entries(HFBTree *tree, HFBTreeIndex offset, BOOL (*func)(id, HFBTreeIndex, void *), void *userInfo) {
1029 struct LeafInfo_t leafInfo = btree_find_leaf(tree, offset);
1030 HFBTreeLeaf *leaf = leafInfo.leaf;
1031 ChildIndex_t entryIndex = leafInfo.entryIndex;
1032 HFBTreeIndex leafOffset = leafInfo.offsetOfEntryInTree;
1033 BOOL continueApplying = YES;
1034 while (leaf != NULL) {
1035 for (; entryIndex < BTREE_LEAF_ORDER; entryIndex++) {
1036 TreeEntry *entry = leaf->children[entryIndex];
1037 if (! entry) break;
1038 continueApplying = func(entry, leafOffset, userInfo);
1039 if (! continueApplying) break;
1040 leafOffset = HFSum(leafOffset, HFBTreeLength(entry));
1041 }
1042 if (! continueApplying) break;
1043 leaf = CHECK_CAST_OR_NULL(leaf->right, HFBTreeLeaf);
1044 entryIndex = 0;
1045 }
1046 }
1047
1048 - (NSEnumerator *)entryEnumerator {
1049 if (! root) return [@[] objectEnumerator];
1050 HFBTreeLeaf *leaf = btree_find_leaf(self, 0).leaf;
1051 return [[[HFBTreeEnumerator alloc] initWithLeaf:leaf] autorelease];
1052 }
1053
1054
1055 static BOOL add_to_array(id entry, HFBTreeIndex offset __attribute__((unused)), void *array) {
1056 [(id)array addObject:entry];
1057 return YES;
1058 }
1059
1060 - (NSArray *)allEntries {
1061 if (! root) return @[];
1062 NSUInteger treeCapacity = 1;
1063 unsigned int depthIndex = depth;
1064 while (depthIndex--) treeCapacity *= BTREE_ORDER;
1065 NSMutableArray *result = [NSMutableArray arrayWithCapacity: treeCapacity/2]; //assume we're half full
1066 btree_apply_function_to_entries(self, 0, add_to_array, result);
1067 return result;
1068 }
1069
1070 - (void)applyFunction:(BOOL (*)(id entry, HFBTreeIndex offset, void *userInfo))func toEntriesStartingAtOffset:(HFBTreeIndex)offset withUserInfo:(void *)userInfo {
1071 NSParameterAssert(func != NULL);
1072 if (! root) return;
1073 btree_apply_function_to_entries(self, offset, func, userInfo);
1074 }
1075
1076 @end
1077
1078
1079 @implementation HFBTreeEnumerator
1080
1081 - (instancetype)initWithLeaf:(HFBTreeLeaf *)leaf {
1082 NSParameterAssert(leaf != nil);
1083 ASSERT_IS_LEAF(leaf);
1084 currentLeaf = leaf;
1085 return self;
1086 }
1087
1088 - (id)nextObject {
1089 if (! currentLeaf) return nil;
1090 if (childIndex >= BTREE_LEAF_ORDER || currentLeaf->children[childIndex] == nil) {
1091 childIndex = 0;
1092 currentLeaf = CHECK_CAST_OR_NULL(currentLeaf->right, HFBTreeLeaf);
1093 }
1094 if (currentLeaf == nil) return nil;
1095 HFASSERT(currentLeaf->children[childIndex] != nil);
1096 return currentLeaf->children[childIndex++];
1097 }
1098
1099 @end
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.