aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore/third_party/Immutable/FSTArraySortedDictionary.m
blob: 985b514094566fe702be9324b01467d2b4db02bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#import "Firestore/third_party/Immutable/FSTArraySortedDictionary.h"

#import "Firestore/third_party/Immutable/FSTArraySortedDictionaryEnumerator.h"
#import "Firestore/third_party/Immutable/FSTTreeSortedDictionary.h"

NS_ASSUME_NONNULL_BEGIN

@interface FSTArraySortedDictionary ()
@property(nonatomic, copy, readwrite) NSComparator comparator;
@property(nonatomic, strong) NSArray<id> *keys;
@property(nonatomic, strong) NSArray<id> *values;
@end

@implementation FSTArraySortedDictionary

+ (FSTArraySortedDictionary *)dictionaryWithDictionary:(NSDictionary *)dictionary
                                            comparator:(NSComparator)comparator {
  NSMutableArray *keys = [NSMutableArray arrayWithCapacity:dictionary.count];
  [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    [keys addObject:key];
  }];
  [keys sortUsingComparator:comparator];

  [keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    if (idx > 0) {
      if (comparator(keys[idx - 1], obj) != NSOrderedAscending) {
        [NSException raise:NSInvalidArgumentException
                    format:
                        @"Can't create FSTImmutableSortedDictionary with keys "
                        @"with same ordering!"];
      }
    }
  }];

  NSMutableArray *values = [NSMutableArray arrayWithCapacity:keys.count];
  NSInteger pos = 0;
  for (id key in keys) {
    values[pos++] = dictionary[key];
  }
  NSAssert(values.count == keys.count, @"We added as many keys as values");
  return [[FSTArraySortedDictionary alloc] initWithComparator:comparator keys:keys values:values];
}

- (id)initWithComparator:(NSComparator)comparator {
  return [self initWithComparator:comparator keys:[NSArray array] values:[NSArray array]];
}

// Designated initializer.
- (id)initWithComparator:(NSComparator)comparator keys:(NSArray *)keys values:(NSArray *)values {
  self = [super init];
  if (self != nil) {
    NSAssert(keys.count == values.count, @"keys and values must have the same count");
    _comparator = comparator;
    _keys = keys;
    _values = values;
  }
  return self;
}

/** Returns the index of the first position where array[position] >= key.  */
- (int)findInsertPositionForKey:(id)key {
  int newPos = 0;
  while (newPos < self.keys.count && self.comparator(self.keys[newPos], key) < NSOrderedSame) {
    newPos++;
  }
  return newPos;
}

- (NSInteger)findKey:(id)key {
  if (key == nil) {
    return NSNotFound;
  }
  for (NSInteger pos = 0; pos < self.keys.count; pos++) {
    NSComparisonResult result = self.comparator(key, self.keys[pos]);
    if (result == NSOrderedSame) {
      return pos;
    } else if (result == NSOrderedAscending) {
      return NSNotFound;
    }
  }
  return NSNotFound;
}

- (FSTImmutableSortedDictionary *)dictionaryBySettingObject:(id)value forKey:(id)key {
  NSInteger pos = [self findKey:key];

  if (pos == NSNotFound) {
    /*
     * If we're above the threshold we want to convert it to a tree backed implementation to not
     * have degrading performance
     */
    if (self.count >= kSortedDictionaryArrayToRBTreeSizeThreshold) {
      NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:self.count];
      for (NSInteger i = 0; i < self.keys.count; i++) {
        dict[self.keys[i]] = self.values[i];
      }
      dict[key] = value;
      return [FSTTreeSortedDictionary dictionaryWithDictionary:dict comparator:self.comparator];
    } else {
      NSMutableArray *newKeys = [NSMutableArray arrayWithArray:self.keys];
      NSMutableArray *newValues = [NSMutableArray arrayWithArray:self.values];
      NSInteger newPos = [self findInsertPositionForKey:key];
      [newKeys insertObject:key atIndex:newPos];
      [newValues insertObject:value atIndex:newPos];
      return [[FSTArraySortedDictionary alloc] initWithComparator:self.comparator
                                                             keys:newKeys
                                                           values:newValues];
    }
  } else {
    NSMutableArray *newKeys = [NSMutableArray arrayWithArray:self.keys];
    NSMutableArray *newValues = [NSMutableArray arrayWithArray:self.values];
    newKeys[pos] = key;
    newValues[pos] = value;
    return [[FSTArraySortedDictionary alloc] initWithComparator:self.comparator
                                                           keys:newKeys
                                                         values:newValues];
  }
}

