FSTUserDataReader.mm 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  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 <FirebaseCore/FIRTimestamp.h>
  17. #include <memory>
  18. #include <set>
  19. #include <string>
  20. #include <utility>
  21. #include <vector>
  22. #import "Firestore/Source/API/FSTUserDataReader.h"
  23. #import "FIRBSONBinaryData.h"
  24. #import "FIRBSONObjectId.h"
  25. #import "FIRBSONTimestamp.h"
  26. #import "FIRDecimal128Value.h"
  27. #import "FIRGeoPoint.h"
  28. #import "FIRInt32Value.h"
  29. #import "FIRMaxKey.h"
  30. #import "FIRMinKey.h"
  31. #import "FIRRegexValue.h"
  32. #import "FIRVectorValue.h"
  33. #import "Firestore/Source/API/FIRDocumentReference+Internal.h"
  34. #import "Firestore/Source/API/FIRFieldPath+Internal.h"
  35. #import "Firestore/Source/API/FIRFieldValue+Internal.h"
  36. #import "Firestore/Source/API/FIRFirestore+Internal.h"
  37. #import "Firestore/Source/API/FIRGeoPoint+Internal.h"
  38. #import "Firestore/Source/API/converters.h"
  39. #import "Firestore/core/include/firebase/firestore/geo_point.h"
  40. #include "Firestore/core/src/core/user_data.h"
  41. #include "Firestore/core/src/model/database_id.h"
  42. #include "Firestore/core/src/model/document_key.h"
  43. #include "Firestore/core/src/model/field_mask.h"
  44. #include "Firestore/core/src/model/field_path.h"
  45. #include "Firestore/core/src/model/field_transform.h"
  46. #include "Firestore/core/src/model/object_value.h"
  47. #include "Firestore/core/src/model/precondition.h"
  48. #include "Firestore/core/src/model/resource_path.h"
  49. #include "Firestore/core/src/model/transform_operation.h"
  50. #include "Firestore/core/src/model/value_util.h"
  51. #include "Firestore/core/src/nanopb/nanopb_util.h"
  52. #include "Firestore/core/src/nanopb/reader.h"
  53. #include "Firestore/core/src/remote/serializer.h"
  54. #include "Firestore/core/src/timestamp_internal.h"
  55. #include "Firestore/core/src/util/exception.h"
  56. #include "Firestore/core/src/util/hard_assert.h"
  57. #include "Firestore/core/src/util/read_context.h"
  58. #include "Firestore/core/src/util/string_apple.h"
  59. #include "absl/memory/memory.h"
  60. #include "absl/strings/match.h"
  61. #include "absl/types/optional.h"
  62. namespace nanopb = firebase::firestore::nanopb;
  63. using firebase::Timestamp;
  64. using firebase::TimestampInternal;
  65. using firebase::firestore::GeoPoint;
  66. using firebase::firestore::google_firestore_v1_ArrayValue;
  67. using firebase::firestore::google_firestore_v1_MapValue;
  68. using firebase::firestore::google_firestore_v1_MapValue_FieldsEntry;
  69. using firebase::firestore::google_firestore_v1_Value;
  70. using firebase::firestore::google_protobuf_NullValue_NULL_VALUE;
  71. using firebase::firestore::google_protobuf_Timestamp;
  72. using firebase::firestore::google_type_LatLng;
  73. using firebase::firestore::core::ParseAccumulator;
  74. using firebase::firestore::core::ParseContext;
  75. using firebase::firestore::core::ParsedSetData;
  76. using firebase::firestore::core::ParsedUpdateData;
  77. using firebase::firestore::core::UserDataSource;
  78. using firebase::firestore::model::ArrayTransform;
  79. using firebase::firestore::model::DatabaseId;
  80. using firebase::firestore::model::DeepClone;
  81. using firebase::firestore::model::DocumentKey;
  82. using firebase::firestore::model::FieldMask;
  83. using firebase::firestore::model::FieldPath;
  84. using firebase::firestore::model::FieldTransform;
  85. using firebase::firestore::model::NullValue;
  86. using firebase::firestore::model::NumericIncrementTransform;
  87. using firebase::firestore::model::ObjectValue;
  88. using firebase::firestore::model::ResourcePath;
  89. using firebase::firestore::model::ServerTimestampTransform;
  90. using firebase::firestore::model::TransformOperation;
  91. using firebase::firestore::nanopb::CheckedSize;
  92. using firebase::firestore::nanopb::Message;
  93. using firebase::firestore::remote::Serializer;
  94. using firebase::firestore::util::MakeString;
  95. using firebase::firestore::util::ReadContext;
  96. using firebase::firestore::util::ThrowInvalidArgument;
  97. using nanopb::StringReader;
  98. NS_ASSUME_NONNULL_BEGIN
  99. #pragma mark - FSTDocumentKeyReference
  100. @implementation FSTDocumentKeyReference {
  101. DocumentKey _key;
  102. DatabaseId _databaseID;
  103. }
  104. - (instancetype)initWithKey:(DocumentKey)key databaseID:(DatabaseId)databaseID {
  105. self = [super init];
  106. if (self) {
  107. _key = std::move(key);
  108. _databaseID = std::move(databaseID);
  109. }
  110. return self;
  111. }
  112. - (const model::DocumentKey &)key {
  113. return _key;
  114. }
  115. - (const model::DatabaseId &)databaseID {
  116. return _databaseID;
  117. }
  118. @end
  119. #pragma mark - FSTUserDataReader
  120. @interface FSTUserDataReader ()
  121. @property(strong, nonatomic, readonly) FSTPreConverterBlock preConverter;
  122. @end
  123. @implementation FSTUserDataReader {
  124. DatabaseId _databaseID;
  125. }
  126. - (instancetype)initWithDatabaseID:(DatabaseId)databaseID
  127. preConverter:(FSTPreConverterBlock)preConverter {
  128. self = [super init];
  129. if (self) {
  130. _databaseID = std::move(databaseID);
  131. _preConverter = preConverter;
  132. }
  133. return self;
  134. }
  135. - (ParsedSetData)parsedSetData:(id)input {
  136. // NOTE: The public API is typed as NSDictionary but we type 'input' as 'id' since we can't trust
  137. // Obj-C to verify the type for us.
  138. if (![input isKindOfClass:[NSDictionary class]]) {
  139. ThrowInvalidArgument("Data to be written must be an NSDictionary.");
  140. }
  141. ParseAccumulator accumulator{UserDataSource::Set};
  142. auto updateData = [self parseData:input context:accumulator.RootContext()];
  143. HARD_ASSERT(updateData.has_value(), "Parsed data should not be nil.");
  144. return std::move(accumulator).SetData(ObjectValue{std::move(*updateData)});
  145. }
  146. - (ParsedSetData)parsedMergeData:(id)input fieldMask:(nullable NSArray<id> *)fieldMask {
  147. // NOTE: The public API is typed as NSDictionary but we type 'input' as 'id' since we can't trust
  148. // Obj-C to verify the type for us.
  149. if (![input isKindOfClass:[NSDictionary class]]) {
  150. ThrowInvalidArgument("Data to be written must be an NSDictionary.");
  151. }
  152. ParseAccumulator accumulator{UserDataSource::MergeSet};
  153. auto updateData = [self parseData:input context:accumulator.RootContext()];
  154. HARD_ASSERT(updateData.has_value(), "Parsed data should not be nil.");
  155. ObjectValue updateObject{std::move(*updateData)};
  156. if (fieldMask) {
  157. std::set<FieldPath> validatedFieldPaths;
  158. for (id fieldPath in fieldMask) {
  159. FieldPath path;
  160. if ([fieldPath isKindOfClass:[NSString class]]) {
  161. path = FieldPath::FromDotSeparatedString(MakeString(fieldPath));
  162. } else if ([fieldPath isKindOfClass:[FIRFieldPath class]]) {
  163. path = static_cast<FIRFieldPath *>(fieldPath).internalValue;
  164. } else {
  165. ThrowInvalidArgument("All elements in mergeFields: must be NSStrings or FIRFieldPaths.");
  166. }
  167. // Verify that all elements specified in the field mask are part of the parsed context.
  168. if (!accumulator.Contains(path)) {
  169. ThrowInvalidArgument(
  170. "Field '%s' is specified in your field mask but missing from your input data.",
  171. path.CanonicalString());
  172. }
  173. validatedFieldPaths.insert(path);
  174. }
  175. return std::move(accumulator)
  176. .MergeData(std::move(updateObject), FieldMask{std::move(validatedFieldPaths)});
  177. } else {
  178. return std::move(accumulator).MergeData(std::move(updateObject));
  179. }
  180. }
  181. - (ParsedUpdateData)parsedUpdateData:(id)input {
  182. // NOTE: The public API is typed as NSDictionary but we type 'input' as 'id' since we can't trust
  183. // Obj-C to verify the type for us.
  184. if (![input isKindOfClass:[NSDictionary class]]) {
  185. ThrowInvalidArgument("Data to be written must be an NSDictionary.");
  186. }
  187. NSDictionary *dict = input;
  188. ParseAccumulator accumulator{UserDataSource::Update};
  189. __block ParseContext context = accumulator.RootContext();
  190. __block ObjectValue updateData;
  191. [dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *) {
  192. FieldPath path;
  193. if ([key isKindOfClass:[NSString class]]) {
  194. path = FieldPath::FromDotSeparatedString(MakeString(key));
  195. } else if ([key isKindOfClass:[FIRFieldPath class]]) {
  196. path = ((FIRFieldPath *)key).internalValue;
  197. } else {
  198. ThrowInvalidArgument("Dictionary keys in updateData: must be NSStrings or FIRFieldPaths.");
  199. }
  200. value = self.preConverter(value);
  201. if ([value isKindOfClass:[FSTDeleteFieldValue class]]) {
  202. // Add it to the field mask, but don't add anything to updateData.
  203. context.AddToFieldMask(std::move(path));
  204. } else {
  205. auto parsedValue = [self parseData:value context:context.ChildContext(path)];
  206. if (parsedValue) {
  207. context.AddToFieldMask(path);
  208. updateData.Set(path, std::move(*parsedValue));
  209. }
  210. }
  211. }];
  212. return std::move(accumulator).UpdateData(std::move(updateData));
  213. }
  214. - (Message<google_firestore_v1_Value>)parsedQueryValue:(id)input {
  215. return [self parsedQueryValue:input allowArrays:false];
  216. }
  217. - (Message<google_firestore_v1_Value>)parsedQueryValue:(id)input allowArrays:(bool)allowArrays {
  218. ParseAccumulator accumulator{allowArrays ? UserDataSource::ArrayArgument
  219. : UserDataSource::Argument};
  220. auto parsed = [self parseData:input context:accumulator.RootContext()];
  221. HARD_ASSERT(parsed, "Parsed data should not be nil.");
  222. HARD_ASSERT(accumulator.field_transforms().empty(),
  223. "Field transforms should have been disallowed.");
  224. return std::move(*parsed);
  225. }
  226. /**
  227. * Internal helper for parsing user data.
  228. *
  229. * @param input Data to be parsed.
  230. * @param context A context object representing the current path being parsed, the source of the
  231. * data being parsed, etc.
  232. *
  233. * @return The parsed value, or nil if the value was a FieldValue sentinel that should not be
  234. * included in the resulting parsed data.
  235. */
  236. - (absl::optional<Message<google_firestore_v1_Value>>)parseData:(id)input
  237. context:(ParseContext &&)context {
  238. input = self.preConverter(input);
  239. if ([input isKindOfClass:[NSDictionary class]]) {
  240. return [self parseDictionary:(NSDictionary *)input context:std::move(context)];
  241. } else if ([input isKindOfClass:[FIRFieldValue class]]) {
  242. // FieldValues usually parse into transforms (except FieldValue.delete()) in which case we
  243. // do not want to include this field in our parsed data (as doing so will overwrite the field
  244. // directly prior to the transform trying to transform it). So we don't call appendToFieldMask
  245. // and we return nil as our parsing result.
  246. [self parseSentinelFieldValue:(FIRFieldValue *)input context:std::move(context)];
  247. return absl::nullopt;
  248. } else {
  249. // If context path is unset we are already inside an array and we don't support field mask paths
  250. // more granular than the top-level array.
  251. if (context.path()) {
  252. context.AddToFieldMask(*context.path());
  253. }
  254. if ([input isKindOfClass:[NSArray class]]) {
  255. // TODO(b/34871131): Include the path containing the array in the error message.
  256. // In the case of IN queries, the parsed data is an array (representing the set of values to
  257. // be included for the IN query) that may directly contain additional arrays (each
  258. // representing an individual field value), so we disable this validation.
  259. if (context.array_element() && context.data_source() != UserDataSource::ArrayArgument) {
  260. ThrowInvalidArgument("Nested arrays are not supported");
  261. }
  262. return [self parseArray:(NSArray *)input context:std::move(context)];
  263. } else {
  264. return [self parseScalarValue:input context:std::move(context)];
  265. }
  266. }
  267. }
  268. - (Message<google_firestore_v1_Value>)parseDictionary:(NSDictionary<NSString *, id> *)dict
  269. context:(ParseContext &&)context {
  270. __block Message<google_firestore_v1_Value> result;
  271. result->which_value_type = google_firestore_v1_Value_map_value_tag;
  272. result->map_value = {};
  273. if (dict.count == 0) {
  274. const FieldPath *path = context.path();
  275. if (path && !path->empty()) {
  276. context.AddToFieldMask(*path);
  277. }
  278. } else {
  279. // Compute the final size of the fields array, which contains an entry for
  280. // all fields that are not FieldValue sentinels
  281. __block pb_size_t count = 0;
  282. [dict enumerateKeysAndObjectsUsingBlock:^(NSString *, id value, BOOL *) {
  283. if (![value isKindOfClass:[FIRFieldValue class]]) {
  284. ++count;
  285. }
  286. }];
  287. result->map_value.fields_count = count;
  288. result->map_value.fields = nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(count);
  289. __block pb_size_t index = 0;
  290. [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *) {
  291. auto parsedValue = [self parseData:value context:context.ChildContext(MakeString(key))];
  292. if (parsedValue) {
  293. result->map_value.fields[index].key = nanopb::MakeBytesArray(MakeString(key));
  294. result->map_value.fields[index].value = *parsedValue->release();
  295. ++index;
  296. }
  297. }];
  298. }
  299. return std::move(result);
  300. }
  301. - (Message<google_firestore_v1_Value>)parseVectorValue:(FIRVectorValue *)vectorValue
  302. context:(ParseContext &&)context {
  303. __block Message<google_firestore_v1_Value> result;
  304. result->which_value_type = google_firestore_v1_Value_map_value_tag;
  305. result->map_value = {};
  306. result->map_value.fields_count = 2;
  307. result->map_value.fields = nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(2);
  308. result->map_value.fields[0].key = nanopb::CopyBytesArray(model::kTypeValueFieldKey);
  309. result->map_value.fields[0].value = *[self encodeStringValue:MakeString(@"__vector__")].release();
  310. NSArray<NSNumber *> *vectorArray = vectorValue.array;
  311. __block Message<google_firestore_v1_Value> arrayMessage;
  312. arrayMessage->which_value_type = google_firestore_v1_Value_array_value_tag;
  313. arrayMessage->array_value.values_count = CheckedSize([vectorArray count]);
  314. arrayMessage->array_value.values =
  315. nanopb::MakeArray<google_firestore_v1_Value>(arrayMessage->array_value.values_count);
  316. [vectorArray enumerateObjectsUsingBlock:^(id entry, NSUInteger idx, BOOL *) {
  317. if (![entry isKindOfClass:[NSNumber class]]) {
  318. ThrowInvalidArgument("VectorValues must only contain numeric values.",
  319. context.FieldDescription());
  320. }
  321. // Vector values must always use Double encoding
  322. arrayMessage->array_value.values[idx] = *[self encodeDouble:[entry doubleValue]].release();
  323. }];
  324. result->map_value.fields[1].key = nanopb::CopyBytesArray(model::kVectorValueFieldKey);
  325. result->map_value.fields[1].value = *arrayMessage.release();
  326. return std::move(result);
  327. }
  328. - (Message<google_firestore_v1_Value>)parseMinKey {
  329. __block Message<google_firestore_v1_Value> result;
  330. result->which_value_type = google_firestore_v1_Value_map_value_tag;
  331. result->map_value = {};
  332. result->map_value.fields_count = 1;
  333. result->map_value.fields = nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(1);
  334. result->map_value.fields[0].key = nanopb::CopyBytesArray(model::kMinKeyTypeFieldValue);
  335. result->map_value.fields[0].value = *DeepClone(NullValue()).release();
  336. return std::move(result);
  337. }
  338. - (Message<google_firestore_v1_Value>)parseMaxKey {
  339. __block Message<google_firestore_v1_Value> result;
  340. result->which_value_type = google_firestore_v1_Value_map_value_tag;
  341. result->map_value = {};
  342. result->map_value.fields_count = 1;
  343. result->map_value.fields = nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(1);
  344. result->map_value.fields[0].key = nanopb::CopyBytesArray(model::kMaxKeyTypeFieldValue);
  345. result->map_value.fields[0].value = *DeepClone(NullValue()).release();
  346. return std::move(result);
  347. }
  348. - (Message<google_firestore_v1_Value>)parseRegexValue:(FIRRegexValue *)regexValue
  349. context:(ParseContext &&)context {
  350. NSString *pattern = regexValue.pattern;
  351. NSString *options = regexValue.options;
  352. __block Message<google_firestore_v1_Value> regexMessage;
  353. regexMessage->which_value_type = google_firestore_v1_Value_map_value_tag;
  354. regexMessage->map_value = {};
  355. regexMessage->map_value.fields_count = 2;
  356. regexMessage->map_value.fields = nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(2);
  357. regexMessage->map_value.fields[0].key =
  358. nanopb::CopyBytesArray(model::kRegexTypePatternFieldValue);
  359. regexMessage->map_value.fields[0].value = *[self encodeStringValue:MakeString(pattern)].release();
  360. regexMessage->map_value.fields[1].key =
  361. nanopb::CopyBytesArray(model::kRegexTypeOptionsFieldValue);
  362. regexMessage->map_value.fields[1].value = *[self encodeStringValue:MakeString(options)].release();
  363. __block Message<google_firestore_v1_Value> result;
  364. result->which_value_type = google_firestore_v1_Value_map_value_tag;
  365. result->map_value = {};
  366. result->map_value.fields_count = 1;
  367. result->map_value.fields = nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(1);
  368. result->map_value.fields[0].key = nanopb::CopyBytesArray(model::kRegexTypeFieldValue);
  369. result->map_value.fields[0].value = *regexMessage.release();
  370. return std::move(result);
  371. }
  372. - (Message<google_firestore_v1_Value>)parseInt32Value:(FIRInt32Value *)int32
  373. context:(ParseContext &&)context {
  374. __block Message<google_firestore_v1_Value> result;
  375. result->which_value_type = google_firestore_v1_Value_map_value_tag;
  376. result->map_value = {};
  377. result->map_value.fields_count = 1;
  378. result->map_value.fields = nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(1);
  379. result->map_value.fields[0].key = nanopb::CopyBytesArray(model::kInt32TypeFieldValue);
  380. // The 32-bit integer value is encoded as a 64-bit long in the proto.
  381. result->map_value.fields[0].value =
  382. *[self encodeInteger:static_cast<int64_t>(int32.value)].release();
  383. return std::move(result);
  384. }
  385. - (Message<google_firestore_v1_Value>)parseDecimal128Value:(FIRDecimal128Value *)decimal128
  386. context:(ParseContext &&)context {
  387. __block Message<google_firestore_v1_Value> result;
  388. result->which_value_type = google_firestore_v1_Value_map_value_tag;
  389. result->map_value = {};
  390. result->map_value.fields_count = 1;
  391. result->map_value.fields = nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(1);
  392. result->map_value.fields[0].key = nanopb::CopyBytesArray(model::kDecimal128TypeFieldValue);
  393. result->map_value.fields[0].value =
  394. *[self encodeStringValue:MakeString(decimal128.value)].release();
  395. return std::move(result);
  396. }
  397. - (Message<google_firestore_v1_Value>)parseBsonObjectId:(FIRBSONObjectId *)oid
  398. context:(ParseContext &&)context {
  399. __block Message<google_firestore_v1_Value> result;
  400. result->which_value_type = google_firestore_v1_Value_map_value_tag;
  401. result->map_value = {};
  402. result->map_value.fields_count = 1;
  403. result->map_value.fields = nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(1);
  404. result->map_value.fields[0].key = nanopb::CopyBytesArray(model::kBsonObjectIdTypeFieldValue);
  405. result->map_value.fields[0].value = *[self encodeStringValue:MakeString(oid.value)].release();
  406. return std::move(result);
  407. }
  408. - (Message<google_firestore_v1_Value>)parseBsonTimestamp:(FIRBSONTimestamp *)timestamp
  409. context:(ParseContext &&)context {
  410. uint32_t seconds = timestamp.seconds;
  411. uint32_t increment = timestamp.increment;
  412. __block Message<google_firestore_v1_Value> timestampMessage;
  413. timestampMessage->which_value_type = google_firestore_v1_Value_map_value_tag;
  414. timestampMessage->map_value = {};
  415. timestampMessage->map_value.fields_count = 2;
  416. timestampMessage->map_value.fields =
  417. nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(2);
  418. timestampMessage->map_value.fields[0].key =
  419. nanopb::CopyBytesArray(model::kBsonTimestampTypeSecondsFieldValue);
  420. // The 32-bit unsigned integer value is encoded as a 64-bit long in the proto.
  421. timestampMessage->map_value.fields[0].value =
  422. *[self encodeInteger:static_cast<int64_t>(seconds)].release();
  423. timestampMessage->map_value.fields[1].key =
  424. nanopb::CopyBytesArray(model::kBsonTimestampTypeIncrementFieldValue);
  425. // The 32-bit unsigned integer value is encoded as a 64-bit long in the proto.
  426. timestampMessage->map_value.fields[1].value =
  427. *[self encodeInteger:static_cast<int64_t>(increment)].release();
  428. __block Message<google_firestore_v1_Value> result;
  429. result->which_value_type = google_firestore_v1_Value_map_value_tag;
  430. result->map_value = {};
  431. result->map_value.fields_count = 1;
  432. result->map_value.fields = nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(1);
  433. result->map_value.fields[0].key = nanopb::CopyBytesArray(model::kBsonTimestampTypeFieldValue);
  434. result->map_value.fields[0].value = *timestampMessage.release();
  435. return std::move(result);
  436. }
  437. - (Message<google_firestore_v1_Value>)parseBsonBinaryData:(FIRBSONBinaryData *)binaryData
  438. context:(ParseContext &&)context {
  439. uint8_t subtypeByte = binaryData.subtype;
  440. NSData *data = binaryData.data;
  441. // We need to prepend the data with one byte representation of the subtype.
  442. NSMutableData *concatData = [NSMutableData data];
  443. [concatData appendBytes:&subtypeByte length:1];
  444. [concatData appendData:data];
  445. __block Message<google_firestore_v1_Value> result;
  446. result->which_value_type = google_firestore_v1_Value_map_value_tag;
  447. result->map_value = {};
  448. result->map_value.fields_count = 1;
  449. result->map_value.fields = nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(1);
  450. result->map_value.fields[0].key = nanopb::CopyBytesArray(model::kBsonBinaryDataTypeFieldValue);
  451. result->map_value.fields[0].value =
  452. *[self encodeBlob:(nanopb::MakeByteString(concatData))].release();
  453. return std::move(result);
  454. }
  455. - (Message<google_firestore_v1_Value>)parseArray:(NSArray<id> *)array
  456. context:(ParseContext &&)context {
  457. __block Message<google_firestore_v1_Value> result;
  458. result->which_value_type = google_firestore_v1_Value_array_value_tag;
  459. result->array_value.values_count = CheckedSize([array count]);
  460. result->array_value.values =
  461. nanopb::MakeArray<google_firestore_v1_Value>(result->array_value.values_count);
  462. [array enumerateObjectsUsingBlock:^(id entry, NSUInteger idx, BOOL *) {
  463. auto parsedEntry = [self parseData:entry context:context.ChildContext(idx)];
  464. if (!parsedEntry) {
  465. // Just include nulls in the array for fields being replaced with a sentinel.
  466. parsedEntry.emplace(DeepClone(NullValue()));
  467. }
  468. result->array_value.values[idx] = *parsedEntry->release();
  469. }];
  470. return std::move(result);
  471. }
  472. /**
  473. * "Parses" the provided FIRFieldValue, adding any necessary transforms to
  474. * context.fieldTransforms.
  475. */
  476. - (void)parseSentinelFieldValue:(FIRFieldValue *)fieldValue context:(ParseContext &&)context {
  477. // Sentinels are only supported with writes, and not within arrays.
  478. if (!context.write()) {
  479. ThrowInvalidArgument("%s can only be used with updateData() and setData()%s",
  480. fieldValue.methodName, context.FieldDescription());
  481. }
  482. if (!context.path()) {
  483. ThrowInvalidArgument("%s is not currently supported inside arrays", fieldValue.methodName);
  484. }
  485. if ([fieldValue isKindOfClass:[FSTDeleteFieldValue class]]) {
  486. if (context.data_source() == UserDataSource::MergeSet) {
  487. // No transform to add for a delete, but we need to add it to our fieldMask so it gets
  488. // deleted.
  489. context.AddToFieldMask(*context.path());
  490. } else if (context.data_source() == UserDataSource::Update) {
  491. HARD_ASSERT(!context.path()->empty(),
  492. "FieldValue.delete() at the top level should have already been handled.");
  493. ThrowInvalidArgument("FieldValue.delete() can only appear at the top level of your "
  494. "update data%s",
  495. context.FieldDescription());
  496. } else {
  497. // We shouldn't encounter delete sentinels for queries or non-merge setData calls.
  498. ThrowInvalidArgument(
  499. "FieldValue.delete() can only be used with updateData() and setData() with merge:true%s",
  500. context.FieldDescription());
  501. }
  502. } else if ([fieldValue isKindOfClass:[FSTServerTimestampFieldValue class]]) {
  503. context.AddToFieldTransforms(*context.path(), ServerTimestampTransform());
  504. } else if ([fieldValue isKindOfClass:[FSTArrayUnionFieldValue class]]) {
  505. auto parsedElements =
  506. [self parseArrayTransformElements:((FSTArrayUnionFieldValue *)fieldValue).elements];
  507. ArrayTransform arrayUnion(TransformOperation::Type::ArrayUnion, std::move(parsedElements));
  508. context.AddToFieldTransforms(*context.path(), std::move(arrayUnion));
  509. } else if ([fieldValue isKindOfClass:[FSTArrayRemoveFieldValue class]]) {
  510. auto parsedElements =
  511. [self parseArrayTransformElements:((FSTArrayRemoveFieldValue *)fieldValue).elements];
  512. ArrayTransform arrayRemove(TransformOperation::Type::ArrayRemove, std::move(parsedElements));
  513. context.AddToFieldTransforms(*context.path(), std::move(arrayRemove));
  514. } else if ([fieldValue isKindOfClass:[FSTNumericIncrementFieldValue class]]) {
  515. auto *numericIncrementFieldValue = (FSTNumericIncrementFieldValue *)fieldValue;
  516. auto operand = [self parsedQueryValue:numericIncrementFieldValue.operand];
  517. NumericIncrementTransform numeric_increment(std::move(operand));
  518. context.AddToFieldTransforms(*context.path(), std::move(numeric_increment));
  519. } else {
  520. HARD_FAIL("Unknown FIRFieldValue type: %s", NSStringFromClass([fieldValue class]));
  521. }
  522. }
  523. /**
  524. * Helper to parse a scalar value (i.e. not an NSDictionary, NSArray, or FIRFieldValue).
  525. *
  526. * Note that it handles all NSNumber values that are encodable as int64_t or doubles
  527. * (depending on the underlying type of the NSNumber). Unsigned integer values are handled though
  528. * any value outside what is representable by int64_t (a signed 64-bit value) will throw an
  529. * exception.
  530. *
  531. * @return The parsed value.
  532. */
  533. - (Message<google_firestore_v1_Value>)parseScalarValue:(nullable id)input
  534. context:(ParseContext &&)context {
  535. if (!input || [input isMemberOfClass:[NSNull class]]) {
  536. return DeepClone(NullValue());
  537. } else if ([input isKindOfClass:[NSNumber class]]) {
  538. // Recover the underlying type of the number, using the method described here:
  539. // http://stackoverflow.com/questions/2518761/get-type-of-nsnumber
  540. const char *cType = [input objCType];
  541. // Type Encoding values taken from
  542. // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/
  543. // Articles/ocrtTypeEncodings.html
  544. switch (cType[0]) {
  545. case 'q':
  546. return [self encodeInteger:[input longLongValue]];
  547. case 'i': // Falls through.
  548. case 's': // Falls through.
  549. case 'l': // Falls through.
  550. case 'I': // Falls through.
  551. case 'S':
  552. // Coerce integer values that aren't long long. Allow unsigned integer types that are
  553. // guaranteed small enough to skip a length check.
  554. return [self encodeInteger:[input longLongValue]];
  555. case 'L': // Falls through.
  556. case 'Q':
  557. // Unsigned integers that could be too large. Note that the 'L' (long) case is handled here
  558. // because when compiled for LP64, unsigned long is 64 bits and could overflow int64_t.
  559. {
  560. unsigned long long extended = [input unsignedLongLongValue];
  561. if (extended > LLONG_MAX) {
  562. ThrowInvalidArgument("NSNumber (%s) is too large%s", [input unsignedLongLongValue],
  563. context.FieldDescription());
  564. } else {
  565. return [self encodeInteger:static_cast<int64_t>(extended)];
  566. }
  567. }
  568. case 'f':
  569. return [self encodeDouble:[input doubleValue]];
  570. case 'd':
  571. // Double values are already the right type, so just reuse the existing boxed double.
  572. //
  573. // Note that NSNumber already performs NaN normalization to a single shared instance
  574. // so there's no need to treat NaN specially here.
  575. return [self encodeDouble:[input doubleValue]];
  576. case 'B': // Falls through.
  577. case 'c': // Falls through.
  578. case 'C':
  579. // Boolean values are weird.
  580. //
  581. // On arm64, objCType of a BOOL-valued NSNumber will be "c", even though @encode(BOOL)
  582. // returns "B". "c" is the same as @encode(signed char). Unfortunately this means that
  583. // legitimate usage of signed chars is impossible, but this should be rare.
  584. //
  585. // Additionally, for consistency, map unsigned chars to bools in the same way.
  586. return [self encodeBoolean:[input boolValue]];
  587. default:
  588. // All documented codes should be handled above, so this shouldn't happen.
  589. HARD_FAIL("Unknown NSNumber objCType %s on %s", cType, input);
  590. }
  591. } else if ([input isKindOfClass:[NSString class]]) {
  592. std::string inputString = MakeString(input);
  593. return [self encodeStringValue:inputString];
  594. } else if ([input isKindOfClass:[NSDate class]]) {
  595. NSDate *inputDate = input;
  596. return [self encodeTimestampValue:api::MakeTimestamp(inputDate)];
  597. } else if ([input isKindOfClass:[FIRTimestamp class]]) {
  598. FIRTimestamp *inputTimestamp = input;
  599. Timestamp timestamp = TimestampInternal::Truncate(api::MakeTimestamp(inputTimestamp));
  600. return [self encodeTimestampValue:timestamp];
  601. } else if ([input isKindOfClass:[FIRGeoPoint class]]) {
  602. return [self encodeGeoPoint:api::MakeGeoPoint(input)];
  603. } else if ([input isKindOfClass:[NSData class]]) {
  604. NSData *inputData = input;
  605. return [self encodeBlob:(nanopb::MakeByteString(inputData))];
  606. } else if ([input isKindOfClass:[FSTDocumentKeyReference class]]) {
  607. FSTDocumentKeyReference *reference = input;
  608. if (reference.databaseID != _databaseID) {
  609. const DatabaseId &other = reference.databaseID;
  610. ThrowInvalidArgument(
  611. "Document Reference is for database %s/%s but should be for database %s/%s%s",
  612. other.project_id(), other.database_id(), _databaseID.project_id(),
  613. _databaseID.database_id(), context.FieldDescription());
  614. }
  615. return [self encodeReference:_databaseID key:reference.key];
  616. } else if ([input isKindOfClass:[FIRVectorValue class]]) {
  617. FIRVectorValue *vector = input;
  618. return [self parseVectorValue:vector context:std::move(context)];
  619. } else if ([input isKindOfClass:[FIRMinKey class]]) {
  620. return [self parseMinKey];
  621. } else if ([input isKindOfClass:[FIRMaxKey class]]) {
  622. return [self parseMaxKey];
  623. } else if ([input isKindOfClass:[FIRRegexValue class]]) {
  624. FIRRegexValue *regex = input;
  625. return [self parseRegexValue:regex context:std::move(context)];
  626. } else if ([input isKindOfClass:[FIRInt32Value class]]) {
  627. FIRInt32Value *value = input;
  628. return [self parseInt32Value:value context:std::move(context)];
  629. } else if ([input isKindOfClass:[FIRDecimal128Value class]]) {
  630. FIRDecimal128Value *value = input;
  631. return [self parseDecimal128Value:value context:std::move(context)];
  632. } else if ([input isKindOfClass:[FIRBSONObjectId class]]) {
  633. FIRBSONObjectId *oid = input;
  634. return [self parseBsonObjectId:oid context:std::move(context)];
  635. } else if ([input isKindOfClass:[FIRBSONTimestamp class]]) {
  636. FIRBSONTimestamp *timestamp = input;
  637. return [self parseBsonTimestamp:timestamp context:std::move(context)];
  638. } else if ([input isKindOfClass:[FIRBSONBinaryData class]]) {
  639. FIRBSONBinaryData *binaryData = input;
  640. return [self parseBsonBinaryData:binaryData context:std::move(context)];
  641. } else {
  642. ThrowInvalidArgument("Unsupported type: %s%s", NSStringFromClass([input class]),
  643. context.FieldDescription());
  644. }
  645. }
  646. - (Message<google_firestore_v1_Value>)encodeBoolean:(bool)value {
  647. Message<google_firestore_v1_Value> result;
  648. result->which_value_type = google_firestore_v1_Value_boolean_value_tag;
  649. result->boolean_value = value;
  650. return result;
  651. }
  652. - (Message<google_firestore_v1_Value>)encodeInteger:(int64_t)value {
  653. Message<google_firestore_v1_Value> result;
  654. result->which_value_type = google_firestore_v1_Value_integer_value_tag;
  655. result->integer_value = value;
  656. return result;
  657. }
  658. - (Message<google_firestore_v1_Value>)encodeDouble:(double)value {
  659. Message<google_firestore_v1_Value> result;
  660. result->which_value_type = google_firestore_v1_Value_double_value_tag;
  661. result->double_value = value;
  662. return result;
  663. }
  664. - (Message<google_firestore_v1_Value>)encodeTimestampValue:(Timestamp)value {
  665. Message<google_firestore_v1_Value> result;
  666. result->which_value_type = google_firestore_v1_Value_timestamp_value_tag;
  667. result->timestamp_value.seconds = value.seconds();
  668. result->timestamp_value.nanos = value.nanoseconds();
  669. return result;
  670. }
  671. - (Message<google_firestore_v1_Value>)encodeStringValue:(const std::string &)value {
  672. Message<google_firestore_v1_Value> result;
  673. result->which_value_type = google_firestore_v1_Value_string_value_tag;
  674. result->string_value = nanopb::MakeBytesArray(value);
  675. return result;
  676. }
  677. - (Message<google_firestore_v1_Value>)encodeBlob:(const nanopb::ByteString &)value {
  678. Message<google_firestore_v1_Value> result;
  679. result->which_value_type = google_firestore_v1_Value_bytes_value_tag;
  680. // Copy the blob so that pb_release can do the right thing.
  681. result->bytes_value = nanopb::CopyBytesArray(value.get());
  682. return result;
  683. }
  684. - (Message<google_firestore_v1_Value>)encodeReference:(const DatabaseId &)databaseId
  685. key:(const DocumentKey &)key {
  686. HARD_ASSERT(_databaseID == databaseId, "Database %s cannot encode reference from %s",
  687. _databaseID.ToString(), databaseId.ToString());
  688. std::string referenceName = ResourcePath({"projects", databaseId.project_id(), "databases",
  689. databaseId.database_id(), "documents", key.ToString()})
  690. .CanonicalString();
  691. Message<google_firestore_v1_Value> result;
  692. result->which_value_type = google_firestore_v1_Value_reference_value_tag;
  693. result->reference_value = nanopb::MakeBytesArray(referenceName);
  694. return result;
  695. }
  696. - (Message<google_firestore_v1_Value>)encodeGeoPoint:(const GeoPoint &)value {
  697. Message<google_firestore_v1_Value> result;
  698. result->which_value_type = google_firestore_v1_Value_geo_point_value_tag;
  699. result->geo_point_value.latitude = value.latitude();
  700. result->geo_point_value.longitude = value.longitude();
  701. return result;
  702. }
  703. - (Message<google_firestore_v1_ArrayValue>)parseArrayTransformElements:(NSArray<id> *)elements {
  704. ParseAccumulator accumulator{UserDataSource::Argument};
  705. Message<google_firestore_v1_ArrayValue> array_value;
  706. array_value->values_count = CheckedSize(elements.count);
  707. array_value->values = nanopb::MakeArray<google_firestore_v1_Value>(array_value->values_count);
  708. for (NSUInteger i = 0; i < elements.count; i++) {
  709. id element = elements[i];
  710. // Although array transforms are used with writes, the actual elements being unioned or removed
  711. // are not considered writes since they cannot contain any FieldValue sentinels, etc.
  712. ParseContext context = accumulator.RootContext();
  713. auto parsedElement = [self parseData:element context:context.ChildContext(i)];
  714. HARD_ASSERT(parsedElement && accumulator.field_transforms().empty(),
  715. "Failed to properly parse array transform element: %s", element);
  716. array_value->values[i] = *parsedElement->release();
  717. }
  718. return array_value;
  719. }
  720. @end
  721. NS_ASSUME_NONNULL_END