FServerValues.m 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "FServerValues.h"
  17. #import "FChildrenNode.h"
  18. #import "FConstants.h"
  19. #import "FLeafNode.h"
  20. #import "FSnapshotUtilities.h"
  21. const NSString *kTimestamp = @"timestamp";
  22. const NSString *kIncrement = @"increment";
  23. BOOL canBeRepresentedAsLong(NSNumber *num) {
  24. switch (num.objCType[0]) {
  25. case 'f': // float; fallthrough
  26. case 'd': // double
  27. return NO;
  28. case 'L': // unsigned long; fallthrough
  29. case 'Q': // unsigned long long; fallthrough
  30. // Only use ulong(long) if there isn't an overflow.
  31. if (num.unsignedLongLongValue > LONG_MAX) {
  32. return NO;
  33. }
  34. }
  35. return YES;
  36. }
  37. // Running through CompoundWrites for all update paths has been shown to
  38. // be a 20% pessimization in microbenchmarks. This is because it slows
  39. // down by O(N) of the write queue length. To eliminate the performance
  40. // hit, we wrap around existing data of either snapshot or CompoundWrite
  41. // (allowing us to share code) and read from the CompoundWrite only when/where
  42. // we need to calculate an incremented value's prior state.
  43. @protocol ValueProvider <NSObject>
  44. - (id<ValueProvider>)getChild:(NSString *)pathSegment;
  45. - (id<FNode>)value;
  46. @end
  47. @interface DeferredValueProvider : NSObject <ValueProvider>
  48. - (instancetype)initWithSyncTree:(FSyncTree *)tree atPath:(FPath *)path;
  49. - (id<ValueProvider>)getChild:(NSString *)pathSegment;
  50. - (id<FNode>)value;
  51. @property FPath *path;
  52. @property FSyncTree *tree;
  53. @end
  54. @interface ExistingValueProvider : NSObject <ValueProvider>
  55. - (instancetype)initWithSnapshot:(id<FNode>)snapshot;
  56. - (id<ValueProvider>)getChild:(NSString *)pathSegment;
  57. - (id<FNode>)value;
  58. @property id<FNode> snapshot;
  59. @end
  60. @implementation DeferredValueProvider
  61. - (instancetype)initWithSyncTree:(FSyncTree *)tree atPath:(FPath *)path {
  62. self.tree = tree;
  63. self.path = path;
  64. return self;
  65. }
  66. - (id<ValueProvider>)getChild:(NSString *)pathSegment {
  67. FPath *child = [self.path childFromString:pathSegment];
  68. return [[DeferredValueProvider alloc] initWithSyncTree:self.tree
  69. atPath:child];
  70. }
  71. - (id<FNode>)value {
  72. return [self.tree calcCompleteEventCacheAtPath:self.path
  73. excludeWriteIds:@[]];
  74. }
  75. @end
  76. @implementation ExistingValueProvider
  77. - (instancetype)initWithSnapshot:(id<FNode>)snapshot {
  78. self.snapshot = snapshot;
  79. return self;
  80. }
  81. - (id<ValueProvider>)getChild:(NSString *)pathSegment {
  82. return [[ExistingValueProvider alloc]
  83. initWithSnapshot:[self.snapshot getImmediateChild:pathSegment]];
  84. }
  85. - (id<FNode>)value {
  86. return self.snapshot.val;
  87. }
  88. @end
  89. @interface FServerValues ()
  90. + (id)resolveScalarServerOp:(NSString *)op
  91. withServerValues:(NSDictionary *)serverValues;
  92. + (id)resolveComplexServerOp:(NSDictionary *)op
  93. withExisting:(id<ValueProvider>)existing
  94. serverValues:(NSDictionary *)serverValues;
  95. + (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
  96. withJITExisting:(id<ValueProvider>)existing
  97. serverValues:(NSDictionary *)serverValues;
  98. @end
  99. @implementation FServerValues
  100. + (NSDictionary *)generateServerValues:(id<FClock>)clock {
  101. long long millis = (long long)([clock currentTime] * 1000);
  102. return @{kTimestamp : [NSNumber numberWithLongLong:millis]};
  103. }
  104. + (id)resolveDeferredValue:(id)val
  105. withExisting:(id<ValueProvider>)existing
  106. serverValues:(NSDictionary *)serverValues {
  107. if (![val isKindOfClass:[NSDictionary class]]) {
  108. return val;
  109. }
  110. NSDictionary *dict = val;
  111. id op = dict[kServerValueSubKey];
  112. if (op == nil) {
  113. return val;
  114. } else if ([op isKindOfClass:NSString.class]) {
  115. return [FServerValues resolveScalarServerOp:op
  116. withServerValues:serverValues];
  117. } else if ([op isKindOfClass:NSDictionary.class]) {
  118. return [FServerValues resolveComplexServerOp:op
  119. withExisting:existing
  120. serverValues:serverValues];
  121. }
  122. return val;
  123. }
  124. + (id)resolveScalarServerOp:(NSString *)op
  125. withServerValues:(NSDictionary *)serverValues {
  126. return serverValues[op];
  127. }
  128. + (id)resolveComplexServerOp:(NSDictionary *)op
  129. withExisting:(id<ValueProvider>)jitExisting
  130. serverValues:(NSDictionary *)serverValues {
  131. // Only increment is supported as of now
  132. if (op[kIncrement] == nil) {
  133. return nil;
  134. }
  135. // Incrementing a non-number sets the value to the incremented amount
  136. NSNumber *delta = op[kIncrement];
  137. id<FNode> existing = jitExisting.value;
  138. if (![existing isLeafNode]) {
  139. return delta;
  140. }
  141. FLeafNode *existingLeaf = existing;
  142. if (![existingLeaf.value isKindOfClass:NSNumber.class]) {
  143. return delta;
  144. }
  145. NSNumber *existingNum = existingLeaf.value;
  146. BOOL incrLong = canBeRepresentedAsLong(delta);
  147. BOOL baseLong = canBeRepresentedAsLong(existingNum);
  148. if (incrLong && baseLong) {
  149. long x = delta.longValue;
  150. long y = existingNum.longValue;
  151. long r = x + y;
  152. // See "Hacker's Delight" 2-12: Overflow if both arguments have the
  153. // opposite sign of the result
  154. if (((x ^ r) & (y ^ r)) >= 0) {
  155. return @(r);
  156. }
  157. }
  158. return @(delta.doubleValue + existingNum.doubleValue);
  159. }
  160. + (FCompoundWrite *)resolveDeferredValueCompoundWrite:(FCompoundWrite *)write
  161. withSyncTree:(FSyncTree *)tree
  162. atPath:(FPath *)path
  163. serverValues:
  164. (NSDictionary *)serverValues {
  165. __block FCompoundWrite *resolved = write;
  166. [write enumerateWrites:^(FPath *subPath, id<FNode> node, BOOL *stop) {
  167. id<ValueProvider> existing =
  168. [[DeferredValueProvider alloc] initWithSyncTree:tree
  169. atPath:[path child:subPath]];
  170. id<FNode> resolvedNode =
  171. [FServerValues resolveDeferredValueSnapshot:node
  172. withJITExisting:existing
  173. serverValues:serverValues];
  174. // Node actually changed, use pointer inequality here
  175. if (resolvedNode != node) {
  176. resolved = [resolved addWrite:resolvedNode atPath:path];
  177. }
  178. }];
  179. return resolved;
  180. }
  181. + (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
  182. withExisting:(FSyncTree *)tree
  183. atPath:(FPath *)path
  184. serverValues:(NSDictionary *)serverValues {
  185. id<ValueProvider> jitExisting =
  186. [[DeferredValueProvider alloc] initWithSyncTree:tree atPath:path];
  187. return [FServerValues resolveDeferredValueSnapshot:node
  188. withJITExisting:jitExisting
  189. serverValues:serverValues];
  190. }
  191. + (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
  192. withExisting:(id<FNode>)existing
  193. serverValues:(NSDictionary *)serverValues {
  194. id<ValueProvider> jitExisting =
  195. [[ExistingValueProvider alloc] initWithSnapshot:existing];
  196. return [FServerValues resolveDeferredValueSnapshot:node
  197. withJITExisting:jitExisting
  198. serverValues:serverValues];
  199. }
  200. + (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
  201. withJITExisting:(id<ValueProvider>)existing
  202. serverValues:(NSDictionary *)serverValues {
  203. id priorityVal =
  204. [FServerValues resolveDeferredValue:[[node getPriority] val]
  205. withExisting:[existing getChild:@".priority"]
  206. serverValues:serverValues];
  207. id<FNode> priority = [FSnapshotUtilities nodeFrom:priorityVal];
  208. if ([node isLeafNode]) {
  209. id value = [self resolveDeferredValue:[node val]
  210. withExisting:existing
  211. serverValues:serverValues];
  212. if (![value isEqual:[node val]] ||
  213. ![priority isEqual:[node getPriority]]) {
  214. return [[FLeafNode alloc] initWithValue:value
  215. withPriority:priority];
  216. } else {
  217. return node;
  218. }
  219. } else {
  220. __block FChildrenNode *newNode = node;
  221. if (![priority isEqual:[node getPriority]]) {
  222. newNode = [newNode updatePriority:priority];
  223. }
  224. [node enumerateChildrenUsingBlock:^(NSString *childKey,
  225. id<FNode> childNode, BOOL *stop) {
  226. id newChildNode = [FServerValues
  227. resolveDeferredValueSnapshot:childNode
  228. withJITExisting:[existing getChild:childKey]
  229. serverValues:serverValues];
  230. if (![newChildNode isEqual:childNode]) {
  231. newNode = [newNode updateImmediateChild:childKey
  232. withNewChild:newChildNode];
  233. }
  234. }];
  235. return newNode;
  236. }
  237. }
  238. @end