FServerValues.m 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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. @interface FServerValues ()
  38. + (id)resolveScalarServerOp:(NSString *)op
  39. withServerValues:(NSDictionary *)serverValues;
  40. + (id)resolveComplexServerOp:(NSDictionary *)op
  41. withExisting:(id<FNode>)existing
  42. serverValues:(NSDictionary *)serverValues;
  43. @end
  44. @implementation FServerValues
  45. + (NSDictionary *)generateServerValues:(id<FClock>)clock {
  46. long long millis = (long long)([clock currentTime] * 1000);
  47. return @{kTimestamp : [NSNumber numberWithLongLong:millis]};
  48. }
  49. + (id)resolveDeferredValue:(id)val
  50. withExisting:(id<FNode>)existing
  51. serverValues:(NSDictionary *)serverValues {
  52. if (![val isKindOfClass:[NSDictionary class]]) {
  53. return val;
  54. }
  55. NSDictionary *dict = val;
  56. id op = dict[kServerValueSubKey];
  57. if (op == nil) {
  58. return val;
  59. } else if ([op isKindOfClass:NSString.class]) {
  60. return [FServerValues resolveScalarServerOp:op
  61. withServerValues:serverValues];
  62. } else if ([op isKindOfClass:NSDictionary.class]) {
  63. return [FServerValues resolveComplexServerOp:op
  64. withExisting:existing
  65. serverValues:serverValues];
  66. }
  67. return val;
  68. }
  69. + (id)resolveScalarServerOp:(NSString *)op
  70. withServerValues:(NSDictionary *)serverValues {
  71. return serverValues[op];
  72. }
  73. + (id)resolveComplexServerOp:(NSDictionary *)op
  74. withExisting:(id<FNode>)existing
  75. serverValues:(NSDictionary *)serverValues {
  76. // Only increment is supported as of now
  77. if (op[kIncrement] == nil) {
  78. return nil;
  79. }
  80. // Incrementing a non-number sets the value to the incremented amount
  81. NSNumber *delta = op[kIncrement];
  82. if (![existing isLeafNode]) {
  83. return delta;
  84. }
  85. FLeafNode *existingLeaf = existing;
  86. if (![existingLeaf.value isKindOfClass:NSNumber.class]) {
  87. return delta;
  88. }
  89. NSNumber *existingNum = existingLeaf.value;
  90. BOOL incrLong = canBeRepresentedAsLong(delta);
  91. BOOL baseLong = canBeRepresentedAsLong(existingNum);
  92. if (incrLong && baseLong) {
  93. long x = delta.longValue;
  94. long y = existingNum.longValue;
  95. long r = x + y;
  96. // See "Hacker's Delight" 2-12: Overflow if both arguments have the
  97. // opposite sign of the result
  98. if (((x ^ r) & (y ^ r)) >= 0) {
  99. return @(r);
  100. }
  101. }
  102. return @(delta.doubleValue + existingNum.doubleValue);
  103. }
  104. + (FCompoundWrite *)resolveDeferredValueCompoundWrite:(FCompoundWrite *)write
  105. withExisting:(id<FNode>)existing
  106. serverValues:
  107. (NSDictionary *)serverValues {
  108. __block FCompoundWrite *resolved = write;
  109. [write enumerateWrites:^(FPath *path, id<FNode> node, BOOL *stop) {
  110. id<FNode> resolvedNode =
  111. [FServerValues resolveDeferredValueSnapshot:node
  112. withExisting:existing
  113. serverValues:serverValues];
  114. // Node actually changed, use pointer inequality here
  115. if (resolvedNode != node) {
  116. resolved = [resolved addWrite:resolvedNode atPath:path];
  117. }
  118. }];
  119. return resolved;
  120. }
  121. + (id)resolveDeferredValueTree:(FSparseSnapshotTree *)tree
  122. withExisting:(id<FNode>)existing
  123. serverValues:(NSDictionary *)serverValues {
  124. FSparseSnapshotTree *resolvedTree = [[FSparseSnapshotTree alloc] init];
  125. [tree
  126. forEachTreeAtPath:[FPath empty]
  127. do:^(FPath *path, id<FNode> node) {
  128. [resolvedTree
  129. rememberData:
  130. [FServerValues
  131. resolveDeferredValueSnapshot:node
  132. withExisting:
  133. [existing
  134. getChild:path]
  135. serverValues:serverValues]
  136. onPath:path];
  137. }];
  138. return resolvedTree;
  139. }
  140. + (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
  141. withExisting:(id<FNode>)existing
  142. serverValues:(NSDictionary *)serverValues {
  143. id priorityVal =
  144. [FServerValues resolveDeferredValue:[[node getPriority] val]
  145. withExisting:existing.getPriority
  146. serverValues:serverValues];
  147. id<FNode> priority = [FSnapshotUtilities nodeFrom:priorityVal];
  148. if ([node isLeafNode]) {
  149. id value = [self resolveDeferredValue:[node val]
  150. withExisting:existing
  151. serverValues:serverValues];
  152. if (![value isEqual:[node val]] ||
  153. ![priority isEqual:[node getPriority]]) {
  154. return [[FLeafNode alloc] initWithValue:value
  155. withPriority:priority];
  156. } else {
  157. return node;
  158. }
  159. } else {
  160. __block FChildrenNode *newNode = node;
  161. if (![priority isEqual:[node getPriority]]) {
  162. newNode = [newNode updatePriority:priority];
  163. }
  164. [node enumerateChildrenUsingBlock:^(NSString *childKey,
  165. id<FNode> childNode, BOOL *stop) {
  166. id newChildNode = [FServerValues
  167. resolveDeferredValueSnapshot:childNode
  168. withExisting:[existing getImmediateChild:childKey]
  169. serverValues:serverValues];
  170. if (![newChildNode isEqual:childNode]) {
  171. newNode = [newNode updateImmediateChild:childKey
  172. withNewChild:newChildNode];
  173. }
  174. }];
  175. return newNode;
  176. }
  177. }
  178. @end