- (FSTImmutableSortedDictionary *)dictionaryByRemovingObjectForKey:(id)key {
  NSInteger pos = [self findKey:key];
  if (pos == NSNotFound) {
    return self;
  } else {
    NSMutableArray *newKeys = [NSMutableArray arrayWithArray:self.keys];
    NSMutableArray *newValues = [NSMutableArray arrayWithArray:self.values];
    [newKeys removeObjectAtIndex:pos];
    [newValues removeObjectAtIndex:pos];
    return [[FSTArraySortedDictionary alloc] initWithComparator:self.comparator
                                                           keys:newKeys
                                                         values:newValues];
  }
}

- (nullable id)objectForKey:(id)key {
  NSInteger pos = [self findKey:key];
  if (pos == NSNotFound) {
    return nil;
  } else {
    return self.values[pos];
  }
}

- (NSUInteger)indexOfKey:(id)key {
  return [self findKey:key];
}

- (BOOL)isEmpty {
  return self.keys.count == 0;
}

- (NSUInteger)count {
  return self.keys.count;
}

- (id)minKey {
  return [self.keys firstObject];
}

- (id)maxKey {
  return [self.keys lastObject];
}

- (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id, id, BOOL *))block {
  [self enumerateKeysAndObjectsReverse:NO usingBlock:block];
}

- (void)enumerateKeysAndObjectsReverse:(BOOL)reverse usingBlock:(void (^)(id, id, BOOL *))block {
  if (reverse) {
    BOOL stop = NO;
    for (NSInteger i = self.keys.count - 1; i >= 0; i--) {
      block(self.keys[i], self.values[i], &stop);
      if (stop) return;
    }
  } else {
    BOOL stop = NO;
    for (NSInteger i = 0; i < self.keys.count; i++) {
      block(self.keys[i], self.values[i], &stop);
      if (stop) return;
    }
  }
}

- (BOOL)containsKey:(id)key {
  return [self findKey:key] != NSNotFound;
}

- (NSEnumerator *)keyEnumerator {
  return [self.keys objectEnumerator];
}

- (NSEnumerator *)keyEnumeratorFrom:(id)startKey {
  return [self keyEnumeratorFrom:startKey to:nil];
}

- (NSEnumerator *)keyEnumeratorFrom:(id)startKey to:(nullable id)endKey {
  int start = [self findInsertPositionForKey:startKey];
  int end = (int)self.count;
  if (endKey) {
    end = [self findInsertPositionForKey:endKey];
  }
  return [[FSTArraySortedDictionaryEnumerator alloc] initWithKeys:self.keys
                                                         startPos:start
                                                           endPos:end
                                                        isReverse:NO];
}

- (NSEnumerator *)reverseKeyEnumerator {
  return [self.keys reverseObjectEnumerator];
}

- (NSEnumerator *)reverseKeyEnumeratorFrom:(id)startKey {
  int startPos = [self findInsertPositionForKey:startKey];
  // if there's no exact match, findKeyOrInsertPosition will return the index *after* the closest
  // match, but since this is a reverse iterator, we want to start just *before* the closest match.
  if (startPos >= self.keys.count ||
      self.comparator(self.keys[startPos], startKey) != NSOrderedSame) {
    startPos -= 1;
  }
  return [[FSTArraySortedDictionaryEnumerator alloc] initWithKeys:self.keys
                                                         startPos:startPos
                                                           endPos:-1
                                                        isReverse:YES];
}

@end

NS_ASSUME_NONNULL_END