Files
firebase-ios-sdk/Firestore/third_party/Immutable/FSTLLRBValueNode.m
T
Gil 4fbc96904f Use fully qualified imports in Firestore (#467)
This simplifies the import process back into google3 and allows us to add additional directories to the project without needing to update the project files for each directory we add. This shows up most clearly in the change to Firestore/Example/Firestore.xcodeproj/project.pbxproj: this no longer needs to list essentially every directory in the project as a header search path.

* Clang-format configuration for C++ files
* Add support C++-only sources to the podspec
* Podspec support for fully qualified imports
* xcodeproj changes for fully-qualified imports
* Use fully-qualified imports in Firestore sources
2017-11-16 20:52:01 -08:00

309 lines
8.9 KiB
Objective-C

#import "Firestore/third_party/Immutable/FSTLLRBValueNode.h"
#import "Firestore/Source/Util/FSTAssert.h"
#import "Firestore/third_party/Immutable/FSTLLRBEmptyNode.h"
NS_ASSUME_NONNULL_BEGIN
@interface FSTLLRBValueNode ()
@property(nonatomic, assign) FSTLLRBColor color;
@property(nonatomic, assign) NSUInteger count;
@property(nonatomic, strong) id key;
@property(nonatomic, strong) id value;
@property(nonatomic, strong) id<FSTLLRBNode> right;
@end
@implementation FSTLLRBValueNode
- (NSString *)colorDescription {
NSString *color = @"unspecified";
if (self.color == FSTLLRBColorRed) {
color = @"red";
} else if (self.color == FSTLLRBColorBlack) {
color = @"black";
}
return color;
}
- (NSString *)description {
NSString *color = self.colorDescription;
return [NSString stringWithFormat:@"[key=%@ val=%@ color=%@]", self.key, self.value, color];
}
// Designated initializer.
- (instancetype)initWithKey:(id _Nullable)aKey
withValue:(id _Nullable)aValue
withColor:(FSTLLRBColor)aColor
withLeft:(id<FSTLLRBNode> _Nullable)aLeft
withRight:(id<FSTLLRBNode> _Nullable)aRight {
self = [super init];
if (self) {
_key = aKey;
_value = aValue;
_color = aColor != FSTLLRBColorUnspecified ? aColor : FSTLLRBColorRed;
_left = aLeft != nil ? aLeft : [FSTLLRBEmptyNode emptyNode];
_right = aRight != nil ? aRight : [FSTLLRBEmptyNode emptyNode];
_count = NSNotFound;
}
return self;
}
- (instancetype)copyWith:(id _Nullable)aKey
withValue:(id _Nullable)aValue
withColor:(FSTLLRBColor)aColor
withLeft:(id<FSTLLRBNode> _Nullable)aLeft
withRight:(id<FSTLLRBNode> _Nullable)aRight {
return [[FSTLLRBValueNode alloc]
initWithKey:(aKey != nil) ? aKey : self.key
withValue:(aValue != nil) ? aValue : self.value
withColor:(aColor != FSTLLRBColorUnspecified) ? aColor : self.color
withLeft:(aLeft != nil) ? aLeft : self.left
withRight:(aRight != nil) ? aRight : self.right];
}
- (void)setLeft:(nullable id<FSTLLRBNode>)left {
// Setting the left node should be only done by the builder, so doing it after someone has
// memoized count is an error.
FSTAssert(_count == NSNotFound, @"Can't update left node after using count");
_left = left;
}
- (NSUInteger)count {
if (_count == NSNotFound) {
_count = _left.count + 1 + _right.count;
}
return _count;
}
- (BOOL)isEmpty {
return NO;
}
/**
* Early terminates if action returns YES.
*
* @return The first truthy value returned by action, or the last falsey value returned by action.
*/
- (BOOL)inorderTraversal:(BOOL (^)(id key, id value))action {
return [self.left inorderTraversal:action] || action(self.key, self.value) ||
[self.right inorderTraversal:action];
}
- (BOOL)reverseTraversal:(BOOL (^)(id key, id value))action {
return [self.right reverseTraversal:action] || action(self.key, self.value) ||
[self.left reverseTraversal:action];
}
- (id<FSTLLRBNode>)min {
if ([self.left isEmpty]) {
return self;
} else {
return [self.left min];
}
}
- (nullable id)minKey {
return [[self min] key];
}
- (nullable id)maxKey {
if ([self.right isEmpty]) {
return self.key;
} else {
return [self.right maxKey];
}
}
- (id<FSTLLRBNode>)insertKey:(id)aKey forValue:(id)aValue withComparator:(NSComparator)aComparator {
NSComparisonResult cmp = aComparator(aKey, self.key);
FSTLLRBValueNode *n = self;
if (cmp == NSOrderedAscending) {
n = [n copyWith:nil
withValue:nil
withColor:FSTLLRBColorUnspecified
withLeft:[n.left insertKey:aKey forValue:aValue withComparator:aComparator]
withRight:nil];
} else if (cmp == NSOrderedSame) {
n = [n copyWith:nil
withValue:aValue
withColor:FSTLLRBColorUnspecified
withLeft:nil
withRight:nil];
} else {
n = [n copyWith:nil
withValue:nil
withColor:FSTLLRBColorUnspecified
withLeft:nil
withRight:[n.right insertKey:aKey forValue:aValue withComparator:aComparator]];
}
return [n fixUp];
}
- (id<FSTLLRBNode>)removeMin {
if ([self.left isEmpty]) {
return [FSTLLRBEmptyNode emptyNode];
}
FSTLLRBValueNode *n = self;
if (![n.left isRed] && ![n.left.left isRed]) {
n = [n moveRedLeft];
}
n = [n copyWith:nil
withValue:nil
withColor:FSTLLRBColorUnspecified
withLeft:[(FSTLLRBValueNode *)n.left removeMin]
withRight:nil];
return [n fixUp];
}
- (id<FSTLLRBNode>)fixUp {
FSTLLRBValueNode *n = self;
if ([n.right isRed] && ![n.left isRed]) n = [n rotateLeft];
if ([n.left isRed] && [n.left.left isRed]) n = [n rotateRight];
if ([n.left isRed] && [n.right isRed]) n = [n colorFlip];
return n;
}
- (FSTLLRBValueNode *)moveRedLeft {
FSTLLRBValueNode *n = [self colorFlip];
if ([n.right.left isRed]) {
n = [n copyWith:nil
withValue:nil
withColor:FSTLLRBColorUnspecified
withLeft:nil
withRight:[(FSTLLRBValueNode *)n.right rotateRight]];
n = [n rotateLeft];
n = [n colorFlip];
}
return n;
}
- (FSTLLRBValueNode *)moveRedRight {
FSTLLRBValueNode *n = [self colorFlip];
if ([n.left.left isRed]) {
n = [n rotateRight];
n = [n colorFlip];
}
return n;
}
- (id<FSTLLRBNode>)rotateLeft {
id<FSTLLRBNode> nl = [self copyWith:nil
withValue:nil
withColor:FSTLLRBColorRed
withLeft:nil
withRight:self.right.left];
return [self.right copyWith:nil withValue:nil withColor:self.color withLeft:nl withRight:nil];
}
- (id<FSTLLRBNode>)rotateRight {
id<FSTLLRBNode> nr = [self copyWith:nil
withValue:nil
withColor:FSTLLRBColorRed
withLeft:self.left.right
withRight:nil];
return [self.left copyWith:nil withValue:nil withColor:self.color withLeft:nil withRight:nr];
}
- (id<FSTLLRBNode>)colorFlip {
FSTLLRBColor color = self.color == FSTLLRBColorBlack ? FSTLLRBColorRed : FSTLLRBColorBlack;
FSTLLRBColor leftColor =
self.left.color == FSTLLRBColorBlack ? FSTLLRBColorRed : FSTLLRBColorBlack;
FSTLLRBColor rightColor =
self.right.color == FSTLLRBColorBlack ? FSTLLRBColorRed : FSTLLRBColorBlack;
id<FSTLLRBNode> nleft =
[self.left copyWith:nil withValue:nil withColor:leftColor withLeft:nil withRight:nil];
id<FSTLLRBNode> nright =
[self.right copyWith:nil withValue:nil withColor:rightColor withLeft:nil withRight:nil];
return [self copyWith:nil withValue:nil withColor:color withLeft:nleft withRight:nright];
}
- (id<FSTLLRBNode>)remove:(id)aKey withComparator:(NSComparator)comparator {
id<FSTLLRBNode> smallest;
FSTLLRBValueNode *n = self;
if (comparator(aKey, n.key) == NSOrderedAscending) {
if (![n.left isEmpty] && ![n.left isRed] && ![n.left.left isRed]) {
n = [n moveRedLeft];
}
n = [n copyWith:nil
withValue:nil
withColor:FSTLLRBColorUnspecified
withLeft:[n.left remove:aKey withComparator:comparator]
withRight:nil];
} else {
if ([n.left isRed]) {
n = [n rotateRight];
}
if (![n.right isEmpty] && ![n.right isRed] && ![n.right.left isRed]) {
n = [n moveRedRight];
}
if (comparator(aKey, n.key) == NSOrderedSame) {
if ([n.right isEmpty]) {
return [FSTLLRBEmptyNode emptyNode];
} else {
smallest = [n.right min];
n = [n copyWith:smallest.key
withValue:smallest.value
withColor:FSTLLRBColorUnspecified
withLeft:nil
withRight:[(FSTLLRBValueNode *)n.right removeMin]];
}
}
n = [n copyWith:nil
withValue:nil
withColor:FSTLLRBColorUnspecified
withLeft:nil
withRight:[n.right remove:aKey withComparator:comparator]];
}
return [n fixUp];
}
- (BOOL)isRed {
return self.color == FSTLLRBColorRed;
}
- (BOOL)checkMaxDepth {
int blackDepth = [self check];
if (pow(2.0, blackDepth) <= ([self count] + 1)) {
return YES;
} else {
return NO;
}
}
- (int)check {
int blackDepth = 0;
if ([self isRed] && [self.left isRed]) {
@throw
[[NSException alloc] initWithName:@"check" reason:@"Red node has a red child" userInfo:nil];
}
if ([self.right isRed]) {
@throw [[NSException alloc] initWithName:@"check" reason:@"Right child is red" userInfo:nil];
}
blackDepth = [self.left check];
if (blackDepth != [self.right check]) {
NSString *err =
[NSString stringWithFormat:@"(%@ -> %@)blackDepth: %d ; self.right check: %d", self.value,
self.colorDescription, blackDepth, [self.right check]];
@throw [[NSException alloc] initWithName:@"check" reason:err userInfo:nil];
} else {
int ret = blackDepth + ([self isRed] ? 0 : 1);
return ret;
}
}
@end
NS_ASSUME_NONNULL_END