aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Firestore/Source')
-rw-r--r--Firestore/Source/API/FIRDocumentReference.mm24
-rw-r--r--Firestore/Source/API/FIRFieldPath.mm3
-rw-r--r--Firestore/Source/API/FIRFirestore.mm9
-rw-r--r--Firestore/Source/API/FIRQuery.mm8
-rw-r--r--Firestore/Source/API/FIRTransaction.mm15
-rw-r--r--Firestore/Source/API/FIRWriteBatch.mm17
-rw-r--r--Firestore/Source/API/FSTUserDataConverter.h3
-rw-r--r--Firestore/Source/API/FSTUserDataConverter.mm42
-rw-r--r--Firestore/Source/Core/FSTFirestoreClient.h19
-rw-r--r--Firestore/Source/Core/FSTFirestoreClient.mm67
-rw-r--r--Firestore/Source/Core/FSTQuery.mm5
-rw-r--r--Firestore/Source/Core/FSTSnapshotVersion.h43
-rw-r--r--Firestore/Source/Core/FSTSnapshotVersion.mm80
-rw-r--r--Firestore/Source/Core/FSTSyncEngine.mm25
-rw-r--r--Firestore/Source/Core/FSTTransaction.mm26
-rw-r--r--Firestore/Source/Core/FSTView.h11
-rw-r--r--Firestore/Source/Core/FSTView.mm100
-rw-r--r--Firestore/Source/Local/FSTLevelDB.mm4
-rw-r--r--Firestore/Source/Local/FSTLevelDBMutationQueue.mm1
-rw-r--r--Firestore/Source/Local/FSTLevelDBQueryCache.mm41
-rw-r--r--Firestore/Source/Local/FSTLocalDocumentsView.h5
-rw-r--r--Firestore/Source/Local/FSTLocalDocumentsView.mm16
-rw-r--r--Firestore/Source/Local/FSTLocalSerializer.h11
-rw-r--r--Firestore/Source/Local/FSTLocalSerializer.mm32
-rw-r--r--Firestore/Source/Local/FSTLocalStore.h8
-rw-r--r--Firestore/Source/Local/FSTLocalStore.mm133
-rw-r--r--Firestore/Source/Local/FSTLocalViewChanges.h11
-rw-r--r--Firestore/Source/Local/FSTLocalViewChanges.mm50
-rw-r--r--Firestore/Source/Local/FSTMemoryMutationQueue.h5
-rw-r--r--Firestore/Source/Local/FSTMemoryMutationQueue.mm12
-rw-r--r--Firestore/Source/Local/FSTMemoryPersistence.mm8
-rw-r--r--Firestore/Source/Local/FSTMemoryQueryCache.h7
-rw-r--r--Firestore/Source/Local/FSTMemoryQueryCache.mm44
-rw-r--r--Firestore/Source/Local/FSTPersistence.h57
-rw-r--r--Firestore/Source/Local/FSTQueryCache.h17
-rw-r--r--Firestore/Source/Local/FSTQueryData.h22
-rw-r--r--Firestore/Source/Local/FSTQueryData.mm40
-rw-r--r--Firestore/Source/Local/FSTReferenceSet.h10
-rw-r--r--Firestore/Source/Local/FSTReferenceSet.mm20
-rw-r--r--Firestore/Source/Model/FSTDocument.h9
-rw-r--r--Firestore/Source/Model/FSTDocument.mm40
-rw-r--r--Firestore/Source/Model/FSTDocumentKey.mm3
-rw-r--r--Firestore/Source/Model/FSTDocumentKeySet.h35
-rw-r--r--Firestore/Source/Model/FSTDocumentKeySet.mm31
-rw-r--r--Firestore/Source/Model/FSTDocumentVersionDictionary.h40
-rw-r--r--Firestore/Source/Model/FSTDocumentVersionDictionary.mm37
-rw-r--r--Firestore/Source/Model/FSTMutation.h8
-rw-r--r--Firestore/Source/Model/FSTMutation.mm112
-rw-r--r--Firestore/Source/Model/FSTMutationBatch.h28
-rw-r--r--Firestore/Source/Model/FSTMutationBatch.mm53
-rw-r--r--Firestore/Source/Public/FIRDocumentReference.h39
-rw-r--r--Firestore/Source/Public/FIRTransaction.h24
-rw-r--r--Firestore/Source/Public/FIRWriteBatch.h23
-rw-r--r--Firestore/Source/Remote/FSTDatastore.h1
-rw-r--r--Firestore/Source/Remote/FSTRemoteEvent.h65
-rw-r--r--Firestore/Source/Remote/FSTRemoteEvent.mm312
-rw-r--r--Firestore/Source/Remote/FSTRemoteStore.h1
-rw-r--r--Firestore/Source/Remote/FSTRemoteStore.mm23
-rw-r--r--Firestore/Source/Remote/FSTSerializerBeta.h19
-rw-r--r--Firestore/Source/Remote/FSTSerializerBeta.mm70
-rw-r--r--Firestore/Source/Remote/FSTStream.h7
-rw-r--r--Firestore/Source/Remote/FSTStream.mm6
-rw-r--r--Firestore/Source/Remote/FSTWatchChange.h1
-rw-r--r--Firestore/Source/Util/FSTAsyncQueryListener.h7
-rw-r--r--Firestore/Source/Util/FSTAsyncQueryListener.mm14
-rw-r--r--Firestore/Source/Util/FSTDispatchQueue.mm277
66 files changed, 1159 insertions, 1177 deletions
diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm
index c2fc546..5ad606c 100644
--- a/Firestore/Source/API/FIRDocumentReference.mm
+++ b/Firestore/Source/API/FIRDocumentReference.mm
@@ -125,6 +125,11 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)setData:(NSDictionary<NSString *, id> *)documentData
+ mergeFields:(NSArray<id> *)mergeFields {
+ return [self setData:documentData mergeFields:mergeFields completion:nil];
+}
+
+- (void)setData:(NSDictionary<NSString *, id> *)documentData
completion:(nullable void (^)(NSError *_Nullable error))completion {
return [self setData:documentData merge:NO completion:completion];
}
@@ -132,8 +137,19 @@ NS_ASSUME_NONNULL_BEGIN
- (void)setData:(NSDictionary<NSString *, id> *)documentData
merge:(BOOL)merge
completion:(nullable void (^)(NSError *_Nullable error))completion {
- FSTParsedSetData *parsed = merge ? [self.firestore.dataConverter parsedMergeData:documentData]
- : [self.firestore.dataConverter parsedSetData:documentData];
+ FSTParsedSetData *parsed =
+ merge ? [self.firestore.dataConverter parsedMergeData:documentData fieldMask:nil]
+ : [self.firestore.dataConverter parsedSetData:documentData];
+ return [self.firestore.client
+ writeMutations:[parsed mutationsWithKey:self.key precondition:Precondition::None()]
+ completion:completion];
+}
+
+- (void)setData:(NSDictionary<NSString *, id> *)documentData
+ mergeFields:(NSArray<id> *)mergeFields
+ completion:(nullable void (^)(NSError *_Nullable error))completion {
+ FSTParsedSetData *parsed =
+ [self.firestore.dataConverter parsedMergeData:documentData fieldMask:mergeFields];
return [self.firestore.client
writeMutations:[parsed mutationsWithKey:self.key precondition:Precondition::None()]
completion:completion];
@@ -264,8 +280,8 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions
};
FSTAsyncQueryListener *asyncListener =
- [[FSTAsyncQueryListener alloc] initWithDispatchQueue:self.firestore.client.userDispatchQueue
- snapshotHandler:snapshotHandler];
+ [[FSTAsyncQueryListener alloc] initWithExecutor:self.firestore.client.userExecutor
+ snapshotHandler:snapshotHandler];
FSTQueryListener *internalListener =
[firestore.client listenToQuery:query
diff --git a/Firestore/Source/API/FIRFieldPath.mm b/Firestore/Source/API/FIRFieldPath.mm
index d0d8714..4fd0022 100644
--- a/Firestore/Source/API/FIRFieldPath.mm
+++ b/Firestore/Source/API/FIRFieldPath.mm
@@ -25,6 +25,7 @@
#import "Firestore/Source/Util/FSTUsageValidation.h"
#include "Firestore/core/src/firebase/firestore/model/field_path.h"
+#include "Firestore/core/src/firebase/firestore/util/hashing.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
namespace util = firebase::firestore::util;
@@ -114,7 +115,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (NSUInteger)hash {
- return _internalValue.Hash();
+ return util::Hash(_internalValue);
}
- (const firebase::firestore::model::FieldPath &)internalValue {
diff --git a/Firestore/Source/API/FIRFirestore.mm b/Firestore/Source/API/FIRFirestore.mm
index fe461d6..e5f0c12 100644
--- a/Firestore/Source/API/FIRFirestore.mm
+++ b/Firestore/Source/API/FIRFirestore.mm
@@ -45,12 +45,16 @@
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
#include "absl/memory/memory.h"
+#include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h"
+
namespace util = firebase::firestore::util;
using firebase::firestore::auth::CredentialsProvider;
using firebase::firestore::auth::FirebaseCredentialsProvider;
using firebase::firestore::core::DatabaseInfo;
using firebase::firestore::model::DatabaseId;
using firebase::firestore::model::ResourcePath;
+using util::internal::Executor;
+using util::internal::ExecutorLibdispatch;
NS_ASSUME_NONNULL_BEGIN
@@ -272,12 +276,13 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain";
const DatabaseInfo database_info(*self.databaseID, util::MakeStringView(_persistenceKey),
util::MakeStringView(_settings.host), _settings.sslEnabled);
- FSTDispatchQueue *userDispatchQueue = [FSTDispatchQueue queueWith:_settings.dispatchQueue];
+ std::unique_ptr<Executor> userExecutor =
+ absl::make_unique<ExecutorLibdispatch>(_settings.dispatchQueue);
_client = [FSTFirestoreClient clientWithDatabaseInfo:database_info
usePersistence:_settings.persistenceEnabled
credentialsProvider:_credentialsProvider.get()
- userDispatchQueue:userDispatchQueue
+ userExecutor:std::move(userExecutor)
workerDispatchQueue:_workerDispatchQueue];
}
}
diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm
index 32d8327..596f6ac 100644
--- a/Firestore/Source/API/FIRQuery.mm
+++ b/Firestore/Source/API/FIRQuery.mm
@@ -183,8 +183,8 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions
};
FSTAsyncQueryListener *asyncListener =
- [[FSTAsyncQueryListener alloc] initWithDispatchQueue:self.firestore.client.userDispatchQueue
- snapshotHandler:snapshotHandler];
+ [[FSTAsyncQueryListener alloc] initWithExecutor:self.firestore.client.userExecutor
+ snapshotHandler:snapshotHandler];
FSTQueryListener *internalListener =
[firestore.client listenToQuery:query
@@ -456,8 +456,8 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions
if (fieldPath.IsKeyFieldPath()) {
if (filterOperator == FSTRelationFilterOperatorArrayContains) {
FSTThrowInvalidArgument(
- @"Invalid query. You can't do arrayContains queries on document ID since document IDs "
- @"are not arrays.");
+ @"Invalid query. You can't perform arrayContains queries on document ID since document "
+ "IDs are not arrays.");
}
if ([value isKindOfClass:[NSString class]]) {
NSString *documentKey = (NSString *)value;
diff --git a/Firestore/Source/API/FIRTransaction.mm b/Firestore/Source/API/FIRTransaction.mm
index 668a359..b5bdefa 100644
--- a/Firestore/Source/API/FIRTransaction.mm
+++ b/Firestore/Source/API/FIRTransaction.mm
@@ -68,8 +68,19 @@ NS_ASSUME_NONNULL_BEGIN
forDocument:(FIRDocumentReference *)document
merge:(BOOL)merge {
[self validateReference:document];
- FSTParsedSetData *parsed = merge ? [self.firestore.dataConverter parsedMergeData:data]
- : [self.firestore.dataConverter parsedSetData:data];
+ FSTParsedSetData *parsed = merge
+ ? [self.firestore.dataConverter parsedMergeData:data fieldMask:nil]
+ : [self.firestore.dataConverter parsedSetData:data];
+ [self.internalTransaction setData:parsed forDocument:document.key];
+ return self;
+}
+
+- (FIRTransaction *)setData:(NSDictionary<NSString *, id> *)data
+ forDocument:(FIRDocumentReference *)document
+ mergeFields:(NSArray<id> *)mergeFields {
+ [self validateReference:document];
+ FSTParsedSetData *parsed =
+ [self.firestore.dataConverter parsedMergeData:data fieldMask:mergeFields];
[self.internalTransaction setData:parsed forDocument:document.key];
return self;
}
diff --git a/Firestore/Source/API/FIRWriteBatch.mm b/Firestore/Source/API/FIRWriteBatch.mm
index 1185dae..366c708 100644
--- a/Firestore/Source/API/FIRWriteBatch.mm
+++ b/Firestore/Source/API/FIRWriteBatch.mm
@@ -70,8 +70,21 @@ NS_ASSUME_NONNULL_BEGIN
merge:(BOOL)merge {
[self verifyNotCommitted];
[self validateReference:document];
- FSTParsedSetData *parsed = merge ? [self.firestore.dataConverter parsedMergeData:data]
- : [self.firestore.dataConverter parsedSetData:data];
+ FSTParsedSetData *parsed = merge
+ ? [self.firestore.dataConverter parsedMergeData:data fieldMask:nil]
+ : [self.firestore.dataConverter parsedSetData:data];
+ [self.mutations
+ addObjectsFromArray:[parsed mutationsWithKey:document.key precondition:Precondition::None()]];
+ return self;
+}
+
+- (FIRWriteBatch *)setData:(NSDictionary<NSString *, id> *)data
+ forDocument:(FIRDocumentReference *)document
+ mergeFields:(NSArray<id> *)mergeFields {
+ [self verifyNotCommitted];
+ [self validateReference:document];
+ FSTParsedSetData *parsed =
+ [self.firestore.dataConverter parsedMergeData:data fieldMask:mergeFields];
[self.mutations
addObjectsFromArray:[parsed mutationsWithKey:document.key precondition:Precondition::None()]];
return self;
diff --git a/Firestore/Source/API/FSTUserDataConverter.h b/Firestore/Source/API/FSTUserDataConverter.h
index 98a65ae..27a5f09 100644
--- a/Firestore/Source/API/FSTUserDataConverter.h
+++ b/Firestore/Source/API/FSTUserDataConverter.h
@@ -27,7 +27,6 @@
@class FSTObjectValue;
@class FSTFieldValue;
@class FSTMutation;
-@class FSTSnapshotVersion;
NS_ASSUME_NONNULL_BEGIN
@@ -130,7 +129,7 @@ typedef id _Nullable (^FSTPreConverterBlock)(id _Nullable);
- (FSTParsedSetData *)parsedSetData:(id)input;
/** Parse document data from a setData call with `merge:YES`. */
-- (FSTParsedSetData *)parsedMergeData:(id)input;
+- (FSTParsedSetData *)parsedMergeData:(id)input fieldMask:(nullable NSArray<id> *)fieldMask;
/** Parse update data from an updateData call. */
- (FSTParsedUpdateData *)parsedUpdateData:(id)input;
diff --git a/Firestore/Source/API/FSTUserDataConverter.mm b/Firestore/Source/API/FSTUserDataConverter.mm
index 2794398..6d01c75 100644
--- a/Firestore/Source/API/FSTUserDataConverter.mm
+++ b/Firestore/Source/API/FSTUserDataConverter.mm
@@ -412,7 +412,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) {
return self;
}
-- (FSTParsedSetData *)parsedMergeData:(id)input {
+- (FSTParsedSetData *)parsedMergeData:(id)input fieldMask:(nullable NSArray<id> *)fieldMask {
// NOTE: The public API is typed as NSDictionary but we type 'input' as 'id' since we can't trust
// Obj-C to verify the type for us.
if (![input isKindOfClass:[NSDictionary class]]) {
@@ -424,9 +424,45 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) {
path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())];
FSTObjectValue *updateData = (FSTObjectValue *)[self parseData:input context:context];
+ FieldMask convertedFieldMask;
+ std::vector<FieldTransform> convertedFieldTransform;
+
+ if (fieldMask) {
+ __block std::vector<FieldPath> fieldMaskPaths{};
+ [fieldMask enumerateObjectsUsingBlock:^(id fieldPath, NSUInteger idx, BOOL *stop) {
+ FieldPath path{};
+
+ if ([fieldPath isKindOfClass:[NSString class]]) {
+ path = [FIRFieldPath pathWithDotSeparatedString:fieldPath].internalValue;
+ } else if ([fieldPath isKindOfClass:[FIRFieldPath class]]) {
+ path = ((FIRFieldPath *)fieldPath).internalValue;
+ } else {
+ FSTThrowInvalidArgument(
+ @"All elements in mergeFields: must be NSStrings or FIRFieldPaths.");
+ }
+
+ if ([updateData valueForPath:path] == nil) {
+ FSTThrowInvalidArgument(
+ @"Field '%s' is specified in your field mask but missing from your input data.",
+ path.CanonicalString().c_str());
+ }
+
+ fieldMaskPaths.push_back(path);
+ }];
+ convertedFieldMask = FieldMask(fieldMaskPaths);
+ std::copy_if(context.fieldTransforms->begin(), context.fieldTransforms->end(),
+ std::back_inserter(convertedFieldTransform),
+ [&](const FieldTransform &fieldTransform) {
+ return convertedFieldMask.covers(fieldTransform.path());
+ });
+ } else {
+ convertedFieldMask = FieldMask{*context.fieldMask};
+ convertedFieldTransform = *context.fieldTransforms;
+ }
+
return [[FSTParsedSetData alloc] initWithData:updateData
- fieldMask:FieldMask{*context.fieldMask}
- fieldTransforms:*context.fieldTransforms];
+ fieldMask:convertedFieldMask
+ fieldTransforms:convertedFieldTransform];
}
- (FSTParsedSetData *)parsedSetData:(id)input {
diff --git a/Firestore/Source/Core/FSTFirestoreClient.h b/Firestore/Source/Core/FSTFirestoreClient.h
index 7285e65..94c2284 100644
--- a/Firestore/Source/Core/FSTFirestoreClient.h
+++ b/Firestore/Source/Core/FSTFirestoreClient.h
@@ -15,6 +15,7 @@
*/
#import <Foundation/Foundation.h>
+#include <memory>
#import "Firestore/Source/Core/FSTTypes.h"
#import "Firestore/Source/Core/FSTViewSnapshot.h"
@@ -23,6 +24,7 @@
#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h"
#include "Firestore/core/src/firebase/firestore/core/database_info.h"
#include "Firestore/core/src/firebase/firestore/model/database_id.h"
+#include "Firestore/core/src/firebase/firestore/util/executor.h"
@class FIRDocumentReference;
@class FIRDocumentSnapshot;
@@ -50,14 +52,15 @@ NS_ASSUME_NONNULL_BEGIN
/**
* Creates and returns a FSTFirestoreClient with the given parameters.
*
- * All callbacks and events will be triggered on the provided userDispatchQueue.
+ * All callbacks and events will be triggered on the provided userExecutor.
*/
-+ (instancetype)clientWithDatabaseInfo:(const firebase::firestore::core::DatabaseInfo &)databaseInfo
- usePersistence:(BOOL)usePersistence
- credentialsProvider:(firebase::firestore::auth::CredentialsProvider *)
- credentialsProvider // no passing ownership
- userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue
- workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue;
++ (instancetype)
+clientWithDatabaseInfo:(const firebase::firestore::core::DatabaseInfo &)databaseInfo
+ usePersistence:(BOOL)usePersistence
+ credentialsProvider:(firebase::firestore::auth::CredentialsProvider *)
+ credentialsProvider // no passing ownership
+ userExecutor:(std::unique_ptr<firebase::firestore::util::internal::Executor>)userExecutor
+ workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue;
- (instancetype)init __attribute__((unavailable("Use static constructor method.")));
@@ -111,7 +114,7 @@ NS_ASSUME_NONNULL_BEGIN
* Dispatch queue for user callbacks / events. This will often be the "Main Dispatch Queue" of the
* app but the developer can configure it to a different queue if they so choose.
*/
-@property(nonatomic, strong, readonly) FSTDispatchQueue *userDispatchQueue;
+- (firebase::firestore::util::internal::Executor *)userExecutor;
@end
diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm
index 4f1a20b..cede958 100644
--- a/Firestore/Source/Core/FSTFirestoreClient.mm
+++ b/Firestore/Source/Core/FSTFirestoreClient.mm
@@ -18,6 +18,7 @@
#include <future> // NOLINT(build/c++11)
#include <memory>
+#include <utility>
#import "FIRFirestoreErrors.h"
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
@@ -56,6 +57,9 @@ using firebase::firestore::auth::CredentialsProvider;
using firebase::firestore::auth::User;
using firebase::firestore::core::DatabaseInfo;
using firebase::firestore::model::DatabaseId;
+using firebase::firestore::model::DocumentKeySet;
+
+using firebase::firestore::util::internal::Executor;
NS_ASSUME_NONNULL_BEGIN
@@ -67,7 +71,7 @@ NS_ASSUME_NONNULL_BEGIN
usePersistence:(BOOL)usePersistence
credentialsProvider:
(CredentialsProvider *)credentialsProvider // no passing ownership
- userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue
+ userExecutor:(std::unique_ptr<Executor>)userExecutor
workerDispatchQueue:(FSTDispatchQueue *)queue NS_DESIGNATED_INITIALIZER;
@property(nonatomic, assign, readonly) const DatabaseInfo *databaseInfo;
@@ -90,18 +94,24 @@ NS_ASSUME_NONNULL_BEGIN
@end
-@implementation FSTFirestoreClient
+@implementation FSTFirestoreClient {
+ std::unique_ptr<Executor> _userExecutor;
+}
+
+- (Executor *)userExecutor {
+ return _userExecutor.get();
+}
+ (instancetype)clientWithDatabaseInfo:(const DatabaseInfo &)databaseInfo
usePersistence:(BOOL)usePersistence
credentialsProvider:
(CredentialsProvider *)credentialsProvider // no passing ownership
- userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue
+ userExecutor:(std::unique_ptr<Executor>)userExecutor
workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue {
return [[FSTFirestoreClient alloc] initWithDatabaseInfo:databaseInfo
usePersistence:usePersistence
credentialsProvider:credentialsProvider
- userDispatchQueue:userDispatchQueue
+ userExecutor:std::move(userExecutor)
workerDispatchQueue:workerDispatchQueue];
}
@@ -109,12 +119,12 @@ NS_ASSUME_NONNULL_BEGIN
usePersistence:(BOOL)usePersistence
credentialsProvider:
(CredentialsProvider *)credentialsProvider // no passing ownership
- userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue
+ userExecutor:(std::unique_ptr<Executor>)userExecutor
workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue {
if (self = [super init]) {
_databaseInfo = databaseInfo;
_credentialsProvider = credentialsProvider;
- _userDispatchQueue = userDispatchQueue;
+ _userExecutor = std::move(userExecutor);
_workerDispatchQueue = workerDispatchQueue;
auto userPromise = std::make_shared<std::promise<User>>();
@@ -229,9 +239,7 @@ NS_ASSUME_NONNULL_BEGIN
[self.workerDispatchQueue dispatchAsync:^{
[self.remoteStore disableNetwork];
if (completion) {
- [self.userDispatchQueue dispatchAsync:^{
- completion(nil);
- }];
+ self->_userExecutor->Execute([=] { completion(nil); });
}
}];
}
@@ -240,9 +248,7 @@ NS_ASSUME_NONNULL_BEGIN
[self.workerDispatchQueue dispatchAsync:^{
[self.remoteStore enableNetwork];
if (completion) {
- [self.userDispatchQueue dispatchAsync:^{
- completion(nil);
- }];
+ self->_userExecutor->Execute([=] { completion(nil); });
}
}];
}
@@ -254,9 +260,7 @@ NS_ASSUME_NONNULL_BEGIN
[self.remoteStore shutdown];
[self.persistence shutdown];
if (completion) {
- [self.userDispatchQueue dispatchAsync:^{
- completion(nil);
- }];
+ self->_userExecutor->Execute([=] { completion(nil); });
}
}];
}
@@ -311,11 +315,9 @@ NS_ASSUME_NONNULL_BEGIN
completion:(void (^)(FIRQuerySnapshot *_Nullable query,
NSError *_Nullable error))completion {
[self.workerDispatchQueue dispatchAsync:^{
-
FSTDocumentDictionary *docs = [self.localStore executeQuery:query.query];
- FSTDocumentKeySet *remoteKeys = [FSTDocumentKeySet keySet];
- FSTView *view = [[FSTView alloc] initWithQuery:query.query remoteDocuments:remoteKeys];
+ FSTView *view = [[FSTView alloc] initWithQuery:query.query remoteDocuments:DocumentKeySet{}];
FSTViewDocumentChanges *viewDocChanges = [view computeChangesWithDocuments:docs];
FSTViewChange *viewChange = [view applyChangesToDocuments:viewDocChanges];
FSTAssert(viewChange.limboChanges.count == 0,
@@ -339,18 +341,14 @@ NS_ASSUME_NONNULL_BEGIN
[self.workerDispatchQueue dispatchAsync:^{
if (mutations.count == 0) {
if (completion) {
- [self.userDispatchQueue dispatchAsync:^{
- completion(nil);
- }];
+ self->_userExecutor->Execute([=] { completion(nil); });
}
} else {
[self.syncEngine writeMutations:mutations
completion:^(NSError *error) {
// Dispatch the result back onto the user dispatch queue.
if (completion) {
- [self.userDispatchQueue dispatchAsync:^{
- completion(error);
- }];
+ self->_userExecutor->Execute([=] { completion(error); });
}
}];
}
@@ -361,17 +359,16 @@ NS_ASSUME_NONNULL_BEGIN
updateBlock:(FSTTransactionBlock)updateBlock
completion:(FSTVoidIDErrorBlock)completion {
[self.workerDispatchQueue dispatchAsync:^{
- [self.syncEngine transactionWithRetries:retries
- workerDispatchQueue:self.workerDispatchQueue
- updateBlock:updateBlock
- completion:^(id _Nullable result, NSError *_Nullable error) {
- // Dispatch the result back onto the user dispatch queue.
- if (completion) {
- [self.userDispatchQueue dispatchAsync:^{
- completion(result, error);
- }];
- }
- }];
+ [self.syncEngine
+ transactionWithRetries:retries
+ workerDispatchQueue:self.workerDispatchQueue
+ updateBlock:updateBlock
+ completion:^(id _Nullable result, NSError *_Nullable error) {
+ // Dispatch the result back onto the user dispatch queue.
+ if (completion) {
+ self->_userExecutor->Execute([=] { completion(result, error); });
+ }
+ }];
}];
}
diff --git a/Firestore/Source/Core/FSTQuery.mm b/Firestore/Source/Core/FSTQuery.mm
index 0cd11e8..d3961e8 100644
--- a/Firestore/Source/Core/FSTQuery.mm
+++ b/Firestore/Source/Core/FSTQuery.mm
@@ -28,6 +28,7 @@
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
#include "Firestore/core/src/firebase/firestore/model/field_path.h"
#include "Firestore/core/src/firebase/firestore/model/resource_path.h"
+#include "Firestore/core/src/firebase/firestore/util/hashing.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
namespace util = firebase::firestore::util;
@@ -259,7 +260,7 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe
}
- (NSUInteger)hash {
- return _field.Hash();
+ return util::Hash(_field);
}
@end
@@ -305,7 +306,7 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe
}
- (NSUInteger)hash {
- return _field.Hash();
+ return util::Hash(_field);
}
@end
diff --git a/Firestore/Source/Core/FSTSnapshotVersion.h b/Firestore/Source/Core/FSTSnapshotVersion.h
deleted file mode 100644
index 8649d40..0000000
--- a/Firestore/Source/Core/FSTSnapshotVersion.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <Foundation/Foundation.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-@class FIRTimestamp;
-
-/**
- * A version of a document in Firestore. This corresponds to the version timestamp, such as
- * update_time or read_time.
- */
-@interface FSTSnapshotVersion : NSObject <NSCopying>
-
-/** Creates a new version that is smaller than all other versions. */
-+ (instancetype)noVersion;
-
-/** Creates a new version representing the given timestamp. */
-+ (instancetype)versionWithTimestamp:(FIRTimestamp *)timestamp;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-- (NSComparisonResult)compare:(FSTSnapshotVersion *)other;
-
-@property(nonatomic, strong, readonly) FIRTimestamp *timestamp;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Core/FSTSnapshotVersion.mm b/Firestore/Source/Core/FSTSnapshotVersion.mm
deleted file mode 100644
index 58b2be4..0000000
--- a/Firestore/Source/Core/FSTSnapshotVersion.mm
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
-
-#import "FIRTimestamp.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@implementation FSTSnapshotVersion
-
-+ (instancetype)noVersion {
- static FSTSnapshotVersion *min;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- FIRTimestamp *timestamp = [[FIRTimestamp alloc] initWithSeconds:0 nanoseconds:0];
- min = [FSTSnapshotVersion versionWithTimestamp:timestamp];
- });
- return min;
-}
-
-+ (instancetype)versionWithTimestamp:(FIRTimestamp *)timestamp {
- return [[FSTSnapshotVersion alloc] initWithTimestamp:timestamp];
-}
-
-- (instancetype)initWithTimestamp:(FIRTimestamp *)timestamp {
- self = [super init];
- if (self) {
- _timestamp = timestamp;
- }
- return self;
-}
-
-#pragma mark - NSObject methods
-
-- (BOOL)isEqual:(id)object {
- if (self == object) {
- return YES;
- }
- if (![object isKindOfClass:[FSTSnapshotVersion class]]) {
- return NO;
- }
- return [self.timestamp isEqual:((FSTSnapshotVersion *)object).timestamp];
-}
-
-- (NSUInteger)hash {
- return self.timestamp.hash;
-}
-
-- (NSString *)description {
- return [NSString stringWithFormat:@"<FSTSnapshotVersion: %@>", self.timestamp];
-}
-
-- (id)copyWithZone:(NSZone *_Nullable)zone {
- // Implements NSCopying without actually copying because timestamps are immutable.
- return self;
-}
-
-#pragma mark - Public methods
-
-- (NSComparisonResult)compare:(FSTSnapshotVersion *)other {
- return [self.timestamp compare:other.timestamp];
-}
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Core/FSTSyncEngine.mm b/Firestore/Source/Core/FSTSyncEngine.mm
index 138fb41..ed97d6c 100644
--- a/Firestore/Source/Core/FSTSyncEngine.mm
+++ b/Firestore/Source/Core/FSTSyncEngine.mm
@@ -21,10 +21,10 @@
#include <map>
#include <set>
#include <unordered_map>
+#include <utility>
#import "FIRFirestoreErrors.h"
#import "Firestore/Source/Core/FSTQuery.h"
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
#import "Firestore/Source/Core/FSTTransaction.h"
#import "Firestore/Source/Core/FSTView.h"
#import "Firestore/Source/Core/FSTViewSnapshot.h"
@@ -45,12 +45,15 @@
#include "Firestore/core/src/firebase/firestore/auth/user.h"
#include "Firestore/core/src/firebase/firestore/core/target_id_generator.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
using firebase::firestore::auth::HashUser;
using firebase::firestore::auth::User;
using firebase::firestore::core::TargetIdGenerator;
using firebase::firestore::model::DocumentKey;
+using firebase::firestore::model::SnapshotVersion;
using firebase::firestore::model::TargetId;
+using firebase::firestore::model::DocumentKeySet;
NS_ASSUME_NONNULL_BEGIN
@@ -184,9 +187,9 @@ static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1;
FSTQueryData *queryData = [self.localStore allocateQuery:query];
FSTDocumentDictionary *docs = [self.localStore executeQuery:query];
- FSTDocumentKeySet *remoteKeys = [self.localStore remoteDocumentKeysForTarget:queryData.targetID];
+ DocumentKeySet remoteKeys = [self.localStore remoteDocumentKeysForTarget:queryData.targetID];
- FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:remoteKeys];
+ FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:std::move(remoteKeys)];
FSTViewDocumentChanges *viewDocChanges = [view computeChangesWithDocuments:docs];
FSTViewChange *viewChange = [view applyChangesToDocuments:viewDocChanges];
FSTAssert(viewChange.limboChanges.count == 0,
@@ -301,8 +304,7 @@ static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1;
if (iter == self->_limboKeysByTarget.end()) {
FSTQueryView *qv = self.queryViewsByTarget[targetID];
FSTAssert(qv, @"Missing queryview for non-limbo query: %i", [targetID intValue]);
- [remoteEvent filterUpdatesFromTargetChange:targetChange
- existingDocuments:qv.view.syncedDocuments];
+ [targetChange.mapping filterUpdatesUsingExistingKeys:qv.view.syncedDocuments];
} else {
[remoteEvent synthesizeDeleteForLimboTargetChange:targetChange key:iter->second];
}
@@ -346,10 +348,15 @@ static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1;
NSMutableDictionary<NSNumber *, FSTTargetChange *> *targetChanges =
[NSMutableDictionary dictionary];
FSTDeletedDocument *doc =
- [FSTDeletedDocument documentWithKey:limboKey version:[FSTSnapshotVersion noVersion]];
- FSTRemoteEvent *event = [FSTRemoteEvent eventWithSnapshotVersion:[FSTSnapshotVersion noVersion]
- targetChanges:targetChanges
- documentUpdates:{{limboKey, doc}}];
+ [FSTDeletedDocument documentWithKey:limboKey version:SnapshotVersion::None()];
+ DocumentKeySet limboDocuments = DocumentKeySet{doc.key};
+ FSTRemoteEvent *event =
+ [[FSTRemoteEvent alloc] initWithSnapshotVersion:SnapshotVersion::None()
+ targetChanges:targetChanges
+ documentUpdates:{
+ { limboKey, doc }
+ }
+ limboDocuments:std::move(limboDocuments)];
[self applyRemoteEvent:event];
} else {
FSTQueryView *queryView = self.queryViewsByTarget[@(targetID)];
diff --git a/Firestore/Source/Core/FSTTransaction.mm b/Firestore/Source/Core/FSTTransaction.mm
index 4aabd5a..5c36b20 100644
--- a/Firestore/Source/Core/FSTTransaction.mm
+++ b/Firestore/Source/Core/FSTTransaction.mm
@@ -23,19 +23,21 @@
#import "FIRFirestoreErrors.h"
#import "Firestore/Source/API/FSTUserDataConverter.h"
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
#import "Firestore/Source/Model/FSTDocument.h"
-#import "Firestore/Source/Model/FSTDocumentKeySet.h"
#import "Firestore/Source/Model/FSTMutation.h"
#import "Firestore/Source/Remote/FSTDatastore.h"
#import "Firestore/Source/Util/FSTAssert.h"
#import "Firestore/Source/Util/FSTUsageValidation.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/document_key_set.h"
#include "Firestore/core/src/firebase/firestore/model/precondition.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
using firebase::firestore::model::DocumentKey;
using firebase::firestore::model::Precondition;
+using firebase::firestore::model::SnapshotVersion;
+using firebase::firestore::model::DocumentKeySet;
NS_ASSUME_NONNULL_BEGIN
@@ -53,7 +55,7 @@ NS_ASSUME_NONNULL_BEGIN
@end
@implementation FSTTransaction {
- std::map<DocumentKey, FSTSnapshotVersion *> _readVersions;
+ std::map<DocumentKey, SnapshotVersion> _readVersions;
}
+ (instancetype)transactionWithDatastore:(FSTDatastore *)datastore {
@@ -79,11 +81,11 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)recordVersionForDocument:(FSTMaybeDocument *)doc error:(NSError **)error {
FSTAssert(error != nil, @"nil error parameter");
*error = nil;
- FSTSnapshotVersion *docVersion = doc.version;
+ SnapshotVersion docVersion = doc.version;
if ([doc isKindOfClass:[FSTDeletedDocument class]]) {
// For deleted docs, we must record an explicit no version to build the right precondition
// when writing.
- docVersion = [FSTSnapshotVersion noVersion];
+ docVersion = SnapshotVersion::None();
}
if (_readVersions.find(doc.key) == _readVersions.end()) {
_readVersions[doc.key] = docVersion;
@@ -159,8 +161,8 @@ NS_ASSUME_NONNULL_BEGIN
return Precondition::Exists(true);
}
- FSTSnapshotVersion *version = iter->second;
- if ([version isEqual:[FSTSnapshotVersion noVersion]]) {
+ const SnapshotVersion &version = iter->second;
+ if (version == SnapshotVersion::None()) {
// The document was read, but doesn't exist.
// Return an error because the precondition is impossible
if (error) {
@@ -200,7 +202,7 @@ NS_ASSUME_NONNULL_BEGIN
precondition:[self preconditionForDocumentKey:key]] ]];
// Since the delete will be applied before all following writes, we need to ensure that the
// precondition for the next write will be exists without timestamp.
- _readVersions[key] = [FSTSnapshotVersion noVersion];
+ _readVersions[key] = SnapshotVersion::None();
}
- (void)commitWithCompletion:(FSTVoidErrorBlock)completion {
@@ -215,15 +217,15 @@ NS_ASSUME_NONNULL_BEGIN
}
// Make a list of read documents that haven't been written.
- FSTDocumentKeySet *unwritten = [FSTDocumentKeySet keySet];
+ DocumentKeySet unwritten;
for (const auto &kv : _readVersions) {
- unwritten = [unwritten setByAddingObject:kv.first];
+ unwritten = unwritten.insert(kv.first);
};
// For each mutation, note that the doc was written.
for (FSTMutation *mutation in self.mutations) {
- unwritten = [unwritten setByRemovingObject:mutation.key];
+ unwritten = unwritten.erase(mutation.key);
}
- if (unwritten.count) {
+ if (!unwritten.empty()) {
// TODO(klimt): This is a temporary restriction, until "verify" is supported on the backend.
completion([NSError
errorWithDomain:FIRFirestoreErrorDomain
diff --git a/Firestore/Source/Core/FSTView.h b/Firestore/Source/Core/FSTView.h
index 431b863..fc6cead 100644
--- a/Firestore/Source/Core/FSTView.h
+++ b/Firestore/Source/Core/FSTView.h
@@ -18,9 +18,9 @@
#import "Firestore/Source/Core/FSTTypes.h"
#import "Firestore/Source/Model/FSTDocumentDictionary.h"
-#import "Firestore/Source/Model/FSTDocumentKeySet.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/document_key_set.h"
@class FSTDocumentSet;
@class FSTDocumentViewChangeSet;
@@ -38,6 +38,8 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)init NS_UNAVAILABLE;
+- (const firebase::firestore::model::DocumentKeySet &)mutatedKeys;
+
/** The new set of docs that should be in the view. */
@property(nonatomic, strong, readonly) FSTDocumentSet *documentSet;
@@ -50,8 +52,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property(nonatomic, assign, readonly) BOOL needsRefill;
-@property(nonatomic, strong, readonly) FSTDocumentKeySet *mutatedKeys;
-
@end
#pragma mark - FSTLimboDocumentChange
@@ -97,7 +97,8 @@ typedef NS_ENUM(NSInteger, FSTLimboDocumentChangeType) {
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithQuery:(FSTQuery *)query
- remoteDocuments:(FSTDocumentKeySet *)remoteDocuments NS_DESIGNATED_INITIALIZER;
+ remoteDocuments:(firebase::firestore::model::DocumentKeySet)remoteDocuments
+ NS_DESIGNATED_INITIALIZER;
/**
* Iterates over a set of doc changes, applies the query limit, and computes what the new results
@@ -152,7 +153,7 @@ typedef NS_ENUM(NSInteger, FSTLimboDocumentChangeType) {
* The set of remote documents that the server has told us belongs to the target associated with
* this view.
*/
-@property(nonatomic, strong, readonly) FSTDocumentKeySet *syncedDocuments;
+- (const firebase::firestore::model::DocumentKeySet &)syncedDocuments;
@end
diff --git a/Firestore/Source/Core/FSTView.mm b/Firestore/Source/Core/FSTView.mm
index d87951a..d254a82 100644
--- a/Firestore/Source/Core/FSTView.mm
+++ b/Firestore/Source/Core/FSTView.mm
@@ -29,6 +29,7 @@
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
using firebase::firestore::model::DocumentKey;
+using firebase::firestore::model::DocumentKeySet;
NS_ASSUME_NONNULL_BEGIN
@@ -40,26 +41,32 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithDocumentSet:(FSTDocumentSet *)documentSet
changeSet:(FSTDocumentViewChangeSet *)changeSet
needsRefill:(BOOL)needsRefill
- mutatedKeys:(FSTDocumentKeySet *)mutatedKeys NS_DESIGNATED_INITIALIZER;
+ mutatedKeys:(DocumentKeySet)mutatedKeys NS_DESIGNATED_INITIALIZER;
@end
-@implementation FSTViewDocumentChanges
+@implementation FSTViewDocumentChanges {
+ DocumentKeySet _mutatedKeys;
+}
- (instancetype)initWithDocumentSet:(FSTDocumentSet *)documentSet
changeSet:(FSTDocumentViewChangeSet *)changeSet
needsRefill:(BOOL)needsRefill
- mutatedKeys:(FSTDocumentKeySet *)mutatedKeys {
+ mutatedKeys:(DocumentKeySet)mutatedKeys {
self = [super init];
if (self) {
_documentSet = documentSet;
_changeSet = changeSet;
_needsRefill = needsRefill;
- _mutatedKeys = mutatedKeys;
+ _mutatedKeys = std::move(mutatedKeys);
}
return self;
}
+- (const DocumentKeySet &)mutatedKeys {
+ return _mutatedKeys;
+}
+
@end
#pragma mark - FSTLimboDocumentChange
@@ -165,32 +172,33 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
@property(nonatomic, strong) FSTDocumentSet *documentSet;
-/** Documents included in the remote target. */
-@property(nonatomic, strong) FSTDocumentKeySet *syncedDocuments;
-
-/** Documents in the view but not in the remote target */
-@property(nonatomic, strong) FSTDocumentKeySet *limboDocuments;
+@end
-/** Document Keys that have local changes. */
-@property(nonatomic, strong) FSTDocumentKeySet *mutatedKeys;
+@implementation FSTView {
+ /** Documents included in the remote target. */
+ DocumentKeySet _syncedDocuments;
-@end
+ /** Documents in the view but not in the remote target */
+ DocumentKeySet _limboDocuments;
-@implementation FSTView
+ /** Document Keys that have local changes. */
+ DocumentKeySet _mutatedKeys;
+}
-- (instancetype)initWithQuery:(FSTQuery *)query
- remoteDocuments:(nonnull FSTDocumentKeySet *)remoteDocuments {
+- (instancetype)initWithQuery:(FSTQuery *)query remoteDocuments:(DocumentKeySet)remoteDocuments {
self = [super init];
if (self) {
_query = query;
_documentSet = [FSTDocumentSet documentSetWithComparator:query.comparator];
- _syncedDocuments = remoteDocuments;
- _limboDocuments = [FSTDocumentKeySet keySet];
- _mutatedKeys = [FSTDocumentKeySet keySet];
+ _syncedDocuments = std::move(remoteDocuments);
}
return self;
}
+- (const DocumentKeySet &)syncedDocuments {
+ return _syncedDocuments;
+}
+
- (FSTViewDocumentChanges *)computeChangesWithDocuments:(FSTMaybeDocumentDictionary *)docChanges {
return [self computeChangesWithDocuments:docChanges previousChanges:nil];
}
@@ -202,8 +210,8 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
previousChanges ? previousChanges.changeSet : [FSTDocumentViewChangeSet changeSet];
FSTDocumentSet *oldDocumentSet = previousChanges ? previousChanges.documentSet : self.documentSet;
- __block FSTDocumentKeySet *newMutatedKeys =
- previousChanges ? previousChanges.mutatedKeys : self.mutatedKeys;
+ __block DocumentKeySet newMutatedKeys =
+ previousChanges ? previousChanges.mutatedKeys : _mutatedKeys;
__block FSTDocumentSet *newDocumentSet = oldDocumentSet;
__block BOOL needsRefill = NO;
@@ -236,13 +244,13 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
if (newDoc) {
newDocumentSet = [newDocumentSet documentSetByAddingDocument:newDoc];
if (newDoc.hasLocalMutations) {
- newMutatedKeys = [newMutatedKeys setByAddingObject:key];
+ newMutatedKeys = newMutatedKeys.insert(key);
} else {
- newMutatedKeys = [newMutatedKeys setByRemovingObject:key];
+ newMutatedKeys = newMutatedKeys.erase(key);
}
} else {
newDocumentSet = [newDocumentSet documentSetByRemovingKey:key];
- newMutatedKeys = [newMutatedKeys setByRemovingObject:key];
+ newMutatedKeys = newMutatedKeys.erase(key);
}
// Calculate change
@@ -311,7 +319,7 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
FSTDocumentSet *oldDocuments = self.documentSet;
self.documentSet = docChanges.documentSet;
- self.mutatedKeys = docChanges.mutatedKeys;
+ _mutatedKeys = docChanges.mutatedKeys;
// Sort changes based on type and query comparator.
NSArray<FSTDocumentViewChange *> *changes = [docChanges.changeSet changes];
@@ -325,7 +333,7 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
}];
[self applyTargetChange:targetChange];
NSArray<FSTLimboDocumentChange *> *limboChanges = [self updateLimboDocuments];
- BOOL synced = self.limboDocuments.count == 0 && self.isCurrent;
+ BOOL synced = _limboDocuments.empty() && self.isCurrent;
FSTSyncState newSyncState = synced ? FSTSyncStateSynced : FSTSyncStateLocal;
BOOL syncStateChanged = newSyncState != self.syncState;
self.syncState = newSyncState;
@@ -340,7 +348,7 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
oldDocuments:oldDocuments
documentChanges:changes
fromCache:newSyncState == FSTSyncStateLocal
- hasPendingWrites:!docChanges.mutatedKeys.isEmpty
+ hasPendingWrites:!docChanges.mutatedKeys.empty()
syncStateChanged:syncStateChanged];
return [FSTViewChange changeWithSnapshot:snapshot limboChanges:limboChanges];
@@ -358,7 +366,7 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
initWithDocumentSet:self.documentSet
changeSet:[FSTDocumentViewChangeSet changeSet]
needsRefill:NO
- mutatedKeys:self.mutatedKeys]];
+ mutatedKeys:_mutatedKeys]];
} else {
// No effect, just return a no-op FSTViewChange.
return [[FSTViewChange alloc] initWithSnapshot:nil limboChanges:@[]];
@@ -370,7 +378,7 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
/** Returns whether the doc for the given key should be in limbo. */
- (BOOL)shouldBeLimboDocumentKey:(const DocumentKey &)key {
// If the remote end says it's part of this query, it's not in limbo.
- if ([self.syncedDocuments containsObject:key]) {
+ if (_syncedDocuments.contains(key)) {
return NO;
}
// The local store doesn't think it's a result, so it shouldn't be in limbo.
@@ -395,16 +403,14 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
if (targetChange) {
FSTTargetMapping *targetMapping = targetChange.mapping;
if ([targetMapping isKindOfClass:[FSTResetMapping class]]) {
- self.syncedDocuments = ((FSTResetMapping *)targetMapping).documents;
+ _syncedDocuments = ((FSTResetMapping *)targetMapping).documents;
} else if ([targetMapping isKindOfClass:[FSTUpdateMapping class]]) {
- [((FSTUpdateMapping *)targetMapping).addedDocuments
- enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) {
- self.syncedDocuments = [self.syncedDocuments setByAddingObject:key];
- }];
- [((FSTUpdateMapping *)targetMapping).removedDocuments
- enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) {
- self.syncedDocuments = [self.syncedDocuments setByRemovingObject:key];
- }];
+ for (const DocumentKey &key : ((FSTUpdateMapping *)targetMapping).addedDocuments) {
+ _syncedDocuments = _syncedDocuments.insert(key);
+ }
+ for (const DocumentKey &key : ((FSTUpdateMapping *)targetMapping).removedDocuments) {
+ _syncedDocuments = _syncedDocuments.erase(key);
+ }
}
switch (targetChange.currentStatusUpdate) {
@@ -428,29 +434,29 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
}
// TODO(klimt): Do this incrementally so that it's not quadratic when updating many documents.
- FSTDocumentKeySet *oldLimboDocuments = self.limboDocuments;
- self.limboDocuments = [FSTDocumentKeySet keySet];
+ DocumentKeySet oldLimboDocuments = std::move(_limboDocuments);
+ _limboDocuments = DocumentKeySet{};
for (FSTDocument *doc in self.documentSet.documentEnumerator) {
if ([self shouldBeLimboDocumentKey:doc.key]) {
- self.limboDocuments = [self.limboDocuments setByAddingObject:doc.key];
+ _limboDocuments = _limboDocuments.insert(doc.key);
}
}
// Diff the new limbo docs with the old limbo docs.
NSMutableArray<FSTLimboDocumentChange *> *changes =
- [NSMutableArray arrayWithCapacity:(oldLimboDocuments.count + self.limboDocuments.count)];
- [oldLimboDocuments enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) {
- if (![self.limboDocuments containsObject:key]) {
+ [NSMutableArray arrayWithCapacity:(oldLimboDocuments.size() + _limboDocuments.size())];
+ for (const DocumentKey &key : oldLimboDocuments) {
+ if (!_limboDocuments.contains(key)) {
[changes addObject:[FSTLimboDocumentChange changeWithType:FSTLimboDocumentChangeTypeRemoved
key:key]];
}
- }];
- [self.limboDocuments enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) {
- if (![oldLimboDocuments containsObject:key]) {
+ }
+ for (const DocumentKey &key : _limboDocuments) {
+ if (!oldLimboDocuments.contains(key)) {
[changes addObject:[FSTLimboDocumentChange changeWithType:FSTLimboDocumentChangeTypeAdded
key:key]];
}
- }];
+ }
return changes;
}
diff --git a/Firestore/Source/Local/FSTLevelDB.mm b/Firestore/Source/Local/FSTLevelDB.mm
index fae85e7..bc2f2eb 100644
--- a/Firestore/Source/Local/FSTLevelDB.mm
+++ b/Firestore/Source/Local/FSTLevelDB.mm
@@ -243,6 +243,10 @@ using leveldb::WriteOptions;
_ptr.reset();
}
+- (_Nullable id<FSTReferenceDelegate>)referenceDelegate {
+ return nil;
+}
+
#pragma mark - Error and Status
+ (nullable NSError *)errorWithStatus:(Status)status description:(NSString *)description, ... {
diff --git a/Firestore/Source/Local/FSTLevelDBMutationQueue.mm b/Firestore/Source/Local/FSTLevelDBMutationQueue.mm
index 75c3cf6..2c9f68d 100644
--- a/Firestore/Source/Local/FSTLevelDBMutationQueue.mm
+++ b/Firestore/Source/Local/FSTLevelDBMutationQueue.mm
@@ -511,6 +511,7 @@ using leveldb::WriteOptions;
documentKey:mutation.key
batchID:batchID];
_db.currentTransaction->Delete(key);
+ [_db.referenceDelegate removeMutationReference:mutation.key];
[garbageCollector addPotentialGarbageKey:mutation.key];
}
}
diff --git a/Firestore/Source/Local/FSTLevelDBQueryCache.mm b/Firestore/Source/Local/FSTLevelDBQueryCache.mm
index 5fde7d7..68b6f98 100644
--- a/Firestore/Source/Local/FSTLevelDBQueryCache.mm
+++ b/Firestore/Source/Local/FSTLevelDBQueryCache.mm
@@ -18,6 +18,7 @@
#include <memory>
#include <string>
+#include <utility>
#import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h"
#import "Firestore/Source/Core/FSTQuery.h"
@@ -28,6 +29,7 @@
#import "Firestore/Source/Util/FSTAssert.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
#include "absl/strings/match.h"
NS_ASSUME_NONNULL_BEGIN
@@ -35,9 +37,11 @@ NS_ASSUME_NONNULL_BEGIN
using firebase::firestore::local::LevelDbTransaction;
using Firestore::StringView;
using firebase::firestore::model::DocumentKey;
+using firebase::firestore::model::SnapshotVersion;
using leveldb::DB;
using leveldb::Slice;
using leveldb::Status;
+using firebase::firestore::model::DocumentKeySet;
@interface FSTLevelDBQueryCache ()
@@ -55,7 +59,7 @@ using leveldb::Status;
* The last received snapshot version. This is part of `metadata` but we store it separately to
* avoid extra conversion to/from GPBTimestamp.
*/
- FSTSnapshotVersion *_lastRemoteSnapshotVersion;
+ SnapshotVersion _lastRemoteSnapshotVersion;
}
+ (nullable FSTPBTargetGlobal *)readTargetMetadataWithTransaction:
@@ -135,13 +139,14 @@ using leveldb::Status;
return self.metadata.highestListenSequenceNumber;
}
-- (FSTSnapshotVersion *)lastRemoteSnapshotVersion {
+- (const SnapshotVersion &)lastRemoteSnapshotVersion {
return _lastRemoteSnapshotVersion;
}
-- (void)setLastRemoteSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion {
- _lastRemoteSnapshotVersion = snapshotVersion;
- self.metadata.lastRemoteSnapshotVersion = [self.serializer encodedVersion:snapshotVersion];
+- (void)setLastRemoteSnapshotVersion:(SnapshotVersion)snapshotVersion {
+ _lastRemoteSnapshotVersion = std::move(snapshotVersion);
+ self.metadata.lastRemoteSnapshotVersion =
+ [self.serializer encodedVersion:_lastRemoteSnapshotVersion];
_db.currentTransaction->Put([FSTLevelDBTargetGlobalKey key], self.metadata);
}
@@ -278,30 +283,30 @@ using leveldb::Status;
#pragma mark Matching Key tracking
-- (void)addMatchingKeys:(FSTDocumentKeySet *)keys forTargetID:(FSTTargetID)targetID {
+- (void)addMatchingKeys:(const DocumentKeySet &)keys forTargetID:(FSTTargetID)targetID {
// Store an empty value in the index which is equivalent to serializing a GPBEmpty message. In the
// future if we wanted to store some other kind of value here, we can parse these empty values as
// with some other protocol buffer (and the parser will see all default values).
std::string emptyBuffer;
- [keys enumerateObjectsUsingBlock:^(FSTDocumentKey *documentKey, BOOL *stop) {
+ for (const DocumentKey &key : keys) {
self->_db.currentTransaction->Put(
- [FSTLevelDBTargetDocumentKey keyWithTargetID:targetID documentKey:documentKey],
- emptyBuffer);
+ [FSTLevelDBTargetDocumentKey keyWithTargetID:targetID documentKey:key], emptyBuffer);
self->_db.currentTransaction->Put(
- [FSTLevelDBDocumentTargetKey keyWithDocumentKey:documentKey targetID:targetID],
- emptyBuffer);
- }];
+ [FSTLevelDBDocumentTargetKey keyWithDocumentKey:key targetID:targetID], emptyBuffer);
+ [self->_db.referenceDelegate addReference:key target:targetID];
+ };
}
-- (void)removeMatchingKeys:(FSTDocumentKeySet *)keys forTargetID:(FSTTargetID)targetID {
- [keys enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) {
+- (void)removeMatchingKeys:(const DocumentKeySet &)keys forTargetID:(FSTTargetID)targetID {
+ for (const DocumentKey &key : keys) {
self->_db.currentTransaction->Delete(
[FSTLevelDBTargetDocumentKey keyWithTargetID:targetID documentKey:key]);
self->_db.currentTransaction->Delete(
[FSTLevelDBDocumentTargetKey keyWithDocumentKey:key targetID:targetID]);
+ [self->_db.referenceDelegate removeReference:key target:targetID];
[self.garbageCollector addPotentialGarbageKey:key];
- }];
+ }
}
- (void)removeMatchingKeysForTargetID:(FSTTargetID)targetID {
@@ -327,12 +332,12 @@ using leveldb::Status;
}
}
-- (FSTDocumentKeySet *)matchingKeysForTargetID:(FSTTargetID)targetID {
+- (DocumentKeySet)matchingKeysForTargetID:(FSTTargetID)targetID {
std::string indexPrefix = [FSTLevelDBTargetDocumentKey keyPrefixWithTargetID:targetID];
auto indexIterator = _db.currentTransaction->NewIterator();
indexIterator->Seek(indexPrefix);
- FSTDocumentKeySet *result = [FSTDocumentKeySet keySet];
+ DocumentKeySet result;
FSTLevelDBTargetDocumentKey *rowKey = [[FSTLevelDBTargetDocumentKey alloc] init];
for (; indexIterator->Valid(); indexIterator->Next()) {
absl::string_view indexKey = indexIterator->key();
@@ -342,7 +347,7 @@ using leveldb::Status;
break;
}
- result = [result setByAddingObject:rowKey.documentKey];
+ result = result.insert(rowKey.documentKey);
}
return result;
diff --git a/Firestore/Source/Local/FSTLocalDocumentsView.h b/Firestore/Source/Local/FSTLocalDocumentsView.h
index e75e0f3..bb5bb22 100644
--- a/Firestore/Source/Local/FSTLocalDocumentsView.h
+++ b/Firestore/Source/Local/FSTLocalDocumentsView.h
@@ -17,9 +17,9 @@
#import <Foundation/Foundation.h>
#import "Firestore/Source/Model/FSTDocumentDictionary.h"
-#import "Firestore/Source/Model/FSTDocumentKeySet.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/document_key_set.h"
@class FSTMaybeDocument;
@class FSTQuery;
@@ -53,7 +53,8 @@ NS_ASSUME_NONNULL_BEGIN
* If we don't have cached state for a document in `keys`, a FSTDeletedDocument will be stored
* for that key in the resulting set.
*/
-- (FSTMaybeDocumentDictionary *)documentsForKeys:(FSTDocumentKeySet *)keys;
+- (FSTMaybeDocumentDictionary *)documentsForKeys:
+ (const firebase::firestore::model::DocumentKeySet &)keys;
/** Performs a query against the local view of all documents. */
- (FSTDocumentDictionary *)documentsMatchingQuery:(FSTQuery *)query;
diff --git a/Firestore/Source/Local/FSTLocalDocumentsView.mm b/Firestore/Source/Local/FSTLocalDocumentsView.mm
index e9b9423..471840a 100644
--- a/Firestore/Source/Local/FSTLocalDocumentsView.mm
+++ b/Firestore/Source/Local/FSTLocalDocumentsView.mm
@@ -17,7 +17,6 @@
#import "Firestore/Source/Local/FSTLocalDocumentsView.h"
#import "Firestore/Source/Core/FSTQuery.h"
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
#import "Firestore/Source/Local/FSTMutationQueue.h"
#import "Firestore/Source/Local/FSTRemoteDocumentCache.h"
#import "Firestore/Source/Model/FSTDocument.h"
@@ -28,9 +27,12 @@
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
#include "Firestore/core/src/firebase/firestore/model/resource_path.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
using firebase::firestore::model::DocumentKey;
using firebase::firestore::model::ResourcePath;
+using firebase::firestore::model::SnapshotVersion;
+using firebase::firestore::model::DocumentKeySet;
NS_ASSUME_NONNULL_BEGIN
@@ -64,14 +66,14 @@ NS_ASSUME_NONNULL_BEGIN
return [self localDocument:remoteDoc key:key];
}
-- (FSTMaybeDocumentDictionary *)documentsForKeys:(FSTDocumentKeySet *)keys {
+- (FSTMaybeDocumentDictionary *)documentsForKeys:(const DocumentKeySet &)keys {
FSTMaybeDocumentDictionary *results = [FSTMaybeDocumentDictionary maybeDocumentDictionary];
- for (FSTDocumentKey *key in keys.objectEnumerator) {
+ for (const DocumentKey &key : keys) {
// TODO(mikelehen): PERF: Consider fetching all remote documents at once rather than one-by-one.
FSTMaybeDocument *maybeDoc = [self documentForKey:key];
// TODO(http://b/32275378): Don't conflate missing / deleted.
if (!maybeDoc) {
- maybeDoc = [FSTDeletedDocument documentWithKey:key version:[FSTSnapshotVersion noVersion]];
+ maybeDoc = [FSTDeletedDocument documentWithKey:key version:SnapshotVersion::None()];
}
results = [results dictionaryBySettingObject:maybeDoc forKey:key];
}
@@ -105,7 +107,7 @@ NS_ASSUME_NONNULL_BEGIN
// Now use the mutation queue to discover any other documents that may match the query after
// applying mutations.
- FSTDocumentKeySet *matchingKeys = [FSTDocumentKeySet keySet];
+ DocumentKeySet matchingKeys;
NSArray<FSTMutationBatch *> *matchingMutationBatches =
[self.mutationQueue allMutationBatchesAffectingQuery:query];
for (FSTMutationBatch *batch in matchingMutationBatches) {
@@ -114,13 +116,13 @@ NS_ASSUME_NONNULL_BEGIN
// If the key is already in the results, we can skip it.
if (![results containsKey:mutation.key]) {
- matchingKeys = [matchingKeys setByAddingObject:mutation.key];
+ matchingKeys = matchingKeys.insert(mutation.key);
}
}
}
// Now add in results for the matchingKeys.
- for (FSTDocumentKey *key in matchingKeys.objectEnumerator) {
+ for (const DocumentKey &key : matchingKeys) {
FSTMaybeDocument *doc = [self documentForKey:key];
if ([doc isKindOfClass:[FSTDocument class]]) {
results = [results dictionaryBySettingObject:(FSTDocument *)doc forKey:key];
diff --git a/Firestore/Source/Local/FSTLocalSerializer.h b/Firestore/Source/Local/FSTLocalSerializer.h
index 6ca7f01..b75f3e6 100644
--- a/Firestore/Source/Local/FSTLocalSerializer.h
+++ b/Firestore/Source/Local/FSTLocalSerializer.h
@@ -16,11 +16,12 @@
#import <Foundation/Foundation.h>
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
+
@class FSTMaybeDocument;
@class FSTMutationBatch;
@class FSTQueryData;
@class FSTSerializerBeta;
-@class FSTSnapshotVersion;
@class FSTPBMaybeDocument;
@class FSTPBTarget;
@@ -61,11 +62,11 @@ NS_ASSUME_NONNULL_BEGIN
/** Decodes an FSTPBTarget proto from local storage into an FSTQueryData model. */
- (FSTQueryData *)decodedQueryData:(FSTPBTarget *)target;
-/** Encodes an FSTSnapshotVersion model into a GPBTimestamp proto. */
-- (GPBTimestamp *)encodedVersion:(FSTSnapshotVersion *)version;
+/** Encodes a SnapshotVersion model into a GPBTimestamp proto. */
+- (GPBTimestamp *)encodedVersion:(const firebase::firestore::model::SnapshotVersion &)version;
-/** Decodes a GPBTimestamp proto into a FSTSnapshotVersion model. */
-- (FSTSnapshotVersion *)decodedVersion:(GPBTimestamp *)version;
+/** Decodes a GPBTimestamp proto into a SnapshotVersion model. */
+- (firebase::firestore::model::SnapshotVersion)decodedVersion:(GPBTimestamp *)version;
@end
diff --git a/Firestore/Source/Local/FSTLocalSerializer.mm b/Firestore/Source/Local/FSTLocalSerializer.mm
index 61e173a..2c5ab4d 100644
--- a/Firestore/Source/Local/FSTLocalSerializer.mm
+++ b/Firestore/Source/Local/FSTLocalSerializer.mm
@@ -18,6 +18,7 @@
#include <cinttypes>
+#import "FIRTimestamp.h"
#import "Firestore/Protos/objc/firestore/local/MaybeDocument.pbobjc.h"
#import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h"
#import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h"
@@ -30,9 +31,13 @@
#import "Firestore/Source/Remote/FSTSerializerBeta.h"
#import "Firestore/Source/Util/FSTAssert.h"
+#include "Firestore/core/include/firebase/firestore/timestamp.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
+using firebase::Timestamp;
using firebase::firestore::model::DocumentKey;
+using firebase::firestore::model::SnapshotVersion;
@interface FSTLocalSerializer ()
@@ -99,8 +104,8 @@ using firebase::firestore::model::DocumentKey;
FSTSerializerBeta *remoteSerializer = self.remoteSerializer;
FSTObjectValue *data = [remoteSerializer decodedFields:document.fields];
- const DocumentKey key = [remoteSerializer decodedDocumentKey:document.name];
- FSTSnapshotVersion *version = [remoteSerializer decodedVersion:document.updateTime];
+ DocumentKey key = [remoteSerializer decodedDocumentKey:document.name];
+ SnapshotVersion version = [remoteSerializer decodedVersion:document.updateTime];
return [FSTDocument documentWithData:data key:key version:version hasLocalMutations:NO];
}
@@ -118,8 +123,8 @@ using firebase::firestore::model::DocumentKey;
- (FSTDeletedDocument *)decodedDeletedDocument:(FSTPBNoDocument *)proto {
FSTSerializerBeta *remoteSerializer = self.remoteSerializer;
- const DocumentKey key = [remoteSerializer decodedDocumentKey:proto.name];
- FSTSnapshotVersion *version = [remoteSerializer decodedVersion:proto.readTime];
+ DocumentKey key = [remoteSerializer decodedDocumentKey:proto.name];
+ SnapshotVersion version = [remoteSerializer decodedVersion:proto.readTime];
return [FSTDeletedDocument documentWithKey:key version:version];
}
@@ -128,7 +133,8 @@ using firebase::firestore::model::DocumentKey;
FSTPBWriteBatch *proto = [FSTPBWriteBatch message];
proto.batchId = batch.batchID;
- proto.localWriteTime = [remoteSerializer encodedTimestamp:batch.localWriteTime];
+ proto.localWriteTime = [remoteSerializer
+ encodedTimestamp:Timestamp{batch.localWriteTime.seconds, batch.localWriteTime.nanoseconds}];
NSMutableArray<GCFSWrite *> *writes = proto.writesArray;
for (FSTMutation *mutation in batch.mutations) {
@@ -146,11 +152,13 @@ using firebase::firestore::model::DocumentKey;
[mutations addObject:[remoteSerializer decodedMutation:write]];
}
- FIRTimestamp *localWriteTime = [remoteSerializer decodedTimestamp:batch.localWriteTime];
+ Timestamp localWriteTime = [remoteSerializer decodedTimestamp:batch.localWriteTime];
- return [[FSTMutationBatch alloc] initWithBatchID:batchID
- localWriteTime:localWriteTime
- mutations:mutations];
+ return [[FSTMutationBatch alloc]
+ initWithBatchID:batchID
+ localWriteTime:[FIRTimestamp timestampWithSeconds:localWriteTime.seconds()
+ nanoseconds:localWriteTime.nanoseconds()]
+ mutations:mutations];
}
- (FSTPBTarget *)encodedQueryData:(FSTQueryData *)queryData {
@@ -181,7 +189,7 @@ using firebase::firestore::model::DocumentKey;
FSTTargetID targetID = target.targetId;
FSTListenSequenceNumber sequenceNumber = target.lastListenSequenceNumber;
- FSTSnapshotVersion *version = [remoteSerializer decodedVersion:target.snapshotVersion];
+ SnapshotVersion version = [remoteSerializer decodedVersion:target.snapshotVersion];
NSData *resumeToken = target.resumeToken;
FSTQuery *query;
@@ -206,11 +214,11 @@ using firebase::firestore::model::DocumentKey;
resumeToken:resumeToken];
}
-- (GPBTimestamp *)encodedVersion:(FSTSnapshotVersion *)version {
+- (GPBTimestamp *)encodedVersion:(const SnapshotVersion &)version {
return [self.remoteSerializer encodedVersion:version];
}
-- (FSTSnapshotVersion *)decodedVersion:(GPBTimestamp *)version {
+- (SnapshotVersion)decodedVersion:(GPBTimestamp *)version {
return [self.remoteSerializer decodedVersion:version];
}
diff --git a/Firestore/Source/Local/FSTLocalStore.h b/Firestore/Source/Local/FSTLocalStore.h
index 82402e4..1f4146a 100644
--- a/Firestore/Source/Local/FSTLocalStore.h
+++ b/Firestore/Source/Local/FSTLocalStore.h
@@ -18,11 +18,11 @@
#import "Firestore/Source/Core/FSTTypes.h"
#import "Firestore/Source/Model/FSTDocumentDictionary.h"
-#import "Firestore/Source/Model/FSTDocumentKeySet.h"
-#import "Firestore/Source/Model/FSTDocumentVersionDictionary.h"
#include "Firestore/core/src/firebase/firestore/auth/user.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/document_key_set.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
@class FSTLocalViewChanges;
@class FSTLocalWriteResult;
@@ -141,7 +141,7 @@ NS_ASSUME_NONNULL_BEGIN
* Returns the last consistent snapshot processed (used by the RemoteStore to determine whether to
* buffer incoming snapshots from the backend).
*/
-- (FSTSnapshotVersion *)lastRemoteSnapshotVersion;
+- (const firebase::firestore::model::SnapshotVersion &)lastRemoteSnapshotVersion;
/**
* Updates the "ground-state" (remote) documents. We assume that the remote event reflects any
@@ -156,7 +156,7 @@ NS_ASSUME_NONNULL_BEGIN
* Returns the keys of the documents that are associated with the given targetID in the remote
* table.
*/
-- (FSTDocumentKeySet *)remoteDocumentKeysForTarget:(FSTTargetID)targetID;
+- (firebase::firestore::model::DocumentKeySet)remoteDocumentKeysForTarget:(FSTTargetID)targetID;
/**
* Collects garbage if necessary.
diff --git a/Firestore/Source/Local/FSTLocalStore.mm b/Firestore/Source/Local/FSTLocalStore.mm
index b5dfeec..0d6a785 100644
--- a/Firestore/Source/Local/FSTLocalStore.mm
+++ b/Firestore/Source/Local/FSTLocalStore.mm
@@ -21,7 +21,6 @@
#import "FIRTimestamp.h"
#import "Firestore/Source/Core/FSTListenSequence.h"
#import "Firestore/Source/Core/FSTQuery.h"
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
#import "Firestore/Source/Local/FSTGarbageCollector.h"
#import "Firestore/Source/Local/FSTLocalDocumentsView.h"
#import "Firestore/Source/Local/FSTLocalViewChanges.h"
@@ -43,11 +42,14 @@
#include "Firestore/core/src/firebase/firestore/auth/user.h"
#include "Firestore/core/src/firebase/firestore/core/target_id_generator.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
using firebase::firestore::auth::User;
-using firebase::firestore::model::DocumentKey;
using firebase::firestore::core::TargetIdGenerator;
using firebase::firestore::model::DocumentKey;
+using firebase::firestore::model::SnapshotVersion;
+using firebase::firestore::model::DocumentKeySet;
+using firebase::firestore::model::DocumentVersionMap;
NS_ASSUME_NONNULL_BEGIN
@@ -112,6 +114,7 @@ NS_ASSUME_NONNULL_BEGIN
_localDocuments = [FSTLocalDocumentsView viewWithRemoteDocumentCache:_remoteDocumentCache
mutationQueue:_mutationQueue];
_localViewReferences = [[FSTReferenceSet alloc] init];
+ [_persistence.referenceDelegate addInMemoryPins:_localViewReferences];
_garbageCollector = garbageCollector;
[_garbageCollector addGarbageSource:_queryCache];
@@ -186,11 +189,11 @@ NS_ASSUME_NONNULL_BEGIN
mutationQueue:self.mutationQueue];
// Union the old/new changed keys.
- FSTDocumentKeySet *changedKeys = [FSTDocumentKeySet keySet];
+ DocumentKeySet changedKeys;
for (NSArray<FSTMutationBatch *> *batches in @[ oldBatches, newBatches ]) {
for (FSTMutationBatch *batch in batches) {
for (FSTMutation *mutation in batch.mutations) {
- changedKeys = [changedKeys setByAddingObject:mutation.key];
+ changedKeys = changedKeys.insert(mutation.key);
}
}
}
@@ -205,7 +208,7 @@ NS_ASSUME_NONNULL_BEGIN
FIRTimestamp *localWriteTime = [FIRTimestamp timestamp];
FSTMutationBatch *batch =
[self.mutationQueue addMutationBatchWithWriteTime:localWriteTime mutations:mutations];
- FSTDocumentKeySet *keys = [batch keys];
+ DocumentKeySet keys = [batch keys];
FSTMaybeDocumentDictionary *changedDocuments = [self.localDocuments documentsForKeys:keys];
return [FSTLocalWriteResult resultForBatchID:batch.batchID changes:changedDocuments];
});
@@ -217,10 +220,9 @@ NS_ASSUME_NONNULL_BEGIN
[mutationQueue acknowledgeBatch:batchResult.batch streamToken:batchResult.streamToken];
- FSTDocumentKeySet *affected;
+ DocumentKeySet affected;
if ([self shouldHoldBatchResultWithVersion:batchResult.commitVersion]) {
[self.heldBatchResults addObject:batchResult];
- affected = [FSTDocumentKeySet keySet];
} else {
affected = [self releaseBatchResults:@[ batchResult ]];
}
@@ -239,7 +241,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTBatchID lastAcked = [self.mutationQueue highestAcknowledgedBatchID];
FSTAssert(batchID > lastAcked, @"Acknowledged batches can't be rejected.");
- FSTDocumentKeySet *affected = [self removeMutationBatch:toReject];
+ DocumentKeySet affected = [self removeMutationBatch:toReject];
[self.mutationQueue performConsistencyCheck];
@@ -256,12 +258,14 @@ NS_ASSUME_NONNULL_BEGIN
[&]() { [self.mutationQueue setLastStreamToken:streamToken]; });
}
-- (FSTSnapshotVersion *)lastRemoteSnapshotVersion {
+- (const SnapshotVersion &)lastRemoteSnapshotVersion {
return [self.queryCache lastRemoteSnapshotVersion];
}
- (FSTMaybeDocumentDictionary *)applyRemoteEvent:(FSTRemoteEvent *)remoteEvent {
return self.persistence.run("Apply remote event", [&]() -> FSTMaybeDocumentDictionary * {
+ // TODO(gsoltis): move the sequence number into the reference delegate.
+ FSTListenSequenceNumber sequenceNumber = [self.listenSequence next];
id<FSTQueryCache> queryCache = self.queryCache;
[remoteEvent.targetChanges enumerateKeysAndObjectsUsingBlock:^(
@@ -274,6 +278,18 @@ NS_ASSUME_NONNULL_BEGIN
return;
}
+ // Update the resume token if the change includes one. Don't clear any preexisting value.
+ // Bump the sequence number as well, so that documents being removed now are ordered later
+ // than documents that were previously removed from this target.
+ NSData *resumeToken = change.resumeToken;
+ if (resumeToken.length > 0) {
+ queryData = [queryData queryDataByReplacingSnapshotVersion:change.snapshotVersion
+ resumeToken:resumeToken
+ sequenceNumber:sequenceNumber];
+ self.targetIDs[targetIDNumber] = queryData;
+ [self.queryCache updateQueryData:queryData];
+ }
+
FSTTargetMapping *mapping = change.mapping;
if (mapping) {
// First make sure that all references are deleted.
@@ -291,61 +307,58 @@ NS_ASSUME_NONNULL_BEGIN
FSTFail(@"Unknown mapping type: %@", mapping);
}
}
-
- // Update the resume token if the change includes one. Don't clear any preexisting value.
- NSData *resumeToken = change.resumeToken;
- if (resumeToken.length > 0) {
- queryData = [queryData queryDataByReplacingSnapshotVersion:change.snapshotVersion
- resumeToken:resumeToken];
- self.targetIDs[targetIDNumber] = queryData;
- [self.queryCache updateQueryData:queryData];
- }
}];
// TODO(klimt): This could probably be an NSMutableDictionary.
- FSTDocumentKeySet *changedDocKeys = [FSTDocumentKeySet keySet];
+ DocumentKeySet changedDocKeys;
+ const DocumentKeySet &limboDocuments = remoteEvent.limboDocumentChanges;
for (const auto &kv : remoteEvent.documentUpdates) {
const DocumentKey &key = kv.first;
FSTMaybeDocument *doc = kv.second;
- changedDocKeys = [changedDocKeys setByAddingObject:key];
+ changedDocKeys = changedDocKeys.insert(key);
FSTMaybeDocument *existingDoc = [self.remoteDocumentCache entryForKey:key];
// Make sure we don't apply an old document version to the remote cache, though we
- // make an exception for [SnapshotVersion noVersion] which can happen for manufactured
+ // make an exception for SnapshotVersion::None() which can happen for manufactured
// events (e.g. in the case of a limbo document resolution failing).
- if (!existingDoc || [doc.version isEqual:[FSTSnapshotVersion noVersion]] ||
- [doc.version compare:existingDoc.version] != NSOrderedAscending) {
+ if (!existingDoc || SnapshotVersion{doc.version} == SnapshotVersion::None() ||
+ SnapshotVersion{doc.version} >= SnapshotVersion{existingDoc.version}) {
[self.remoteDocumentCache addEntry:doc];
} else {
FSTLog(
@"FSTLocalStore Ignoring outdated watch update for %s. "
- "Current version: %@ Watch version: %@",
- key.ToString().c_str(), existingDoc.version, doc.version);
+ "Current version: %s Watch version: %s",
+ key.ToString().c_str(), existingDoc.version.timestamp().ToString().c_str(),
+ doc.version.timestamp().ToString().c_str());
}
// The document might be garbage because it was unreferenced by everything.
// Make sure to mark it as garbage if it is...
[self.garbageCollector addPotentialGarbageKey:key];
+ if (limboDocuments.contains(key)) {
+ [self.persistence.referenceDelegate limboDocumentUpdated:key];
+ }
}
// HACK: The only reason we allow omitting snapshot version is so we can synthesize remote
// events when we get permission denied errors while trying to resolve the state of a locally
// cached document that is in limbo.
- FSTSnapshotVersion *lastRemoteVersion = [self.queryCache lastRemoteSnapshotVersion];
- FSTSnapshotVersion *remoteVersion = remoteEvent.snapshotVersion;
- if (![remoteVersion isEqual:[FSTSnapshotVersion noVersion]]) {
- FSTAssert([remoteVersion compare:lastRemoteVersion] != NSOrderedAscending,
- @"Watch stream reverted to previous snapshot?? (%@ < %@)", remoteVersion,
- lastRemoteVersion);
+ const SnapshotVersion &lastRemoteVersion = [self.queryCache lastRemoteSnapshotVersion];
+ const SnapshotVersion &remoteVersion = remoteEvent.snapshotVersion;
+ if (remoteVersion != SnapshotVersion::None()) {
+ FSTAssert(remoteVersion >= lastRemoteVersion,
+ @"Watch stream reverted to previous snapshot?? (%s < %s)",
+ remoteVersion.timestamp().ToString().c_str(),
+ lastRemoteVersion.timestamp().ToString().c_str());
[self.queryCache setLastRemoteSnapshotVersion:remoteVersion];
}
- FSTDocumentKeySet *releasedWriteKeys = [self releaseHeldBatchResults];
+ DocumentKeySet releasedWriteKeys = [self releaseHeldBatchResults];
// Union the two key sets.
- __block FSTDocumentKeySet *keysToRecalc = changedDocKeys;
- [releasedWriteKeys enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) {
- keysToRecalc = [keysToRecalc setByAddingObject:key];
- }];
+ DocumentKeySet keysToRecalc = changedDocKeys;
+ for (const DocumentKey &key : releasedWriteKeys) {
+ keysToRecalc = keysToRecalc.insert(key);
+ }
return [self.localDocuments documentsForKeys:keysToRecalc];
});
@@ -358,6 +371,9 @@ NS_ASSUME_NONNULL_BEGIN
FSTQueryData *queryData = [self.queryCache queryDataForQuery:view.query];
FSTAssert(queryData, @"Local view changes contain unallocated query.");
FSTTargetID targetID = queryData.targetID;
+ for (const DocumentKey &key : view.removedKeys) {
+ [self->_persistence.referenceDelegate removeReference:key target:targetID];
+ }
[localViewReferences addReferencesToKeys:view.addedKeys forID:targetID];
[localViewReferences removeReferencesToKeys:view.removedKeys forID:targetID];
}
@@ -408,6 +424,7 @@ NS_ASSUME_NONNULL_BEGIN
if (self.garbageCollector.isEager) {
[self.queryCache removeQueryData:queryData];
}
+ [self.persistence.referenceDelegate removeTarget:queryData];
[self.targetIDs removeObjectForKey:@(queryData.targetID)];
// If this was the last watch target, then we won't get any more watch snapshots, so we should
@@ -424,8 +441,8 @@ NS_ASSUME_NONNULL_BEGIN
});
}
-- (FSTDocumentKeySet *)remoteDocumentKeysForTarget:(FSTTargetID)targetID {
- return self.persistence.run("RemoteDocumentKeysForTarget", [&]() -> FSTDocumentKeySet * {
+- (DocumentKeySet)remoteDocumentKeysForTarget:(FSTTargetID)targetID {
+ return self.persistence.run("RemoteDocumentKeysForTarget", [&]() -> DocumentKeySet {
return [self.queryCache matchingKeysForTargetID:targetID];
});
}
@@ -449,7 +466,7 @@ NS_ASSUME_NONNULL_BEGIN
*
* @return the set of keys of docs that were modified by those writes.
*/
-- (FSTDocumentKeySet *)releaseHeldBatchResults {
+- (DocumentKeySet)releaseHeldBatchResults {
NSMutableArray<FSTMutationBatchResult *> *toRelease = [NSMutableArray array];
for (FSTMutationBatchResult *batchResult in self.heldBatchResults) {
if (![self isRemoteUpToVersion:batchResult.commitVersion]) {
@@ -459,25 +476,24 @@ NS_ASSUME_NONNULL_BEGIN
}
if (toRelease.count == 0) {
- return [FSTDocumentKeySet keySet];
+ return DocumentKeySet{};
} else {
[self.heldBatchResults removeObjectsInRange:NSMakeRange(0, toRelease.count)];
return [self releaseBatchResults:toRelease];
}
}
-- (BOOL)isRemoteUpToVersion:(FSTSnapshotVersion *)version {
+- (BOOL)isRemoteUpToVersion:(const SnapshotVersion &)version {
// If there are no watch targets, then we won't get remote snapshots, and are always "up-to-date."
- return [version compare:self.queryCache.lastRemoteSnapshotVersion] != NSOrderedDescending ||
- self.targetIDs.count == 0;
+ return version <= self.queryCache.lastRemoteSnapshotVersion || self.targetIDs.count == 0;
}
-- (BOOL)shouldHoldBatchResultWithVersion:(FSTSnapshotVersion *)version {
+- (BOOL)shouldHoldBatchResultWithVersion:(const SnapshotVersion &)version {
// Check if watcher isn't up to date or prior results are already held.
return ![self isRemoteUpToVersion:version] || self.heldBatchResults.count > 0;
}
-- (FSTDocumentKeySet *)releaseBatchResults:(NSArray<FSTMutationBatchResult *> *)batchResults {
+- (DocumentKeySet)releaseBatchResults:(NSArray<FSTMutationBatchResult *> *)batchResults {
NSMutableArray<FSTMutationBatch *> *batches = [NSMutableArray array];
for (FSTMutationBatchResult *batchResult in batchResults) {
[self applyBatchResult:batchResult];
@@ -487,36 +503,37 @@ NS_ASSUME_NONNULL_BEGIN
return [self removeMutationBatches:batches];
}
-- (FSTDocumentKeySet *)removeMutationBatch:(FSTMutationBatch *)batch {
+- (DocumentKeySet)removeMutationBatch:(FSTMutationBatch *)batch {
return [self removeMutationBatches:@[ batch ]];
}
/** Removes all the mutation batches named in the given array. */
-- (FSTDocumentKeySet *)removeMutationBatches:(NSArray<FSTMutationBatch *> *)batches {
- // TODO(klimt): Could this be an NSMutableDictionary?
- __block FSTDocumentKeySet *affectedDocs = [FSTDocumentKeySet keySet];
-
+- (DocumentKeySet)removeMutationBatches:(NSArray<FSTMutationBatch *> *)batches {
+ DocumentKeySet affectedDocs;
for (FSTMutationBatch *batch in batches) {
for (FSTMutation *mutation in batch.mutations) {
const DocumentKey &key = mutation.key;
- affectedDocs = [affectedDocs setByAddingObject:key];
+ affectedDocs = affectedDocs.insert(key);
}
}
[self.mutationQueue removeMutationBatches:batches];
-
return affectedDocs;
}
- (void)applyBatchResult:(FSTMutationBatchResult *)batchResult {
FSTMutationBatch *batch = batchResult.batch;
- FSTDocumentKeySet *docKeys = batch.keys;
- [docKeys enumerateObjectsUsingBlock:^(FSTDocumentKey *docKey, BOOL *stop) {
+ DocumentKeySet docKeys = batch.keys;
+ const DocumentVersionMap &versions = batchResult.docVersions;
+ for (const DocumentKey &docKey : docKeys) {
FSTMaybeDocument *_Nullable remoteDoc = [self.remoteDocumentCache entryForKey:docKey];
FSTMaybeDocument *_Nullable doc = remoteDoc;
- FSTSnapshotVersion *ackVersion = batchResult.docVersions[docKey];
- FSTAssert(ackVersion, @"docVersions should contain every doc in the write.");
- if (!doc || [doc.version compare:ackVersion] == NSOrderedAscending) {
+
+ auto ackVersionIter = versions.find(docKey);
+ FSTAssert(ackVersionIter != versions.end(),
+ @"docVersions should contain every doc in the write.");
+ const SnapshotVersion &ackVersion = ackVersionIter->second;
+ if (!doc || doc.version < ackVersion) {
doc = [batch applyTo:doc documentKey:docKey mutationBatchResult:batchResult];
if (!doc) {
FSTAssert(!remoteDoc, @"Mutation batch %@ applied to document %@ resulted in nil.", batch,
@@ -525,7 +542,7 @@ NS_ASSUME_NONNULL_BEGIN
[self.remoteDocumentCache addEntry:doc];
}
}
- }];
+ }
}
@end
diff --git a/Firestore/Source/Local/FSTLocalViewChanges.h b/Firestore/Source/Local/FSTLocalViewChanges.h
index eb84642..143d010 100644
--- a/Firestore/Source/Local/FSTLocalViewChanges.h
+++ b/Firestore/Source/Local/FSTLocalViewChanges.h
@@ -16,7 +16,7 @@
#import <Foundation/Foundation.h>
-#import "Firestore/Source/Model/FSTDocumentKeySet.h"
+#include "Firestore/core/src/firebase/firestore/model/document_key_set.h"
@class FSTDocumentSet;
@class FSTMutation;
@@ -34,16 +34,17 @@ NS_ASSUME_NONNULL_BEGIN
@interface FSTLocalViewChanges : NSObject
+ (instancetype)changesForQuery:(FSTQuery *)query
- addedKeys:(FSTDocumentKeySet *)addedKeys
- removedKeys:(FSTDocumentKeySet *)removedKeys;
+ addedKeys:(firebase::firestore::model::DocumentKeySet)addedKeys
+ removedKeys:(firebase::firestore::model::DocumentKeySet)removedKeys;
+ (instancetype)changesForViewSnapshot:(FSTViewSnapshot *)viewSnapshot;
- (id)init NS_UNAVAILABLE;
@property(nonatomic, strong, readonly) FSTQuery *query;
-@property(nonatomic, strong) FSTDocumentKeySet *addedKeys;
-@property(nonatomic, strong) FSTDocumentKeySet *removedKeys;
+
+- (const firebase::firestore::model::DocumentKeySet &)addedKeys;
+- (const firebase::firestore::model::DocumentKeySet &)removedKeys;
@end
diff --git a/Firestore/Source/Local/FSTLocalViewChanges.mm b/Firestore/Source/Local/FSTLocalViewChanges.mm
index 9a7f445..eb6b259 100644
--- a/Firestore/Source/Local/FSTLocalViewChanges.mm
+++ b/Firestore/Source/Local/FSTLocalViewChanges.mm
@@ -16,31 +16,38 @@
#import "Firestore/Source/Local/FSTLocalViewChanges.h"
+#include <utility>
+
#import "Firestore/Source/Core/FSTViewSnapshot.h"
#import "Firestore/Source/Model/FSTDocument.h"
+using firebase::firestore::model::DocumentKeySet;
+
NS_ASSUME_NONNULL_BEGIN
@interface FSTLocalViewChanges ()
- (instancetype)initWithQuery:(FSTQuery *)query
- addedKeys:(FSTDocumentKeySet *)addedKeys
- removedKeys:(FSTDocumentKeySet *)removedKeys NS_DESIGNATED_INITIALIZER;
+ addedKeys:(DocumentKeySet)addedKeys
+ removedKeys:(DocumentKeySet)removedKeys NS_DESIGNATED_INITIALIZER;
@end
-@implementation FSTLocalViewChanges
+@implementation FSTLocalViewChanges {
+ DocumentKeySet _addedKeys;
+ DocumentKeySet _removedKeys;
+}
+ (instancetype)changesForViewSnapshot:(FSTViewSnapshot *)viewSnapshot {
- FSTDocumentKeySet *addedKeys = [FSTDocumentKeySet keySet];
- FSTDocumentKeySet *removedKeys = [FSTDocumentKeySet keySet];
+ DocumentKeySet addedKeys;
+ DocumentKeySet removedKeys;
for (FSTDocumentViewChange *docChange in viewSnapshot.documentChanges) {
switch (docChange.type) {
case FSTDocumentViewChangeTypeAdded:
- addedKeys = [addedKeys setByAddingObject:docChange.document.key];
+ addedKeys = addedKeys.insert(docChange.document.key);
break;
case FSTDocumentViewChangeTypeRemoved:
- removedKeys = [removedKeys setByAddingObject:docChange.document.key];
+ removedKeys = removedKeys.insert(docChange.document.key);
break;
default:
@@ -49,28 +56,39 @@ NS_ASSUME_NONNULL_BEGIN
}
}
- return [self changesForQuery:viewSnapshot.query addedKeys:addedKeys removedKeys:removedKeys];
+ return [self changesForQuery:viewSnapshot.query
+ addedKeys:std::move(addedKeys)
+ removedKeys:std::move(removedKeys)];
}
+ (instancetype)changesForQuery:(FSTQuery *)query
- addedKeys:(FSTDocumentKeySet *)addedKeys
- removedKeys:(FSTDocumentKeySet *)removedKeys {
- return
- [[FSTLocalViewChanges alloc] initWithQuery:query addedKeys:addedKeys removedKeys:removedKeys];
+ addedKeys:(DocumentKeySet)addedKeys
+ removedKeys:(DocumentKeySet)removedKeys {
+ return [[FSTLocalViewChanges alloc] initWithQuery:query
+ addedKeys:std::move(addedKeys)
+ removedKeys:std::move(removedKeys)];
}
- (instancetype)initWithQuery:(FSTQuery *)query
- addedKeys:(FSTDocumentKeySet *)addedKeys
- removedKeys:(FSTDocumentKeySet *)removedKeys {
+ addedKeys:(DocumentKeySet)addedKeys
+ removedKeys:(DocumentKeySet)removedKeys {
self = [super init];
if (self) {
_query = query;
- _addedKeys = addedKeys;
- _removedKeys = removedKeys;
+ _addedKeys = std::move(addedKeys);
+ _removedKeys = std::move(removedKeys);
}
return self;
}
+- (const DocumentKeySet &)addedKeys {
+ return _addedKeys;
+}
+
+- (const DocumentKeySet &)removedKeys {
+ return _removedKeys;
+}
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Local/FSTMemoryMutationQueue.h b/Firestore/Source/Local/FSTMemoryMutationQueue.h
index f0786cc..fd46a6e 100644
--- a/Firestore/Source/Local/FSTMemoryMutationQueue.h
+++ b/Firestore/Source/Local/FSTMemoryMutationQueue.h
@@ -16,6 +16,7 @@
#import <Foundation/Foundation.h>
+#import "Firestore/Source/Local/FSTMemoryPersistence.h"
#import "Firestore/Source/Local/FSTMutationQueue.h"
@protocol FSTGarbageCollector;
@@ -24,7 +25,9 @@ NS_ASSUME_NONNULL_BEGIN
@interface FSTMemoryMutationQueue : NSObject <FSTMutationQueue>
-+ (instancetype)mutationQueue;
+- (instancetype)initWithPersistence:(FSTMemoryPersistence *)persistence NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
/** The garbage collector to notify about potential garbage keys. */
@property(nonatomic, weak, readwrite, nullable) id<FSTGarbageCollector> garbageCollector;
diff --git a/Firestore/Source/Local/FSTMemoryMutationQueue.mm b/Firestore/Source/Local/FSTMemoryMutationQueue.mm
index 8028bb3..e05ee64 100644
--- a/Firestore/Source/Local/FSTMemoryMutationQueue.mm
+++ b/Firestore/Source/Local/FSTMemoryMutationQueue.mm
@@ -18,9 +18,11 @@
#import "Firestore/Source/Core/FSTQuery.h"
#import "Firestore/Source/Local/FSTDocumentReference.h"
+#import "Firestore/Source/Local/FSTMemoryPersistence.h"
#import "Firestore/Source/Model/FSTMutation.h"
#import "Firestore/Source/Model/FSTMutationBatch.h"
#import "Firestore/Source/Util/FSTAssert.h"
+#import "Firestore/third_party/Immutable/FSTImmutableSortedSet.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
#include "Firestore/core/src/firebase/firestore/model/resource_path.h"
@@ -72,14 +74,13 @@ static const NSComparator NumberComparator = ^NSComparisonResult(NSNumber *left,
@end
-@implementation FSTMemoryMutationQueue
-
-+ (instancetype)mutationQueue {
- return [[FSTMemoryMutationQueue alloc] init];
+@implementation FSTMemoryMutationQueue {
+ FSTMemoryPersistence *_persistence;
}
-- (instancetype)init {
+- (instancetype)initWithPersistence:(FSTMemoryPersistence *)persistence {
if (self = [super init]) {
+ _persistence = persistence;
_queue = [NSMutableArray array];
_batchesByDocumentKey =
[FSTImmutableSortedSet setWithComparator:FSTDocumentReferenceComparatorByKey];
@@ -347,6 +348,7 @@ static const NSComparator NumberComparator = ^NSComparisonResult(NSNumber *left,
for (FSTMutation *mutation in batch.mutations) {
const DocumentKey &key = mutation.key;
[garbageCollector addPotentialGarbageKey:key];
+ [_persistence.referenceDelegate removeMutationReference:key];
FSTDocumentReference *reference = [[FSTDocumentReference alloc] initWithKey:key ID:batchID];
references = [references setByRemovingObject:reference];
diff --git a/Firestore/Source/Local/FSTMemoryPersistence.mm b/Firestore/Source/Local/FSTMemoryPersistence.mm
index 8d74881..3466f3e 100644
--- a/Firestore/Source/Local/FSTMemoryPersistence.mm
+++ b/Firestore/Source/Local/FSTMemoryPersistence.mm
@@ -59,7 +59,7 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)init {
if (self = [super init]) {
- _queryCache = [[FSTMemoryQueryCache alloc] init];
+ _queryCache = [[FSTMemoryQueryCache alloc] initWithPersistence:self];
_remoteDocumentCache = [[FSTMemoryRemoteDocumentCache alloc] init];
}
return self;
@@ -78,6 +78,10 @@ NS_ASSUME_NONNULL_BEGIN
self.started = NO;
}
+- (_Nullable id<FSTReferenceDelegate>)referenceDelegate {
+ return nil;
+}
+
- (const FSTTransactionRunner &)run {
return _transactionRunner;
}
@@ -85,7 +89,7 @@ NS_ASSUME_NONNULL_BEGIN
- (id<FSTMutationQueue>)mutationQueueForUser:(const User &)user {
id<FSTMutationQueue> queue = _mutationQueues[user];
if (!queue) {
- queue = [FSTMemoryMutationQueue mutationQueue];
+ queue = [[FSTMemoryMutationQueue alloc] initWithPersistence:self];
_mutationQueues[user] = queue;
}
return queue;
diff --git a/Firestore/Source/Local/FSTMemoryQueryCache.h b/Firestore/Source/Local/FSTMemoryQueryCache.h
index 98f0277..126ce59 100644
--- a/Firestore/Source/Local/FSTMemoryQueryCache.h
+++ b/Firestore/Source/Local/FSTMemoryQueryCache.h
@@ -20,11 +20,18 @@
NS_ASSUME_NONNULL_BEGIN
+@class FSTMemoryPersistence;
+
/**
* An implementation of the FSTQueryCache protocol that merely keeps queries in memory, suitable
* for online only clients with persistence disabled.
*/
@interface FSTMemoryQueryCache : NSObject <FSTQueryCache>
+
+- (instancetype)initWithPersistence:(FSTMemoryPersistence *)persistence NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Local/FSTMemoryQueryCache.mm b/Firestore/Source/Local/FSTMemoryQueryCache.mm
index 18d14f2..2eba4f6 100644
--- a/Firestore/Source/Local/FSTMemoryQueryCache.mm
+++ b/Firestore/Source/Local/FSTMemoryQueryCache.mm
@@ -16,12 +16,19 @@
#import "Firestore/Source/Local/FSTMemoryQueryCache.h"
+#include <utility>
+
#import "Firestore/Source/Core/FSTQuery.h"
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
+#import "Firestore/Source/Local/FSTMemoryPersistence.h"
#import "Firestore/Source/Local/FSTQueryData.h"
#import "Firestore/Source/Local/FSTReferenceSet.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
+
+using firebase::firestore::model::SnapshotVersion;
+using firebase::firestore::model::DocumentKeySet;
+using firebase::firestore::model::DocumentKey;
NS_ASSUME_NONNULL_BEGIN
@@ -38,18 +45,20 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic, assign) FSTListenSequenceNumber highestListenSequenceNumber;
-/** The last received snapshot version. */
-@property(nonatomic, strong) FSTSnapshotVersion *lastRemoteSnapshotVersion;
-
@end
-@implementation FSTMemoryQueryCache
+@implementation FSTMemoryQueryCache {
+ FSTMemoryPersistence *_persistence;
+ /** The last received snapshot version. */
+ SnapshotVersion _lastRemoteSnapshotVersion;
+}
-- (instancetype)init {
+- (instancetype)initWithPersistence:(FSTMemoryPersistence *)persistence {
if (self = [super init]) {
+ _persistence = persistence;
_queries = [NSMutableDictionary dictionary];
_references = [[FSTReferenceSet alloc] init];
- _lastRemoteSnapshotVersion = [FSTSnapshotVersion noVersion];
+ _lastRemoteSnapshotVersion = SnapshotVersion::None();
}
return self;
}
@@ -69,14 +78,13 @@ NS_ASSUME_NONNULL_BEGIN
return _highestListenSequenceNumber;
}
-/*- (FSTSnapshotVersion *)lastRemoteSnapshotVersion {
+- (const SnapshotVersion &)lastRemoteSnapshotVersion {
return _lastRemoteSnapshotVersion;
}
-- (void)setLastRemoteSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
- group:(FSTWriteGroup *)group {
- _lastRemoteSnapshotVersion = snapshotVersion;
-}*/
+- (void)setLastRemoteSnapshotVersion:(SnapshotVersion)snapshotVersion {
+ _lastRemoteSnapshotVersion = std::move(snapshotVersion);
+}
- (void)addQueryData:(FSTQueryData *)queryData {
self.queries[queryData.query] = queryData;
@@ -113,19 +121,25 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark Reference tracking
-- (void)addMatchingKeys:(FSTDocumentKeySet *)keys forTargetID:(FSTTargetID)targetID {
+- (void)addMatchingKeys:(const DocumentKeySet &)keys forTargetID:(FSTTargetID)targetID {
[self.references addReferencesToKeys:keys forID:targetID];
+ for (const DocumentKey &key : keys) {
+ [_persistence.referenceDelegate addReference:key target:targetID];
+ }
}
-- (void)removeMatchingKeys:(FSTDocumentKeySet *)keys forTargetID:(FSTTargetID)targetID {
+- (void)removeMatchingKeys:(const DocumentKeySet &)keys forTargetID:(FSTTargetID)targetID {
[self.references removeReferencesToKeys:keys forID:targetID];
+ for (const DocumentKey &key : keys) {
+ [_persistence.referenceDelegate removeReference:key target:targetID];
+ }
}
- (void)removeMatchingKeysForTargetID:(FSTTargetID)targetID {
[self.references removeReferencesForID:targetID];
}
-- (FSTDocumentKeySet *)matchingKeysForTargetID:(FSTTargetID)targetID {
+- (DocumentKeySet)matchingKeysForTargetID:(FSTTargetID)targetID {
return [self.references referencedKeysForID:targetID];
}
diff --git a/Firestore/Source/Local/FSTPersistence.h b/Firestore/Source/Local/FSTPersistence.h
index 2294ef1..417ff3f 100644
--- a/Firestore/Source/Local/FSTPersistence.h
+++ b/Firestore/Source/Local/FSTPersistence.h
@@ -16,12 +16,16 @@
#import <Foundation/Foundation.h>
+#import "Firestore/Source/Core/FSTTypes.h"
#import "Firestore/Source/Util/FSTAssert.h"
#include "Firestore/core/src/firebase/firestore/auth/user.h"
+@class FSTDocumentKey;
@protocol FSTMutationQueue;
@protocol FSTQueryCache;
+@class FSTQueryData;
@protocol FSTRemoteDocumentCache;
+@class FSTReferenceSet;
NS_ASSUME_NONNULL_BEGIN
@@ -56,6 +60,7 @@ NS_ASSUME_NONNULL_BEGIN
* of its reads and writes in order to avoid relying on being able to read back uncommitted writes.
*/
struct FSTTransactionRunner;
+@protocol FSTReferenceDelegate;
@protocol FSTPersistence <NSObject>
/**
@@ -87,6 +92,12 @@ struct FSTTransactionRunner;
@property(nonatomic, readonly, assign) const FSTTransactionRunner &run;
+/**
+ * This property provides access to hooks around the document reference lifecycle. It is initially
+ * nullable while being implemented, but the goal is to eventually have it be non-nil.
+ */
+@property(nonatomic, readonly, strong) _Nullable id<FSTReferenceDelegate> referenceDelegate;
+
@end
@protocol FSTTransactional
@@ -97,6 +108,52 @@ struct FSTTransactionRunner;
@end
+/**
+ * An FSTReferenceDelegate instance handles all of the hooks into the document-reference lifecycle.
+ * This includes being added to a target, being removed from a target, being subject to mutation,
+ * and being mutated by the user.
+ *
+ * Different implementations may do different things with each of these events. Not every
+ * implementation needs to do something with every lifecycle hook.
+ *
+ * Implementations that care about sequence numbers are responsible for generating them and making
+ * them available.
+ */
+@protocol FSTReferenceDelegate
+
+/**
+ * Registers an FSTReferenceSet of documents that should be considered 'referenced' and not eligible
+ * for removal during garbage collection.
+ */
+- (void)addInMemoryPins:(FSTReferenceSet *)set;
+
+/**
+ * Notify the delegate that a target was removed.
+ */
+- (void)removeTarget:(FSTQueryData *)queryData;
+
+/**
+ * Notify the delegate that the given document was added to the given target.
+ */
+- (void)addReference:(FSTDocumentKey *)key target:(FSTTargetID)targetID;
+
+/**
+ * Notify the delegate that the given document was removed from the given target.
+ */
+- (void)removeReference:(FSTDocumentKey *)key target:(FSTTargetID)targetID;
+
+/**
+ * Notify the delegate that a document is no longer being mutated by the user.
+ */
+- (void)removeMutationReference:(FSTDocumentKey *)key;
+
+/**
+ * Notify the delegate that a limbo document was updated.
+ */
+- (void)limboDocumentUpdated:(FSTDocumentKey *)key;
+
+@end
+
struct FSTTransactionRunner {
// Intentionally disable nullability checking for this function. We cannot properly annotate
// the function because this function can handle both pointer and non-pointer types. It is an error
diff --git a/Firestore/Source/Local/FSTQueryCache.h b/Firestore/Source/Local/FSTQueryCache.h
index d797d49..1ad46aa 100644
--- a/Firestore/Source/Local/FSTQueryCache.h
+++ b/Firestore/Source/Local/FSTQueryCache.h
@@ -18,13 +18,14 @@
#import "Firestore/Source/Core/FSTTypes.h"
#import "Firestore/Source/Local/FSTGarbageCollector.h"
-#import "Firestore/Source/Model/FSTDocumentKeySet.h"
+
+#include "Firestore/core/src/firebase/firestore/model/document_key_set.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
@class FSTDocumentSet;
@class FSTMaybeDocument;
@class FSTQuery;
@class FSTQueryData;
-@class FSTSnapshotVersion;
NS_ASSUME_NONNULL_BEGIN
@@ -61,7 +62,7 @@ NS_ASSUME_NONNULL_BEGIN
*
* This is updated whenever our we get a TargetChange with a read_time and empty target_ids.
*/
-- (FSTSnapshotVersion *)lastRemoteSnapshotVersion;
+- (const firebase::firestore::model::SnapshotVersion &)lastRemoteSnapshotVersion;
/**
* Set the snapshot version representing the last consistent snapshot received from the backend.
@@ -69,7 +70,7 @@ NS_ASSUME_NONNULL_BEGIN
*
* @param snapshotVersion The new snapshot version.
*/
-- (void)setLastRemoteSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion;
+- (void)setLastRemoteSnapshotVersion:(firebase::firestore::model::SnapshotVersion)snapshotVersion;
/**
* Adds an entry in the cache.
@@ -104,15 +105,17 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable FSTQueryData *)queryDataForQuery:(FSTQuery *)query;
/** Adds the given document keys to cached query results of the given target ID. */
-- (void)addMatchingKeys:(FSTDocumentKeySet *)keys forTargetID:(FSTTargetID)targetID;
+- (void)addMatchingKeys:(const firebase::firestore::model::DocumentKeySet &)keys
+ forTargetID:(FSTTargetID)targetID;
/** Removes the given document keys from the cached query results of the given target ID. */
-- (void)removeMatchingKeys:(FSTDocumentKeySet *)keys forTargetID:(FSTTargetID)targetID;
+- (void)removeMatchingKeys:(const firebase::firestore::model::DocumentKeySet &)keys
+ forTargetID:(FSTTargetID)targetID;
/** Removes all the keys in the query results of the given target ID. */
- (void)removeMatchingKeysForTargetID:(FSTTargetID)targetID;
-- (FSTDocumentKeySet *)matchingKeysForTargetID:(FSTTargetID)targetID;
+- (firebase::firestore::model::DocumentKeySet)matchingKeysForTargetID:(FSTTargetID)targetID;
@end
diff --git a/Firestore/Source/Local/FSTQueryData.h b/Firestore/Source/Local/FSTQueryData.h
index 5db2de6..bde0a15 100644
--- a/Firestore/Source/Local/FSTQueryData.h
+++ b/Firestore/Source/Local/FSTQueryData.h
@@ -18,8 +18,9 @@
#import "Firestore/Source/Core/FSTTypes.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
+
@class FSTQuery;
-@class FSTSnapshotVersion;
NS_ASSUME_NONNULL_BEGIN
@@ -42,7 +43,7 @@ typedef NS_ENUM(NSInteger, FSTQueryPurpose) {
targetID:(FSTTargetID)targetID
listenSequenceNumber:(FSTListenSequenceNumber)sequenceNumber
purpose:(FSTQueryPurpose)purpose
- snapshotVersion:(FSTSnapshotVersion *)snapshotVersion
+ snapshotVersion:(firebase::firestore::model::SnapshotVersion)snapshotVersion
resumeToken:(NSData *)resumeToken NS_DESIGNATED_INITIALIZER;
/** Convenience initializer for use when creating an FSTQueryData for the first time. */
@@ -53,9 +54,17 @@ typedef NS_ENUM(NSInteger, FSTQueryPurpose) {
- (instancetype)init NS_UNAVAILABLE;
-/** Creates a new query data instance with an updated snapshot version and resume token. */
-- (instancetype)queryDataByReplacingSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
- resumeToken:(NSData *)resumeToken;
+/**
+ * Creates a new query data instance with an updated snapshot version, resume token, and sequence
+ * number.
+ */
+- (instancetype)queryDataByReplacingSnapshotVersion:
+ (firebase::firestore::model::SnapshotVersion)snapshotVersion
+ resumeToken:(NSData *)resumeToken
+ sequenceNumber:(FSTListenSequenceNumber)sequenceNumber;
+
+/** The latest snapshot version seen for this target. */
+- (const firebase::firestore::model::SnapshotVersion &)snapshotVersion;
/** The query being listened to. */
@property(nonatomic, strong, readonly) FSTQuery *query;
@@ -71,9 +80,6 @@ typedef NS_ENUM(NSInteger, FSTQueryPurpose) {
/** The purpose of the query. */
@property(nonatomic, assign, readonly) FSTQueryPurpose purpose;
-/** The latest snapshot version seen for this target. */
-@property(nonatomic, strong, readonly) FSTSnapshotVersion *snapshotVersion;
-
/**
* An opaque, server-assigned token that allows watching a query to be resumed after disconnecting
* without retransmitting all the data that matches the query. The resume token essentially
diff --git a/Firestore/Source/Local/FSTQueryData.mm b/Firestore/Source/Local/FSTQueryData.mm
index 6bb716a..16c3b2e 100644
--- a/Firestore/Source/Local/FSTQueryData.mm
+++ b/Firestore/Source/Local/FSTQueryData.mm
@@ -16,18 +16,25 @@
#import "Firestore/Source/Local/FSTQueryData.h"
+#include <utility>
+
#import "Firestore/Source/Core/FSTQuery.h"
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
+
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
+
+using firebase::firestore::model::SnapshotVersion;
NS_ASSUME_NONNULL_BEGIN
-@implementation FSTQueryData
+@implementation FSTQueryData {
+ SnapshotVersion _snapshotVersion;
+}
- (instancetype)initWithQuery:(FSTQuery *)query
targetID:(FSTTargetID)targetID
listenSequenceNumber:(FSTListenSequenceNumber)sequenceNumber
purpose:(FSTQueryPurpose)purpose
- snapshotVersion:(FSTSnapshotVersion *)snapshotVersion
+ snapshotVersion:(SnapshotVersion)snapshotVersion
resumeToken:(NSData *)resumeToken {
self = [super init];
if (self) {
@@ -35,7 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
_targetID = targetID;
_sequenceNumber = sequenceNumber;
_purpose = purpose;
- _snapshotVersion = snapshotVersion;
+ _snapshotVersion = std::move(snapshotVersion);
_resumeToken = [resumeToken copy];
}
return self;
@@ -49,10 +56,14 @@ NS_ASSUME_NONNULL_BEGIN
targetID:targetID
listenSequenceNumber:sequenceNumber
purpose:purpose
- snapshotVersion:[FSTSnapshotVersion noVersion]
+ snapshotVersion:SnapshotVersion::None()
resumeToken:[NSData data]];
}
+- (const firebase::firestore::model::SnapshotVersion &)snapshotVersion {
+ return _snapshotVersion;
+}
+
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
@@ -63,7 +74,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTQueryData *other = (FSTQueryData *)object;
return [self.query isEqual:other.query] && self.targetID == other.targetID &&
- self.purpose == other.purpose && [self.snapshotVersion isEqual:other.snapshotVersion] &&
+ self.purpose == other.purpose && self.snapshotVersion == other.snapshotVersion &&
[self.resumeToken isEqual:other.resumeToken];
}
@@ -71,25 +82,26 @@ NS_ASSUME_NONNULL_BEGIN
NSUInteger result = [self.query hash];
result = result * 31 + self.targetID;
result = result * 31 + self.purpose;
- result = result * 31 + [self.snapshotVersion hash];
+ result = result * 31 + self.snapshotVersion.Hash();
result = result * 31 + [self.resumeToken hash];
return result;
}
- (NSString *)description {
return [NSString
- stringWithFormat:@"<FSTQueryData: query:%@ target:%d purpose:%lu version:%@ resumeToken:%@)>",
- self.query, self.targetID, (unsigned long)self.purpose, self.snapshotVersion,
- self.resumeToken];
+ stringWithFormat:@"<FSTQueryData: query:%@ target:%d purpose:%lu version:%s resumeToken:%@)>",
+ self.query, self.targetID, (unsigned long)self.purpose,
+ self.snapshotVersion.timestamp().ToString().c_str(), self.resumeToken];
}
-- (instancetype)queryDataByReplacingSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
- resumeToken:(NSData *)resumeToken {
+- (instancetype)queryDataByReplacingSnapshotVersion:(SnapshotVersion)snapshotVersion
+ resumeToken:(NSData *)resumeToken
+ sequenceNumber:(FSTListenSequenceNumber)sequenceNumber {
return [[FSTQueryData alloc] initWithQuery:self.query
targetID:self.targetID
- listenSequenceNumber:self.sequenceNumber
+ listenSequenceNumber:sequenceNumber
purpose:self.purpose
- snapshotVersion:snapshotVersion
+ snapshotVersion:std::move(snapshotVersion)
resumeToken:resumeToken];
}
diff --git a/Firestore/Source/Local/FSTReferenceSet.h b/Firestore/Source/Local/FSTReferenceSet.h
index 9d842cb..9a90a40 100644
--- a/Firestore/Source/Local/FSTReferenceSet.h
+++ b/Firestore/Source/Local/FSTReferenceSet.h
@@ -18,7 +18,8 @@
#import "Firestore/Source/Core/FSTTypes.h"
#import "Firestore/Source/Local/FSTGarbageCollector.h"
-#import "Firestore/Source/Model/FSTDocumentKeySet.h"
+
+#include "Firestore/core/src/firebase/firestore/model/document_key_set.h"
NS_ASSUME_NONNULL_BEGIN
@@ -47,13 +48,14 @@ NS_ASSUME_NONNULL_BEGIN
- (void)addReferenceToKey:(const firebase::firestore::model::DocumentKey &)key forID:(int)ID;
/** Add references to the given document keys for the given ID. */
-- (void)addReferencesToKeys:(FSTDocumentKeySet *)keys forID:(int)ID;
+- (void)addReferencesToKeys:(const firebase::firestore::model::DocumentKeySet &)keys forID:(int)ID;
/** Removes a reference to the given document key for the given ID. */
- (void)removeReferenceToKey:(const firebase::firestore::model::DocumentKey &)key forID:(int)ID;
/** Removes references to the given document keys for the given ID. */
-- (void)removeReferencesToKeys:(FSTDocumentKeySet *)keys forID:(int)ID;
+- (void)removeReferencesToKeys:(const firebase::firestore::model::DocumentKeySet &)keys
+ forID:(int)ID;
/** Clears all references with a given ID. Calls -removeReferenceToKey: for each key removed. */
- (void)removeReferencesForID:(int)ID;
@@ -62,7 +64,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)removeAllReferences;
/** Returns all of the document keys that have had references added for the given ID. */
-- (FSTDocumentKeySet *)referencedKeysForID:(int)ID;
+- (firebase::firestore::model::DocumentKeySet)referencedKeysForID:(int)ID;
@end
diff --git a/Firestore/Source/Local/FSTReferenceSet.mm b/Firestore/Source/Local/FSTReferenceSet.mm
index 14f5d47..6b34725 100644
--- a/Firestore/Source/Local/FSTReferenceSet.mm
+++ b/Firestore/Source/Local/FSTReferenceSet.mm
@@ -17,10 +17,12 @@
#import "Firestore/Source/Local/FSTReferenceSet.h"
#import "Firestore/Source/Local/FSTDocumentReference.h"
+#import "Firestore/third_party/Immutable/FSTImmutableSortedSet.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
using firebase::firestore::model::DocumentKey;
+using firebase::firestore::model::DocumentKeySet;
NS_ASSUME_NONNULL_BEGIN
@@ -68,20 +70,20 @@ NS_ASSUME_NONNULL_BEGIN
self.referencesByID = [self.referencesByID setByAddingObject:reference];
}
-- (void)addReferencesToKeys:(FSTDocumentKeySet *)keys forID:(int)ID {
- [keys enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) {
+- (void)addReferencesToKeys:(const DocumentKeySet &)keys forID:(int)ID {
+ for (const DocumentKey &key : keys) {
[self addReferenceToKey:key forID:ID];
- }];
+ }
}
- (void)removeReferenceToKey:(const DocumentKey &)key forID:(int)ID {
[self removeReference:[[FSTDocumentReference alloc] initWithKey:key ID:ID]];
}
-- (void)removeReferencesToKeys:(FSTDocumentKeySet *)keys forID:(int)ID {
- [keys enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) {
+- (void)removeReferencesToKeys:(const DocumentKeySet &)keys forID:(int)ID {
+ for (const DocumentKey &key : keys) {
[self removeReferenceToKey:key forID:ID];
- }];
+ }
}
- (void)removeReferencesForID:(int)ID {
@@ -109,17 +111,17 @@ NS_ASSUME_NONNULL_BEGIN
[self.garbageCollector addPotentialGarbageKey:reference.key];
}
-- (FSTDocumentKeySet *)referencedKeysForID:(int)ID {
+- (DocumentKeySet)referencedKeysForID:(int)ID {
FSTDocumentReference *start =
[[FSTDocumentReference alloc] initWithKey:DocumentKey::Empty() ID:ID];
FSTDocumentReference *end =
[[FSTDocumentReference alloc] initWithKey:DocumentKey::Empty() ID:(ID + 1)];
- __block FSTDocumentKeySet *keys = [FSTDocumentKeySet keySet];
+ __block DocumentKeySet keys;
[self.referencesByID enumerateObjectsFrom:start
to:end
usingBlock:^(FSTDocumentReference *reference, BOOL *stop) {
- keys = [keys setByAddingObject:reference.key];
+ keys = keys.insert(reference.key);
}];
return keys;
}
diff --git a/Firestore/Source/Model/FSTDocument.h b/Firestore/Source/Model/FSTDocument.h
index 47e4d28..0f8d4b3 100644
--- a/Firestore/Source/Model/FSTDocument.h
+++ b/Firestore/Source/Model/FSTDocument.h
@@ -18,10 +18,10 @@
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
#include "Firestore/core/src/firebase/firestore/model/field_path.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
@class FSTFieldValue;
@class FSTObjectValue;
-@class FSTSnapshotVersion;
NS_ASSUME_NONNULL_BEGIN
@@ -32,14 +32,13 @@ NS_ASSUME_NONNULL_BEGIN
@interface FSTMaybeDocument : NSObject <NSCopying>
- (id)init __attribute__((unavailable("Abstract base class")));
- (const firebase::firestore::model::DocumentKey &)key;
-
-@property(nonatomic, readonly) FSTSnapshotVersion *version;
+- (const firebase::firestore::model::SnapshotVersion &)version;
@end
@interface FSTDocument : FSTMaybeDocument
+ (instancetype)documentWithData:(FSTObjectValue *)data
key:(firebase::firestore::model::DocumentKey)key
- version:(FSTSnapshotVersion *)version
+ version:(firebase::firestore::model::SnapshotVersion)version
hasLocalMutations:(BOOL)mutations;
- (nullable FSTFieldValue *)fieldForPath:(const firebase::firestore::model::FieldPath &)path;
@@ -51,7 +50,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface FSTDeletedDocument : FSTMaybeDocument
+ (instancetype)documentWithKey:(firebase::firestore::model::DocumentKey)key
- version:(FSTSnapshotVersion *)version;
+ version:(firebase::firestore::model::SnapshotVersion)version;
@end
/** An NSComparator suitable for comparing docs using only their keys. */
diff --git a/Firestore/Source/Model/FSTDocument.mm b/Firestore/Source/Model/FSTDocument.mm
index 9898c2a..8c4c801 100644
--- a/Firestore/Source/Model/FSTDocument.mm
+++ b/Firestore/Source/Model/FSTDocument.mm
@@ -18,37 +18,38 @@
#include <utility>
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
#import "Firestore/Source/Model/FSTFieldValue.h"
#import "Firestore/Source/Util/FSTAssert.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
#include "Firestore/core/src/firebase/firestore/model/field_path.h"
+#include "Firestore/core/src/firebase/firestore/util/hashing.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
namespace util = firebase::firestore::util;
using firebase::firestore::model::DocumentKey;
using firebase::firestore::model::FieldPath;
+using firebase::firestore::model::SnapshotVersion;
NS_ASSUME_NONNULL_BEGIN
@interface FSTMaybeDocument ()
- (instancetype)initWithKey:(DocumentKey)key
- version:(FSTSnapshotVersion *)version NS_DESIGNATED_INITIALIZER;
+ version:(SnapshotVersion)version NS_DESIGNATED_INITIALIZER;
@end
@implementation FSTMaybeDocument {
DocumentKey _key;
+ SnapshotVersion _version;
}
-- (instancetype)initWithKey:(DocumentKey)key version:(FSTSnapshotVersion *)version {
- FSTAssert(!!version, @"Version must not be nil.");
+- (instancetype)initWithKey:(DocumentKey)key version:(SnapshotVersion)version {
self = [super init];
if (self) {
_key = std::move(key);
- _version = version;
+ _version = std::move(version);
}
return self;
}
@@ -62,25 +63,29 @@ NS_ASSUME_NONNULL_BEGIN
return _key;
}
+- (const SnapshotVersion &)version {
+ return _version;
+}
+
@end
@implementation FSTDocument
+ (instancetype)documentWithData:(FSTObjectValue *)data
key:(DocumentKey)key
- version:(FSTSnapshotVersion *)version
+ version:(SnapshotVersion)version
hasLocalMutations:(BOOL)mutations {
return [[FSTDocument alloc] initWithData:data
key:std::move(key)
- version:version
+ version:std::move(version)
hasLocalMutations:mutations];
}
- (instancetype)initWithData:(FSTObjectValue *)data
key:(DocumentKey)key
- version:(FSTSnapshotVersion *)version
+ version:(SnapshotVersion)version
hasLocalMutations:(BOOL)mutations {
- self = [super initWithKey:std::move(key) version:version];
+ self = [super initWithKey:std::move(key) version:std::move(version)];
if (self) {
_data = data;
_localMutations = mutations;
@@ -97,21 +102,22 @@ NS_ASSUME_NONNULL_BEGIN
}
FSTDocument *otherDoc = other;
- return [self.key isEqual:otherDoc.key] && [self.version isEqual:otherDoc.version] &&
+ return [self.key isEqual:otherDoc.key] && self.version == otherDoc.version &&
[self.data isEqual:otherDoc.data] && self.hasLocalMutations == otherDoc.hasLocalMutations;
}
- (NSUInteger)hash {
NSUInteger result = [self.key hash];
- result = result * 31 + [self.version hash];
+ result = result * 31 + self.version.Hash();
result = result * 31 + [self.data hash];
result = result * 31 + (self.hasLocalMutations ? 1 : 0);
return result;
}
- (NSString *)description {
- return [NSString stringWithFormat:@"<FSTDocument: key:%s version:%@ localMutations:%@ data:%@>",
- self.key.ToString().c_str(), self.version,
+ return [NSString stringWithFormat:@"<FSTDocument: key:%s version:%s localMutations:%@ data:%@>",
+ self.key.ToString().c_str(),
+ self.version.timestamp().ToString().c_str(),
self.localMutations ? @"YES" : @"NO", self.data];
}
@@ -123,8 +129,8 @@ NS_ASSUME_NONNULL_BEGIN
@implementation FSTDeletedDocument
-+ (instancetype)documentWithKey:(DocumentKey)key version:(FSTSnapshotVersion *)version {
- return [[FSTDeletedDocument alloc] initWithKey:std::move(key) version:version];
++ (instancetype)documentWithKey:(DocumentKey)key version:(SnapshotVersion)version {
+ return [[FSTDeletedDocument alloc] initWithKey:std::move(key) version:std::move(version)];
}
- (BOOL)isEqual:(id)other {
@@ -136,12 +142,12 @@ NS_ASSUME_NONNULL_BEGIN
}
FSTDocument *otherDoc = other;
- return [self.key isEqual:otherDoc.key] && [self.version isEqual:otherDoc.version];
+ return [self.key isEqual:otherDoc.key] && self.version == otherDoc.version;
}
- (NSUInteger)hash {
NSUInteger result = [self.key hash];
- result = result * 31 + [self.version hash];
+ result = result * 31 + self.version.Hash();
return result;
}
diff --git a/Firestore/Source/Model/FSTDocumentKey.mm b/Firestore/Source/Model/FSTDocumentKey.mm
index 679d7a6..d29df86 100644
--- a/Firestore/Source/Model/FSTDocumentKey.mm
+++ b/Firestore/Source/Model/FSTDocumentKey.mm
@@ -23,6 +23,7 @@
#import "Firestore/Source/Util/FSTAssert.h"
#include "Firestore/core/src/firebase/firestore/model/resource_path.h"
+#include "Firestore/core/src/firebase/firestore/util/hashing.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
namespace util = firebase::firestore::util;
@@ -72,7 +73,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (NSUInteger)hash {
- return _path.Hash();
+ return util::Hash(_path);
}
- (NSString *)description {
diff --git a/Firestore/Source/Model/FSTDocumentKeySet.h b/Firestore/Source/Model/FSTDocumentKeySet.h
deleted file mode 100644
index 80f6624..0000000
--- a/Firestore/Source/Model/FSTDocumentKeySet.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <Foundation/Foundation.h>
-
-#import "Firestore/third_party/Immutable/FSTImmutableSortedSet.h"
-
-@class FSTDocumentKey;
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** Convenience type for a set of keys, since they are so common. */
-typedef FSTImmutableSortedSet<FSTDocumentKey *> FSTDocumentKeySet;
-
-@interface FSTImmutableSortedSet (FSTDocumentKey)
-
-/** Returns a new set using the DocumentKeyComparator. */
-+ (FSTDocumentKeySet *)keySet;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Model/FSTDocumentKeySet.mm b/Firestore/Source/Model/FSTDocumentKeySet.mm
deleted file mode 100644
index f07b785..0000000
--- a/Firestore/Source/Model/FSTDocumentKeySet.mm
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import "Firestore/Source/Model/FSTDocumentKeySet.h"
-
-#import "Firestore/Source/Model/FSTDocumentKey.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@implementation FSTImmutableSortedSet (FSTDocumentKey)
-
-+ (instancetype)keySet {
- return [FSTDocumentKeySet setWithComparator:FSTDocumentKeyComparator];
-}
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Model/FSTDocumentVersionDictionary.h b/Firestore/Source/Model/FSTDocumentVersionDictionary.h
deleted file mode 100644
index 674614e..0000000
--- a/Firestore/Source/Model/FSTDocumentVersionDictionary.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <Foundation/Foundation.h>
-
-#import "Firestore/third_party/Immutable/FSTImmutableSortedDictionary.h"
-
-@class FSTDocumentKey;
-@class FSTSnapshotVersion;
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** A map of key to version number. */
-typedef FSTImmutableSortedDictionary<FSTDocumentKey *, FSTSnapshotVersion *>
- FSTDocumentVersionDictionary;
-
-/**
- * Extension to FSTImmutableSortedDictionary that allows natural construction of
- * FSTDocumentVersionDictionary.
- */
-@interface FSTImmutableSortedDictionary (FSTDocumentVersionDictionary)
-
-+ (instancetype)documentVersionDictionary;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Model/FSTDocumentVersionDictionary.mm b/Firestore/Source/Model/FSTDocumentVersionDictionary.mm
deleted file mode 100644
index 870e082..0000000
--- a/Firestore/Source/Model/FSTDocumentVersionDictionary.mm
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import "Firestore/Source/Model/FSTDocumentVersionDictionary.h"
-
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
-#import "Firestore/Source/Model/FSTDocumentKey.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@implementation FSTImmutableSortedDictionary (FSTDocumentVersionDictionary)
-
-+ (instancetype)documentVersionDictionary {
- static FSTDocumentVersionDictionary *singleton;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- singleton = [FSTDocumentVersionDictionary dictionaryWithComparator:FSTDocumentKeyComparator];
- });
- return singleton;
-}
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Model/FSTMutation.h b/Firestore/Source/Model/FSTMutation.h
index 7261f30..0acec15 100644
--- a/Firestore/Source/Model/FSTMutation.h
+++ b/Firestore/Source/Model/FSTMutation.h
@@ -24,13 +24,15 @@
#include "Firestore/core/src/firebase/firestore/model/field_path.h"
#include "Firestore/core/src/firebase/firestore/model/field_transform.h"
#include "Firestore/core/src/firebase/firestore/model/precondition.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
#include "Firestore/core/src/firebase/firestore/model/transform_operations.h"
+#include "absl/types/optional.h"
+
@class FSTDocument;
@class FSTFieldValue;
@class FSTMaybeDocument;
@class FSTObjectValue;
-@class FSTSnapshotVersion;
@class FIRTimestamp;
NS_ASSUME_NONNULL_BEGIN
@@ -40,12 +42,12 @@ NS_ASSUME_NONNULL_BEGIN
@interface FSTMutationResult : NSObject
- (instancetype)init NS_UNAVAILABLE;
-- (instancetype)initWithVersion:(FSTSnapshotVersion *_Nullable)version
+- (instancetype)initWithVersion:(absl::optional<firebase::firestore::model::SnapshotVersion>)version
transformResults:(NSArray<FSTFieldValue *> *_Nullable)transformResults
NS_DESIGNATED_INITIALIZER;
/** The version at which the mutation was committed or null for a delete. */
-@property(nonatomic, strong, readonly, nullable) FSTSnapshotVersion *version;
+- (const absl::optional<firebase::firestore::model::SnapshotVersion> &)version;
/**
* The resulting fields returned from the backend after a FSTTransformMutation has been committed.
diff --git a/Firestore/Source/Model/FSTMutation.mm b/Firestore/Source/Model/FSTMutation.mm
index 3432a7c..82a535e 100644
--- a/Firestore/Source/Model/FSTMutation.mm
+++ b/Firestore/Source/Model/FSTMutation.mm
@@ -23,7 +23,6 @@
#import "FIRTimestamp.h"
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
#import "Firestore/Source/Model/FSTDocument.h"
#import "Firestore/Source/Model/FSTFieldValue.h"
#import "Firestore/Source/Util/FSTAssert.h"
@@ -36,6 +35,8 @@
#include "Firestore/core/src/firebase/firestore/model/precondition.h"
#include "Firestore/core/src/firebase/firestore/model/transform_operations.h"
+#include "absl/types/optional.h"
+
using firebase::firestore::model::ArrayTransform;
using firebase::firestore::model::DocumentKey;
using firebase::firestore::model::FieldMask;
@@ -43,23 +44,30 @@ using firebase::firestore::model::FieldPath;
using firebase::firestore::model::FieldTransform;
using firebase::firestore::model::Precondition;
using firebase::firestore::model::ServerTimestampTransform;
+using firebase::firestore::model::SnapshotVersion;
using firebase::firestore::model::TransformOperation;
NS_ASSUME_NONNULL_BEGIN
#pragma mark - FSTMutationResult
-@implementation FSTMutationResult
+@implementation FSTMutationResult {
+ absl::optional<SnapshotVersion> _version;
+}
-- (instancetype)initWithVersion:(nullable FSTSnapshotVersion *)version
+- (instancetype)initWithVersion:(absl::optional<SnapshotVersion>)version
transformResults:(nullable NSArray<FSTFieldValue *> *)transformResults {
if (self = [super init]) {
- _version = version;
+ _version = std::move(version);
_transformResults = transformResults;
}
return self;
}
+- (const absl::optional<SnapshotVersion> &)version {
+ return _version;
+}
+
@end
#pragma mark - FSTMutation
@@ -157,7 +165,7 @@ NS_ASSUME_NONNULL_BEGIN
// If the document didn't exist before, create it.
return [FSTDocument documentWithData:self.value
key:self.key
- version:[FSTSnapshotVersion noVersion]
+ version:SnapshotVersion::None()
hasLocalMutations:hasLocalMutations];
}
@@ -239,10 +247,10 @@ NS_ASSUME_NONNULL_BEGIN
if (!maybeDoc || [maybeDoc isMemberOfClass:[FSTDeletedDocument class]]) {
// Precondition applied, so create the document if necessary
const DocumentKey &key = maybeDoc ? maybeDoc.key : self.key;
- FSTSnapshotVersion *version = maybeDoc ? maybeDoc.version : [FSTSnapshotVersion noVersion];
+ SnapshotVersion version = maybeDoc ? maybeDoc.version : SnapshotVersion::None();
maybeDoc = [FSTDocument documentWithData:[FSTObjectValue objectValue]
key:key
- version:version
+ version:std::move(version)
hasLocalMutations:hasLocalMutations];
}
@@ -384,30 +392,15 @@ serverTransformResultsWithBaseDocument:(nullable FSTMaybeDocument *)baseDocument
for (NSUInteger i = 0; i < serverTransformResults.count; i++) {
const FieldTransform &fieldTransform = self.fieldTransforms[i];
+ const TransformOperation &transform = fieldTransform.transformation();
+
FSTFieldValue *previousValue = nil;
if ([baseDocument isMemberOfClass:[FSTDocument class]]) {
previousValue = [((FSTDocument *)baseDocument) fieldForPath:fieldTransform.path()];
}
- FSTFieldValue *transformResult;
- // The server just sends null as the transform result for array union / remove operations, so
- // we have to calculate a result the same as we do for local applications.
- if (fieldTransform.transformation().type() == TransformOperation::Type::ArrayUnion) {
- transformResult = [self
- arrayUnionResultWithElements:ArrayTransform::Elements(fieldTransform.transformation())
- previousValue:previousValue];
-
- } else if (fieldTransform.transformation().type() == TransformOperation::Type::ArrayRemove) {
- transformResult = [self
- arrayRemoveResultWithElements:ArrayTransform::Elements(fieldTransform.transformation())
- previousValue:previousValue];
-
- } else {
- // Just use the server-supplied result.
- transformResult = serverTransformResults[i];
- }
-
- [transformResults addObject:transformResult];
+ [transformResults
+ addObject:transform.ApplyToRemoteDocument(previousValue, serverTransformResults[i])];
}
return transformResults;
}
@@ -426,77 +419,18 @@ serverTransformResultsWithBaseDocument:(nullable FSTMaybeDocument *)baseDocument
writeTime:(FIRTimestamp *)localWriteTime {
NSMutableArray<FSTFieldValue *> *transformResults = [NSMutableArray array];
for (const FieldTransform &fieldTransform : self.fieldTransforms) {
+ const TransformOperation &transform = fieldTransform.transformation();
+
FSTFieldValue *previousValue = nil;
if ([baseDocument isMemberOfClass:[FSTDocument class]]) {
previousValue = [((FSTDocument *)baseDocument) fieldForPath:fieldTransform.path()];
}
- FSTFieldValue *transformResult;
- if (fieldTransform.transformation().type() == TransformOperation::Type::ServerTimestamp) {
- transformResult =
- [FSTServerTimestampValue serverTimestampValueWithLocalWriteTime:localWriteTime
- previousValue:previousValue];
-
- } else if (fieldTransform.transformation().type() == TransformOperation::Type::ArrayUnion) {
- transformResult = [self
- arrayUnionResultWithElements:ArrayTransform::Elements(fieldTransform.transformation())
- previousValue:previousValue];
-
- } else if (fieldTransform.transformation().type() == TransformOperation::Type::ArrayRemove) {
- transformResult = [self
- arrayRemoveResultWithElements:ArrayTransform::Elements(fieldTransform.transformation())
- previousValue:previousValue];
-
- } else {
- FSTFail(@"Encountered unknown transform: %d type", fieldTransform.transformation().type());
- }
-
- [transformResults addObject:transformResult];
+ [transformResults addObject:transform.ApplyToLocalView(previousValue, localWriteTime)];
}
return transformResults;
}
-/**
- * Transforms the provided `previousValue` via the provided `elements`. Used both for local
- * application and after server acknowledgement.
- */
-- (FSTFieldValue *)arrayUnionResultWithElements:(const std::vector<FSTFieldValue *> &)elements
- previousValue:(FSTFieldValue *)previousValue {
- NSMutableArray<FSTFieldValue *> *result = [self coercedFieldValuesArray:previousValue];
- for (FSTFieldValue *element : elements) {
- if (![result containsObject:element]) {
- [result addObject:element];
- }
- }
- return [[FSTArrayValue alloc] initWithValueNoCopy:result];
-}
-
-/**
- * Transforms the provided `previousValue` via the provided `elements`. Used both for local
- * application and after server acknowledgement.
- */
-- (FSTFieldValue *)arrayRemoveResultWithElements:(const std::vector<FSTFieldValue *> &)elements
- previousValue:(FSTFieldValue *)previousValue {
- NSMutableArray<FSTFieldValue *> *result = [self coercedFieldValuesArray:previousValue];
- for (FSTFieldValue *element : elements) {
- [result removeObject:element];
- }
- return [[FSTArrayValue alloc] initWithValueNoCopy:result];
-}
-
-/**
- * Inspects the provided value, returning a mutable copy of the internal array if it's an
- * FSTArrayValue and an empty mutable array if it's nil or any other type of FSTFieldValue.
- */
-- (NSMutableArray<FSTFieldValue *> *)coercedFieldValuesArray:(nullable FSTFieldValue *)value {
- if ([value isMemberOfClass:[FSTArrayValue class]]) {
- return [NSMutableArray arrayWithArray:((FSTArrayValue *)value).internalValue];
- } else {
- // coerce to empty array.
- return [NSMutableArray array];
- }
-}
-
- (FSTObjectValue *)transformObject:(FSTObjectValue *)objectValue
transformResults:(NSArray<FSTFieldValue *> *)transformResults {
FSTAssert(transformResults.count == self.fieldTransforms.size(),
@@ -556,7 +490,7 @@ serverTransformResultsWithBaseDocument:(nullable FSTMaybeDocument *)baseDocument
FSTAssert([maybeDoc.key isEqual:self.key], @"Can only delete a document with the same key");
}
- return [FSTDeletedDocument documentWithKey:self.key version:[FSTSnapshotVersion noVersion]];
+ return [FSTDeletedDocument documentWithKey:self.key version:SnapshotVersion::None()];
}
@end
diff --git a/Firestore/Source/Model/FSTMutationBatch.h b/Firestore/Source/Model/FSTMutationBatch.h
index 3c82338..761a885 100644
--- a/Firestore/Source/Model/FSTMutationBatch.h
+++ b/Firestore/Source/Model/FSTMutationBatch.h
@@ -16,17 +16,29 @@
#import <Foundation/Foundation.h>
+#include <unordered_map>
+
#import "Firestore/Source/Core/FSTTypes.h"
-#import "Firestore/Source/Model/FSTDocumentKeySet.h"
-#import "Firestore/Source/Model/FSTDocumentVersionDictionary.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/document_key_set.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
@class FSTMutation;
@class FIRTimestamp;
@class FSTMutationResult;
@class FSTMutationBatchResult;
-@class FSTSnapshotVersion;
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+// TODO(wilhuff): make this type a member of MutationBatchResult once that's a C++ class.
+using DocumentVersionMap = std::unordered_map<DocumentKey, SnapshotVersion, DocumentKeyHash>;
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
NS_ASSUME_NONNULL_BEGIN
@@ -85,7 +97,7 @@ extern const FSTBatchID kFSTBatchIDUnknown;
- (FSTMutationBatch *)toTombstone;
/** Returns the set of unique keys referenced by all mutations in the batch. */
-- (FSTDocumentKeySet *)keys;
+- (firebase::firestore::model::DocumentKeySet)keys;
@property(nonatomic, assign, readonly) FSTBatchID batchID;
@property(nonatomic, strong, readonly) FIRTimestamp *localWriteTime;
@@ -106,15 +118,17 @@ extern const FSTBatchID kFSTBatchIDUnknown;
* (as docVersions).
*/
+ (instancetype)resultWithBatch:(FSTMutationBatch *)batch
- commitVersion:(FSTSnapshotVersion *)commitVersion
+ commitVersion:(firebase::firestore::model::SnapshotVersion)commitVersion
mutationResults:(NSArray<FSTMutationResult *> *)mutationResults
streamToken:(nullable NSData *)streamToken;
+- (const firebase::firestore::model::SnapshotVersion &)commitVersion;
+
@property(nonatomic, strong, readonly) FSTMutationBatch *batch;
-@property(nonatomic, strong, readonly) FSTSnapshotVersion *commitVersion;
@property(nonatomic, strong, readonly) NSArray<FSTMutationResult *> *mutationResults;
@property(nonatomic, strong, readonly, nullable) NSData *streamToken;
-@property(nonatomic, strong, readonly) FSTDocumentVersionDictionary *docVersions;
+
+- (const firebase::firestore::model::DocumentVersionMap &)docVersions;
@end
diff --git a/Firestore/Source/Model/FSTMutationBatch.mm b/Firestore/Source/Model/FSTMutationBatch.mm
index e62a72c..1e9189c 100644
--- a/Firestore/Source/Model/FSTMutationBatch.mm
+++ b/Firestore/Source/Model/FSTMutationBatch.mm
@@ -16,16 +16,19 @@
#import "Firestore/Source/Model/FSTMutationBatch.h"
+#include <utility>
+
#import "FIRTimestamp.h"
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
#import "Firestore/Source/Model/FSTDocument.h"
#import "Firestore/Source/Model/FSTMutation.h"
#import "Firestore/Source/Util/FSTAssert.h"
-#include "Firestore/core/src/firebase/firestore/model/document_key.h"
-
using firebase::firestore::model::DocumentKey;
+using firebase::firestore::model::DocumentKeyHash;
+using firebase::firestore::model::SnapshotVersion;
+using firebase::firestore::model::DocumentKeySet;
+using firebase::firestore::model::DocumentVersionMap;
NS_ASSUME_NONNULL_BEGIN
@@ -113,10 +116,10 @@ const FSTBatchID kFSTBatchIDUnknown = -1;
}
// TODO(klimt): This could use NSMutableDictionary instead.
-- (FSTDocumentKeySet *)keys {
- FSTDocumentKeySet *set = [FSTDocumentKeySet keySet];
+- (DocumentKeySet)keys {
+ DocumentKeySet set;
for (FSTMutation *mutation in self.mutations) {
- set = [set setByAddingObject:mutation.key];
+ set = set.insert(mutation.key);
}
return set;
}
@@ -127,56 +130,66 @@ const FSTBatchID kFSTBatchIDUnknown = -1;
@interface FSTMutationBatchResult ()
- (instancetype)initWithBatch:(FSTMutationBatch *)batch
- commitVersion:(FSTSnapshotVersion *)commitVersion
+ commitVersion:(SnapshotVersion)commitVersion
mutationResults:(NSArray<FSTMutationResult *> *)mutationResults
streamToken:(nullable NSData *)streamToken
- docVersions:(FSTDocumentVersionDictionary *)docVersions NS_DESIGNATED_INITIALIZER;
+ docVersions:(DocumentVersionMap)docVersions NS_DESIGNATED_INITIALIZER;
@end
-@implementation FSTMutationBatchResult
+@implementation FSTMutationBatchResult {
+ SnapshotVersion _commitVersion;
+ DocumentVersionMap _docVersions;
+}
- (instancetype)initWithBatch:(FSTMutationBatch *)batch
- commitVersion:(FSTSnapshotVersion *)commitVersion
+ commitVersion:(SnapshotVersion)commitVersion
mutationResults:(NSArray<FSTMutationResult *> *)mutationResults
streamToken:(nullable NSData *)streamToken
- docVersions:(FSTDocumentVersionDictionary *)docVersions {
+ docVersions:(DocumentVersionMap)docVersions {
if (self = [super init]) {
_batch = batch;
- _commitVersion = commitVersion;
+ _commitVersion = std::move(commitVersion);
_mutationResults = mutationResults;
_streamToken = streamToken;
- _docVersions = docVersions;
+ _docVersions = std::move(docVersions);
}
return self;
}
+- (const SnapshotVersion &)commitVersion {
+ return _commitVersion;
+}
+
+- (const DocumentVersionMap &)docVersions {
+ return _docVersions;
+}
+
+ (instancetype)resultWithBatch:(FSTMutationBatch *)batch
- commitVersion:(FSTSnapshotVersion *)commitVersion
+ commitVersion:(SnapshotVersion)commitVersion
mutationResults:(NSArray<FSTMutationResult *> *)mutationResults
streamToken:(nullable NSData *)streamToken {
FSTAssert(batch.mutations.count == mutationResults.count,
@"Mutations sent %lu must equal results received %lu",
(unsigned long)batch.mutations.count, (unsigned long)mutationResults.count);
- FSTDocumentVersionDictionary *docVersions =
- [FSTDocumentVersionDictionary documentVersionDictionary];
+ DocumentVersionMap docVersions;
NSArray<FSTMutation *> *mutations = batch.mutations;
for (NSUInteger i = 0; i < mutations.count; i++) {
- FSTSnapshotVersion *_Nullable version = mutationResults[i].version;
+ absl::optional<SnapshotVersion> version = mutationResults[i].version;
if (!version) {
// deletes don't have a version, so we substitute the commitVersion
// of the entire batch.
version = commitVersion;
}
- docVersions = [docVersions dictionaryBySettingObject:version forKey:mutations[i].key];
+ docVersions[mutations[i].key] = version.value();
}
return [[FSTMutationBatchResult alloc] initWithBatch:batch
- commitVersion:commitVersion
+ commitVersion:std::move(commitVersion)
mutationResults:mutationResults
streamToken:streamToken
- docVersions:docVersions];
+ docVersions:std::move(docVersions)];
}
@end
diff --git a/Firestore/Source/Public/FIRDocumentReference.h b/Firestore/Source/Public/FIRDocumentReference.h
index 4aa8c45..7baa30a 100644
--- a/Firestore/Source/Public/FIRDocumentReference.h
+++ b/Firestore/Source/Public/FIRDocumentReference.h
@@ -92,6 +92,23 @@ NS_SWIFT_NAME(DocumentReference)
- (void)setData:(NSDictionary<NSString *, id> *)documentData merge:(BOOL)merge;
/**
+ * Writes to the document referred to by `document` and only replace the fields
+ * specified under `mergeFields`. Any field that is not specified in `mergeFields`
+ * is ignored and remains untouched. If the document doesn't yet exist,
+ * this method creates it and then sets the data.
+ *
+ * It is an error to include a field in `mergeFields` that does not have a corresponding
+ * value in the `data` dictionary.
+ *
+ * @param documentData An `NSDictionary` containing the fields that make up the document
+ * to be written.
+ * @param mergeFields An `NSArray` that contains a list of `NSString` or `FIRFieldPath` elements
+ * specifying which fields to merge. Fields can contain dots to reference nested fields within
+ * the document.
+ */
+- (void)setData:(NSDictionary<NSString *, id> *)documentData mergeFields:(NSArray<id> *)mergeFields;
+
+/**
* Overwrites the document referred to by this `FIRDocumentReference`. If no document exists, it
* is created. If a document already exists, it is overwritten.
*
@@ -121,6 +138,28 @@ NS_SWIFT_NAME(DocumentReference)
completion:(nullable void (^)(NSError *_Nullable error))completion;
/**
+ * Writes to the document referred to by `document` and only replace the fields
+ * specified under `mergeFields`. Any field that is not specified in `mergeFields`
+ * is ignored and remains untouched. If the document doesn't yet exist,
+ * this method creates it and then sets the data.
+ *
+ * It is an error to include a field in `mergeFields` that does not have a corresponding
+ * value in the `data` dictionary.
+ *
+ * @param documentData An `NSDictionary` containing the fields that make up the document
+ * to be written.
+ * @param mergeFields An `NSArray` that contains a list of `NSString` or `FIRFieldPath` elements
+ * specifying which fields to merge. Fields can contain dots to reference nested fields within
+ * the document.
+ * @param completion A block to execute once the document has been successfully written to the
+ * server. This block will not be called while the client is offline, though local
+ * changes will be visible immediately.
+ */
+- (void)setData:(NSDictionary<NSString *, id> *)documentData
+ mergeFields:(NSArray<id> *)mergeFields
+ completion:(nullable void (^)(NSError *_Nullable error))completion;
+
+/**
* Updates fields in the document referred to by this `FIRDocumentReference`.
* If the document does not exist, the update fails (specify a completion block to be notified).
*
diff --git a/Firestore/Source/Public/FIRTransaction.h b/Firestore/Source/Public/FIRTransaction.h
index 2fa4430..e53414d 100644
--- a/Firestore/Source/Public/FIRTransaction.h
+++ b/Firestore/Source/Public/FIRTransaction.h
@@ -65,6 +65,30 @@ NS_SWIFT_NAME(Transaction)
// clang-format on
/**
+ * Writes to the document referred to by `document` and only replace the fields
+ * specified under `mergeFields`. Any field that is not specified in `mergeFields`
+ * is ignored and remains untouched. If the document doesn't yet exist,
+ * this method creates it and then sets the data.
+ *
+ * It is an error to include a field in `mergeFields` that does not have a corresponding
+ * value in the `data` dictionary.
+ *
+ * @param data An `NSDictionary` containing the fields that make up the document
+ * to be written.
+ * @param document A reference to the document whose data should be overwritten.
+ * @param mergeFields An `NSArray` that contains a list of `NSString` or `FIRFieldPath` elements
+ * specifying which fields to merge. Fields can contain dots to reference nested fields within
+ * the document.
+ * @return This `FIRTransaction` instance. Used for chaining method calls.
+ */
+// clang-format off
+- (FIRTransaction *)setData:(NSDictionary<NSString *, id> *)data
+ forDocument:(FIRDocumentReference *)document
+ mergeFields:(NSArray<id> *)mergeFields
+ NS_SWIFT_NAME(setData(_:forDocument:mergeFields:));
+// clang-format on
+
+/**
* Updates fields in the document referred to by `document`.
* If the document does not exist, the transaction will fail.
*
diff --git a/Firestore/Source/Public/FIRWriteBatch.h b/Firestore/Source/Public/FIRWriteBatch.h
index 1568723..22d1b16 100644
--- a/Firestore/Source/Public/FIRWriteBatch.h
+++ b/Firestore/Source/Public/FIRWriteBatch.h
@@ -68,6 +68,29 @@ NS_SWIFT_NAME(WriteBatch)
// clang-format on
/**
+ * Writes to the document referred to by `document` and only replace the fields
+ * specified under `mergeFields`. Any field that is not specified in `mergeFields`
+ * is ignored and remains untouched. If the document doesn't yet exist,
+ * this method creates it and then sets the data.
+ *
+ * It is an error to include a field in `mergeFields` that does not have a corresponding
+ * value in the `data` dictionary.
+ *
+ * @param data An `NSDictionary` that contains the fields and data to write to the document.
+ * @param document A reference to the document whose data should be overwritten.
+ * @param mergeFields An `NSArray` that contains a list of `NSString` or `FIRFieldPath` elements
+ * specifying which fields to merge. Fields can contain dots to reference nested fields within
+ * the document.
+ * @return This `FIRWriteBatch` instance. Used for chaining method calls.
+ */
+// clang-format off
+- (FIRWriteBatch *)setData:(NSDictionary<NSString *, id> *)data
+ forDocument:(FIRDocumentReference *)document
+ mergeFields:(NSArray<id> *)mergeFields
+ NS_SWIFT_NAME(setData(_:forDocument:mergeFields:));
+// clang-format on
+
+/**
* Updates fields in the document referred to by `document`.
* If document does not exist, the write batch will fail.
*
diff --git a/Firestore/Source/Remote/FSTDatastore.h b/Firestore/Source/Remote/FSTDatastore.h
index b3ba46c..da14b6e 100644
--- a/Firestore/Source/Remote/FSTDatastore.h
+++ b/Firestore/Source/Remote/FSTDatastore.h
@@ -31,7 +31,6 @@
@class FSTMutationResult;
@class FSTQueryData;
@class FSTSerializerBeta;
-@class FSTSnapshotVersion;
@class FSTWatchChange;
@class FSTWatchStream;
@class FSTWriteStream;
diff --git a/Firestore/Source/Remote/FSTRemoteEvent.h b/Firestore/Source/Remote/FSTRemoteEvent.h
index 0f6b6c7..c84e34d 100644
--- a/Firestore/Source/Remote/FSTRemoteEvent.h
+++ b/Firestore/Source/Remote/FSTRemoteEvent.h
@@ -20,14 +20,14 @@
#import "Firestore/Source/Core/FSTTypes.h"
#import "Firestore/Source/Model/FSTDocumentDictionary.h"
-#import "Firestore/Source/Model/FSTDocumentKeySet.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/document_key_set.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
@class FSTDocument;
@class FSTExistenceFilter;
@class FSTMaybeDocument;
-@class FSTSnapshotVersion;
@class FSTWatchChange;
@class FSTQueryData;
@@ -43,6 +43,15 @@ NS_ASSUME_NONNULL_BEGIN
* base class.
*/
@interface FSTTargetMapping : NSObject
+
+/**
+ * Strips out mapping changes that aren't actually changes. That is, if the document already
+ * existed in the target, and is being added in the target, and this is not a reset, we can
+ * skip doing the work to associate the document with the target because it has already been done.
+ */
+- (void)filterUpdatesUsingExistingKeys:
+ (const firebase::firestore::model::DocumentKeySet &)existingKeys;
+
@end
#pragma mark - FSTResetMapping
@@ -57,7 +66,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (FSTResetMapping *)mappingWithDocuments:(NSArray<FSTDocument *> *)documents;
/** The new set of documents for the target. */
-@property(nonatomic, strong, readonly) FSTDocumentKeySet *documents;
+- (const firebase::firestore::model::DocumentKeySet &)documents;
@end
#pragma mark - FSTUpdateMapping
@@ -74,12 +83,13 @@ NS_ASSUME_NONNULL_BEGIN
+ (FSTUpdateMapping *)mappingWithAddedDocuments:(NSArray<FSTDocument *> *)added
removedDocuments:(NSArray<FSTDocument *> *)removed;
-- (FSTDocumentKeySet *)applyTo:(FSTDocumentKeySet *)keys;
+- (firebase::firestore::model::DocumentKeySet)applyTo:
+ (const firebase::firestore::model::DocumentKeySet &)keys;
/** The documents added to the target. */
-@property(nonatomic, strong, readonly) FSTDocumentKeySet *addedDocuments;
+- (const firebase::firestore::model::DocumentKeySet &)addedDocuments;
/** The documents removed from the target. */
-@property(nonatomic, strong, readonly) FSTDocumentKeySet *removedDocuments;
+- (const firebase::firestore::model::DocumentKeySet &)removedDocuments;
@end
#pragma mark - FSTTargetChange
@@ -107,6 +117,12 @@ typedef NS_ENUM(NSUInteger, FSTCurrentStatusUpdate) {
@interface FSTTargetChange : NSObject
/**
+ * Creates a new target change with the given SnapshotVersion.
+ */
+- (instancetype)initWithSnapshotVersion:
+ (firebase::firestore::model::SnapshotVersion)snapshotVersion;
+
+/**
* Creates a new target change with the given documents. Instances of FSTDocument are considered
* added. Instance of FSTDeletedDocument are considered removed. This is intended primarily for
* testing.
@@ -115,6 +131,12 @@ typedef NS_ENUM(NSUInteger, FSTCurrentStatusUpdate) {
currentStatusUpdate:(FSTCurrentStatusUpdate)currentStatusUpdate;
/**
+ * The snapshot version representing the last state at which this target received a consistent
+ * snapshot from the backend.
+ */
+- (const firebase::firestore::model::SnapshotVersion &)snapshotVersion;
+
+/**
* The new "current" (synced) status of this target. Set to CurrentStatusUpdateNone if the status
* should not be updated. Note "current" has special meaning for in the RPC protocol that implies
* that a target is both up-to-date and consistent with the rest of the watch stream.
@@ -125,12 +147,6 @@ typedef NS_ENUM(NSUInteger, FSTCurrentStatusUpdate) {
@property(nonatomic, strong, readonly) FSTTargetMapping *mapping;
/**
- * The snapshot version representing the last state at which this target received a consistent
- * snapshot from the backend.
- */
-@property(nonatomic, strong, readonly) FSTSnapshotVersion *snapshotVersion;
-
-/**
* An opaque, server-assigned token that allows watching a query to be resumed after disconnecting
* without retransmitting all the data that matches the query. The resume token essentially
* identifies a point in time from which the server should resume sending results.
@@ -147,14 +163,15 @@ typedef NS_ENUM(NSUInteger, FSTCurrentStatusUpdate) {
*/
@interface FSTRemoteEvent : NSObject
-+ (instancetype)
-eventWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
- targetChanges:(NSMutableDictionary<NSNumber *, FSTTargetChange *> *)targetChanges
- documentUpdates:
- (std::map<firebase::firestore::model::DocumentKey, FSTMaybeDocument *>)documentUpdates;
+- (instancetype)
+initWithSnapshotVersion:(firebase::firestore::model::SnapshotVersion)snapshotVersion
+ targetChanges:(NSMutableDictionary<NSNumber *, FSTTargetChange *> *)targetChanges
+ documentUpdates:
+ (std::map<firebase::firestore::model::DocumentKey, FSTMaybeDocument *>)documentUpdates
+ limboDocuments:(firebase::firestore::model::DocumentKeySet)limboDocuments;
/** The snapshot version this event brings us up to. */
-@property(nonatomic, strong, readonly) FSTSnapshotVersion *snapshotVersion;
+- (const firebase::firestore::model::SnapshotVersion &)snapshotVersion;
/** A map from target to changes to the target. See TargetChange. */
@property(nonatomic, strong, readonly)
@@ -166,6 +183,8 @@ eventWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
*/
- (const std::map<firebase::firestore::model::DocumentKey, FSTMaybeDocument *> &)documentUpdates;
+- (const firebase::firestore::model::DocumentKeySet &)limboDocumentChanges;
+
/** Adds a document update to this remote event */
- (void)addDocumentUpdate:(FSTMaybeDocument *)document;
@@ -175,14 +194,6 @@ eventWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
- (void)synthesizeDeleteForLimboTargetChange:(FSTTargetChange *)targetChange
key:(const firebase::firestore::model::DocumentKey &)key;
-/**
- * Strips out mapping changes that aren't actually changes. That is, if the document already
- * existed in the target, and is being added in the target, and this is not a reset, we can
- * skip doing the work to associate the document with the target because it has already been done.
- */
-- (void)filterUpdatesFromTargetChange:(FSTTargetChange *)targetChange
- existingDocuments:(FSTDocumentKeySet *)existingDocuments;
-
@end
#pragma mark - FSTWatchChangeAggregator
@@ -194,7 +205,7 @@ eventWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
@interface FSTWatchChangeAggregator : NSObject
- (instancetype)
-initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
+initWithSnapshotVersion:(firebase::firestore::model::SnapshotVersion)snapshotVersion
listenTargets:(NSDictionary<FSTBoxedTargetID *, FSTQueryData *> *)listenTargets
pendingTargetResponses:(NSDictionary<FSTBoxedTargetID *, NSNumber *> *)pendingTargetResponses
NS_DESIGNATED_INITIALIZER;
diff --git a/Firestore/Source/Remote/FSTRemoteEvent.mm b/Firestore/Source/Remote/FSTRemoteEvent.mm
index 30aa0d3..438072e 100644
--- a/Firestore/Source/Remote/FSTRemoteEvent.mm
+++ b/Firestore/Source/Remote/FSTRemoteEvent.mm
@@ -19,16 +19,19 @@
#include <map>
#include <utility>
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
+#import "Firestore/Source/Local/FSTQueryData.h"
#import "Firestore/Source/Model/FSTDocument.h"
#import "Firestore/Source/Remote/FSTWatchChange.h"
#import "Firestore/Source/Util/FSTAssert.h"
#import "Firestore/Source/Util/FSTClasses.h"
#import "Firestore/Source/Util/FSTLogger.h"
-
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/util/hashing.h"
using firebase::firestore::model::DocumentKey;
+using firebase::firestore::model::SnapshotVersion;
+using firebase::firestore::util::Hash;
+using firebase::firestore::model::DocumentKeySet;
NS_ASSUME_NONNULL_BEGIN
@@ -54,32 +57,38 @@ NS_ASSUME_NONNULL_BEGIN
@throw FSTAbstractMethodException(); // NOLINT
}
+- (void)filterUpdatesUsingExistingKeys:(const DocumentKeySet &)existingKeys {
+ @throw FSTAbstractMethodException(); // NOLINT
+}
+
@end
#pragma mark - FSTResetMapping
-@interface FSTResetMapping ()
-@property(nonatomic, strong) FSTDocumentKeySet *documents;
-@end
-
-@implementation FSTResetMapping
+@implementation FSTResetMapping {
+ DocumentKeySet _documents;
+}
+ (instancetype)mappingWithDocuments:(NSArray<FSTDocument *> *)documents {
- FSTResetMapping *mapping = [[FSTResetMapping alloc] init];
+ DocumentKeySet keys;
for (FSTDocument *doc in documents) {
- mapping.documents = [mapping.documents setByAddingObject:doc.key];
+ keys = keys.insert(doc.key);
}
- return mapping;
+ return [[FSTResetMapping alloc] initWithDocuments:std::move(keys)];
}
-- (instancetype)init {
+- (instancetype)initWithDocuments:(DocumentKeySet)documents {
self = [super init];
if (self) {
- _documents = [FSTDocumentKeySet keySet];
+ _documents = std::move(documents);
}
return self;
}
+- (const DocumentKeySet &)documents {
+ return _documents;
+}
+
- (BOOL)isEqual:(id)other {
if (other == self) {
return YES;
@@ -89,53 +98,66 @@ NS_ASSUME_NONNULL_BEGIN
}
FSTResetMapping *otherMapping = (FSTResetMapping *)other;
- return [self.documents isEqual:otherMapping.documents];
+ return _documents == otherMapping.documents;
}
- (NSUInteger)hash {
- return self.documents.hash;
+ return Hash(_documents);
}
- (void)addDocumentKey:(const DocumentKey &)documentKey {
- self.documents = [self.documents setByAddingObject:documentKey];
+ _documents = _documents.insert(documentKey);
}
- (void)removeDocumentKey:(const DocumentKey &)documentKey {
- self.documents = [self.documents setByRemovingObject:documentKey];
+ _documents = _documents.erase(documentKey);
+}
+
+- (void)filterUpdatesUsingExistingKeys:(const DocumentKeySet &)existingKeys {
+ // No-op. Resets are not filtered.
}
@end
#pragma mark - FSTUpdateMapping
-@interface FSTUpdateMapping ()
-@property(nonatomic, strong) FSTDocumentKeySet *addedDocuments;
-@property(nonatomic, strong) FSTDocumentKeySet *removedDocuments;
-@end
-
-@implementation FSTUpdateMapping
+@implementation FSTUpdateMapping {
+ DocumentKeySet _addedDocuments;
+ DocumentKeySet _removedDocuments;
+}
+ (FSTUpdateMapping *)mappingWithAddedDocuments:(NSArray<FSTDocument *> *)added
removedDocuments:(NSArray<FSTDocument *> *)removed {
- FSTUpdateMapping *mapping = [[FSTUpdateMapping alloc] init];
+ DocumentKeySet addedDocuments;
+ DocumentKeySet removedDocuments;
for (FSTDocument *doc in added) {
- mapping.addedDocuments = [mapping.addedDocuments setByAddingObject:doc.key];
+ addedDocuments = addedDocuments.insert(doc.key);
}
for (FSTDocument *doc in removed) {
- mapping.removedDocuments = [mapping.removedDocuments setByAddingObject:doc.key];
+ removedDocuments = removedDocuments.insert(doc.key);
}
- return mapping;
+ return [[FSTUpdateMapping alloc] initWithAddedDocuments:std::move(addedDocuments)
+ removedDocuments:std::move(removedDocuments)];
}
-- (instancetype)init {
+- (instancetype)initWithAddedDocuments:(DocumentKeySet)addedDocuments
+ removedDocuments:(DocumentKeySet)removedDocuments {
self = [super init];
if (self) {
- _addedDocuments = [FSTDocumentKeySet keySet];
- _removedDocuments = [FSTDocumentKeySet keySet];
+ _addedDocuments = std::move(addedDocuments);
+ _removedDocuments = std::move(removedDocuments);
}
return self;
}
+- (const DocumentKeySet &)addedDocuments {
+ return _addedDocuments;
+}
+
+- (const DocumentKeySet &)removedDocuments {
+ return _removedDocuments;
+}
+
- (BOOL)isEqual:(id)other {
if (other == self) {
return YES;
@@ -145,33 +167,43 @@ NS_ASSUME_NONNULL_BEGIN
}
FSTUpdateMapping *otherMapping = (FSTUpdateMapping *)other;
- return [self.addedDocuments isEqual:otherMapping.addedDocuments] &&
- [self.removedDocuments isEqual:otherMapping.removedDocuments];
+ return _addedDocuments == otherMapping.addedDocuments &&
+ _removedDocuments == otherMapping.removedDocuments;
}
- (NSUInteger)hash {
- return self.addedDocuments.hash * 31 + self.removedDocuments.hash;
+ return Hash(_addedDocuments, _removedDocuments);
}
-- (FSTDocumentKeySet *)applyTo:(FSTDocumentKeySet *)keys {
- __block FSTDocumentKeySet *result = keys;
- [self.addedDocuments enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) {
- result = [result setByAddingObject:key];
- }];
- [self.removedDocuments enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) {
- result = [result setByRemovingObject:key];
- }];
+- (DocumentKeySet)applyTo:(const DocumentKeySet &)keys {
+ DocumentKeySet result = keys;
+ for (const DocumentKey &key : _addedDocuments) {
+ result = result.insert(key);
+ }
+ for (const DocumentKey &key : _removedDocuments) {
+ result = result.erase(key);
+ }
return result;
}
- (void)addDocumentKey:(const DocumentKey &)documentKey {
- self.addedDocuments = [self.addedDocuments setByAddingObject:documentKey];
- self.removedDocuments = [self.removedDocuments setByRemovingObject:documentKey];
+ _addedDocuments = _addedDocuments.insert(documentKey);
+ _removedDocuments = _removedDocuments.erase(documentKey);
}
- (void)removeDocumentKey:(const DocumentKey &)documentKey {
- self.addedDocuments = [self.addedDocuments setByRemovingObject:documentKey];
- self.removedDocuments = [self.removedDocuments setByAddingObject:documentKey];
+ _addedDocuments = _addedDocuments.erase(documentKey);
+ _removedDocuments = _removedDocuments.insert(documentKey);
+}
+
+- (void)filterUpdatesUsingExistingKeys:(const DocumentKeySet &)existingKeys {
+ DocumentKeySet result = _addedDocuments;
+ for (const DocumentKey &key : _addedDocuments) {
+ if (existingKeys.contains(key)) {
+ result = result.erase(key);
+ }
+ }
+ _addedDocuments = result;
}
@end
@@ -181,11 +213,12 @@ NS_ASSUME_NONNULL_BEGIN
@interface FSTTargetChange ()
@property(nonatomic, assign) FSTCurrentStatusUpdate currentStatusUpdate;
@property(nonatomic, strong, nullable) FSTTargetMapping *mapping;
-@property(nonatomic, strong) FSTSnapshotVersion *snapshotVersion;
@property(nonatomic, strong) NSData *resumeToken;
@end
-@implementation FSTTargetChange
+@implementation FSTTargetChange {
+ SnapshotVersion _snapshotVersion;
+}
- (instancetype)init {
if (self = [super init]) {
@@ -195,16 +228,32 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
+- (instancetype)initWithSnapshotVersion:(SnapshotVersion)snapshotVersion {
+ if (self = [self init]) {
+ _snapshotVersion = std::move(snapshotVersion);
+ }
+ return self;
+}
+
+- (const SnapshotVersion &)snapshotVersion {
+ return _snapshotVersion;
+}
+
+ (instancetype)changeWithDocuments:(NSArray<FSTMaybeDocument *> *)docs
currentStatusUpdate:(FSTCurrentStatusUpdate)currentStatusUpdate {
- FSTUpdateMapping *mapping = [[FSTUpdateMapping alloc] init];
+ DocumentKeySet addedDocuments;
+ DocumentKeySet removedDocuments;
for (FSTMaybeDocument *doc in docs) {
if ([doc isKindOfClass:[FSTDeletedDocument class]]) {
- mapping.removedDocuments = [mapping.removedDocuments setByAddingObject:doc.key];
+ removedDocuments = removedDocuments.insert(doc.key);
} else {
- mapping.addedDocuments = [mapping.addedDocuments setByAddingObject:doc.key];
+ addedDocuments = addedDocuments.insert(doc.key);
}
}
+ FSTUpdateMapping *mapping =
+ [[FSTUpdateMapping alloc] initWithAddedDocuments:std::move(addedDocuments)
+ removedDocuments:std::move(removedDocuments)];
+
FSTTargetChange *change = [[FSTTargetChange alloc] init];
change.mapping = mapping;
change.currentStatusUpdate = currentStatusUpdate;
@@ -212,11 +261,11 @@ NS_ASSUME_NONNULL_BEGIN
}
+ (instancetype)changeWithMapping:(FSTTargetMapping *)mapping
- snapshotVersion:(FSTSnapshotVersion *)snapshotVersion
+ snapshotVersion:(SnapshotVersion)snapshotVersion
currentStatusUpdate:(FSTCurrentStatusUpdate)currentStatusUpdate {
FSTTargetChange *change = [[FSTTargetChange alloc] init];
change.mapping = mapping;
- change.snapshotVersion = snapshotVersion;
+ change->_snapshotVersion = std::move(snapshotVersion);
change.currentStatusUpdate = currentStatusUpdate;
return change;
}
@@ -243,57 +292,42 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - FSTRemoteEvent
-@interface FSTRemoteEvent () {
- NSMutableDictionary<FSTBoxedTargetID *, FSTTargetChange *> *_targetChanges;
-}
-
-- (instancetype)
-initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
- targetChanges:(NSMutableDictionary<FSTBoxedTargetID *, FSTTargetChange *> *)targetChanges
- documentUpdates:(std::map<DocumentKey, FSTMaybeDocument *>)documentUpdates;
-
-@property(nonatomic, strong) FSTSnapshotVersion *snapshotVersion;
-
-@end
-
@implementation FSTRemoteEvent {
+ SnapshotVersion _snapshotVersion;
+ NSMutableDictionary<FSTBoxedTargetID *, FSTTargetChange *> *_targetChanges;
std::map<DocumentKey, FSTMaybeDocument *> _documentUpdates;
-}
-+ (instancetype)
-eventWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
- targetChanges:(NSMutableDictionary<NSNumber *, FSTTargetChange *> *)targetChanges
- documentUpdates:(std::map<DocumentKey, FSTMaybeDocument *>)documentUpdates {
- return [[FSTRemoteEvent alloc] initWithSnapshotVersion:snapshotVersion
- targetChanges:targetChanges
- documentUpdates:std::move(documentUpdates)];
+ DocumentKeySet _limboDocumentChanges;
}
-- (instancetype)initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
+- (instancetype)initWithSnapshotVersion:(SnapshotVersion)snapshotVersion
targetChanges:
(NSMutableDictionary<NSNumber *, FSTTargetChange *> *)targetChanges
- documentUpdates:(std::map<DocumentKey, FSTMaybeDocument *>)documentUpdates {
+ documentUpdates:(std::map<DocumentKey, FSTMaybeDocument *>)documentUpdates
+ limboDocuments:(DocumentKeySet)limboDocuments {
self = [super init];
if (self) {
- _snapshotVersion = snapshotVersion;
+ _snapshotVersion = std::move(snapshotVersion);
_targetChanges = targetChanges;
_documentUpdates = std::move(documentUpdates);
+ _limboDocumentChanges = std::move(limboDocuments);
}
return self;
}
-- (void)filterUpdatesFromTargetChange:(FSTTargetChange *)targetChange
- existingDocuments:(FSTDocumentKeySet *)existingDocuments {
- if ([targetChange.mapping isKindOfClass:[FSTUpdateMapping class]]) {
- FSTUpdateMapping *update = (FSTUpdateMapping *)targetChange.mapping;
- FSTDocumentKeySet *added = update.addedDocuments;
- __block FSTDocumentKeySet *result = added;
- [added enumerateObjectsUsingBlock:^(FSTDocumentKey *docKey, BOOL *stop) {
- if ([existingDocuments containsObject:docKey]) {
- result = [result setByRemovingObject:docKey];
- }
- }];
- update.addedDocuments = result;
- }
+- (NSDictionary<FSTBoxedTargetID *, FSTTargetChange *> *)targetChanges {
+ return _targetChanges;
+}
+
+- (const DocumentKeySet &)limboDocumentChanges {
+ return _limboDocumentChanges;
+}
+
+- (const std::map<DocumentKey, FSTMaybeDocument *> &)documentUpdates {
+ return _documentUpdates;
+}
+
+- (const SnapshotVersion &)snapshotVersion {
+ return _snapshotVersion;
}
- (void)synthesizeDeleteForLimboTargetChange:(FSTTargetChange *)targetChange
@@ -314,22 +348,15 @@ eventWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
// However, if the document doesn't exist and the current marker arrives, the document is
// not present in the snapshot and our normal view handling would consider the document to
// remain in limbo indefinitely because there are no updates to the document. To avoid this,
- // we specially handle this just this case here: synthesizing a delete.
+ // we specially handle this case here: synthesizing a delete.
//
// TODO(dimond): Ideally we would have an explicit lookup query instead resulting in an
// explicit delete message and we could remove this special logic.
_documentUpdates[key] = [FSTDeletedDocument documentWithKey:key version:_snapshotVersion];
+ _limboDocumentChanges = _limboDocumentChanges.insert(key);
}
}
-- (NSDictionary<FSTBoxedTargetID *, FSTTargetChange *> *)targetChanges {
- return static_cast<NSDictionary<FSTBoxedTargetID *, FSTTargetChange *> *>(_targetChanges);
-}
-
-- (const std::map<DocumentKey, FSTMaybeDocument *> &)documentUpdates {
- return _documentUpdates;
-}
-
/** Adds a document update to this remote event */
- (void)addDocumentUpdate:(FSTMaybeDocument *)document {
_documentUpdates[document.key] = document;
@@ -350,7 +377,7 @@ eventWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
// TODO(dimond): keep track of reset targets not to raise.
FSTTargetChange *targetChange =
[FSTTargetChange changeWithMapping:[[FSTResetMapping alloc] init]
- snapshotVersion:[FSTSnapshotVersion noVersion]
+ snapshotVersion:SnapshotVersion::None()
currentStatusUpdate:FSTCurrentStatusUpdateMarkNotCurrent];
_targetChanges[targetID] = targetChange;
}
@@ -361,9 +388,6 @@ eventWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
@interface FSTWatchChangeAggregator ()
-/** The snapshot version for every target change this creates. */
-@property(nonatomic, strong, readonly) FSTSnapshotVersion *snapshotVersion;
-
/** Keeps track of the current target mappings */
@property(nonatomic, strong, readonly)
NSMutableDictionary<FSTBoxedTargetID *, FSTTargetChange *> *targetChanges;
@@ -381,35 +405,38 @@ eventWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
NSMutableDictionary<FSTBoxedTargetID *, FSTExistenceFilter *> *_existenceFilters;
/** Keeps track of document to update */
std::map<DocumentKey, FSTMaybeDocument *> _documentUpdates;
+
+ DocumentKeySet _limboDocuments;
+ /** The snapshot version for every target change this creates. */
+ SnapshotVersion _snapshotVersion;
}
- (instancetype)
-initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
+initWithSnapshotVersion:(SnapshotVersion)snapshotVersion
listenTargets:(NSDictionary<FSTBoxedTargetID *, FSTQueryData *> *)listenTargets
pendingTargetResponses:(NSDictionary<FSTBoxedTargetID *, NSNumber *> *)pendingTargetResponses {
self = [super init];
if (self) {
- _snapshotVersion = snapshotVersion;
+ _snapshotVersion = std::move(snapshotVersion);
_frozen = NO;
_targetChanges = [NSMutableDictionary dictionary];
_listenTargets = listenTargets;
_pendingTargetResponses = [NSMutableDictionary dictionaryWithDictionary:pendingTargetResponses];
-
+ _limboDocuments = DocumentKeySet{};
_existenceFilters = [NSMutableDictionary dictionary];
}
return self;
}
- (NSDictionary<FSTBoxedTargetID *, FSTExistenceFilter *> *)existenceFilters {
- return static_cast<NSDictionary<FSTBoxedTargetID *, FSTExistenceFilter *> *>(_existenceFilters);
+ return _existenceFilters;
}
- (FSTTargetChange *)targetChangeForTargetID:(FSTBoxedTargetID *)targetID {
FSTTargetChange *change = self.targetChanges[targetID];
if (!change) {
- change = [[FSTTargetChange alloc] init];
- change.snapshotVersion = self.snapshotVersion;
+ change = [[FSTTargetChange alloc] initWithSnapshotVersion:_snapshotVersion];
self.targetChanges[targetID] = change;
}
return change;
@@ -435,20 +462,66 @@ initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
}
}
+/**
+ * Updates limbo document tracking for a given target-document mapping change. If the target is a
+ * limbo target, and the change for the document has only seen limbo targets so far, and we are not
+ * already tracking a change for this document, then consider this document a limbo document update.
+ * Otherwise, ensure that we don't consider this document a limbo document. Returns true if the
+ * change still has only seen limbo resolution changes.
+ */
+- (BOOL)updateLimboDocumentsForKey:(const DocumentKey &)documentKey
+ queryData:(FSTQueryData *)queryData
+ isOnlyLimbo:(BOOL)isOnlyLimbo {
+ if (!isOnlyLimbo) {
+ // It wasn't a limbo doc before, so it definitely isn't now.
+ return NO;
+ }
+ if (_documentUpdates.find(documentKey) == _documentUpdates.end()) {
+ // We haven't seen a document update for this key yet.
+ if (queryData.purpose == FSTQueryPurposeLimboResolution) {
+ // We haven't seen this document before, and this target is a limbo target.
+ _limboDocuments = _limboDocuments.insert(documentKey);
+ return YES;
+ } else {
+ // We haven't seen the document before, but this is a non-limbo target.
+ // Since we haven't seen it, we know it's not in our set of limbo docs. Return NO to ensure
+ // that this key is marked as non-limbo.
+ return NO;
+ }
+ } else if (queryData.purpose == FSTQueryPurposeLimboResolution) {
+ // We have only seen limbo targets so far for this document, and this is another limbo target.
+ return YES;
+ } else {
+ // We haven't marked this as non-limbo yet, but this target is not a limbo target.
+ // Mark the key as non-limbo and make sure it isn't in our set.
+ _limboDocuments = _limboDocuments.erase(documentKey);
+ return NO;
+ }
+}
+
- (void)addDocumentChange:(FSTDocumentWatchChange *)docChange {
BOOL relevant = NO;
+ BOOL isOnlyLimbo = YES;
for (FSTBoxedTargetID *targetID in docChange.updatedTargetIDs) {
- if ([self isActiveTarget:targetID]) {
+ FSTQueryData *queryData = [self queryDataForActiveTarget:targetID];
+ if (queryData) {
FSTTargetChange *change = [self targetChangeForTargetID:targetID];
+ isOnlyLimbo = [self updateLimboDocumentsForKey:docChange.documentKey
+ queryData:queryData
+ isOnlyLimbo:isOnlyLimbo];
[change.mapping addDocumentKey:docChange.documentKey];
relevant = YES;
}
}
for (FSTBoxedTargetID *targetID in docChange.removedTargetIDs) {
- if ([self isActiveTarget:targetID]) {
+ FSTQueryData *queryData = [self queryDataForActiveTarget:targetID];
+ if (queryData) {
FSTTargetChange *change = [self targetChangeForTargetID:targetID];
+ isOnlyLimbo = [self updateLimboDocumentsForKey:docChange.documentKey
+ queryData:queryData
+ isOnlyLimbo:isOnlyLimbo];
[change.mapping removeDocumentKey:docChange.documentKey];
relevant = YES;
}
@@ -473,7 +546,7 @@ initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
break;
case FSTWatchTargetChangeStateAdded:
[self recordResponseForTargetID:targetID];
- if (![self.pendingTargetResponses objectForKey:targetID]) {
+ if (!self.pendingTargetResponses[targetID]) {
// We have a freshly added target, so we need to reset any state that we had previously
// This can happen e.g. when remove and add back a target for existence filter
// mismatches.
@@ -514,12 +587,12 @@ initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
* responses that we have.
*/
- (void)recordResponseForTargetID:(FSTBoxedTargetID *)targetID {
- NSNumber *count = [self.pendingTargetResponses objectForKey:targetID];
+ NSNumber *count = self.pendingTargetResponses[targetID];
int newCount = count ? [count intValue] - 1 : -1;
if (newCount == 0) {
[self.pendingTargetResponses removeObjectForKey:targetID];
} else {
- [self.pendingTargetResponses setObject:[NSNumber numberWithInt:newCount] forKey:targetID];
+ self.pendingTargetResponses[targetID] = @(newCount);
}
}
@@ -532,8 +605,12 @@ initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
* yet acknowledged the intended change in state.
*/
- (BOOL)isActiveTarget:(FSTBoxedTargetID *)targetID {
- return [self.listenTargets objectForKey:targetID] &&
- ![self.pendingTargetResponses objectForKey:targetID];
+ return [self queryDataForActiveTarget:targetID] != nil;
+}
+
+- (FSTQueryData *_Nullable)queryDataForActiveTarget:(FSTBoxedTargetID *)targetID {
+ FSTQueryData *queryData = self.listenTargets[targetID];
+ return (queryData && !self.pendingTargetResponses[targetID]) ? queryData : nil;
}
- (void)addExistenceFilterChange:(FSTExistenceFilterWatchChange *)existenceFilterChange {
@@ -559,9 +636,10 @@ initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
// Mark this aggregator as frozen so no further modifications are made.
self.frozen = YES;
- return [FSTRemoteEvent eventWithSnapshotVersion:self.snapshotVersion
- targetChanges:targetChanges
- documentUpdates:_documentUpdates];
+ return [[FSTRemoteEvent alloc] initWithSnapshotVersion:_snapshotVersion
+ targetChanges:targetChanges
+ documentUpdates:_documentUpdates
+ limboDocuments:_limboDocuments];
}
@end
diff --git a/Firestore/Source/Remote/FSTRemoteStore.h b/Firestore/Source/Remote/FSTRemoteStore.h
index 09e1d32..9b01ce4 100644
--- a/Firestore/Source/Remote/FSTRemoteStore.h
+++ b/Firestore/Source/Remote/FSTRemoteStore.h
@@ -17,7 +17,6 @@
#import <Foundation/Foundation.h>
#import "Firestore/Source/Core/FSTTypes.h"
-#import "Firestore/Source/Model/FSTDocumentVersionDictionary.h"
#include "Firestore/core/src/firebase/firestore/auth/user.h"
diff --git a/Firestore/Source/Remote/FSTRemoteStore.mm b/Firestore/Source/Remote/FSTRemoteStore.mm
index 39d285a..0ea4887 100644
--- a/Firestore/Source/Remote/FSTRemoteStore.mm
+++ b/Firestore/Source/Remote/FSTRemoteStore.mm
@@ -19,7 +19,6 @@
#include <cinttypes>
#import "Firestore/Source/Core/FSTQuery.h"
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
#import "Firestore/Source/Core/FSTTransaction.h"
#import "Firestore/Source/Local/FSTLocalStore.h"
#import "Firestore/Source/Local/FSTQueryData.h"
@@ -37,11 +36,14 @@
#include "Firestore/core/src/firebase/firestore/auth/user.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
namespace util = firebase::firestore::util;
using firebase::firestore::auth::User;
using firebase::firestore::model::DocumentKey;
+using firebase::firestore::model::SnapshotVersion;
+using firebase::firestore::model::DocumentKeySet;
NS_ASSUME_NONNULL_BEGIN
@@ -299,7 +301,7 @@ static const int kMaxPendingWrites = 10;
}
- (void)watchStreamDidChange:(FSTWatchChange *)change
- snapshotVersion:(FSTSnapshotVersion *)snapshotVersion {
+ snapshotVersion:(const SnapshotVersion &)snapshotVersion {
// Mark the connection as Online because we got a message from the server.
[self.onlineStateTracker updateState:FSTOnlineStateOnline];
@@ -315,10 +317,8 @@ static const int kMaxPendingWrites = 10;
// older than a previous snapshot we've processed (can happen after we resume a target
// using a resume token).
[self.accumulatedChanges addObject:change];
- FSTAssert(snapshotVersion, @"snapshotVersion must not be nil.");
- if ([snapshotVersion isEqual:[FSTSnapshotVersion noVersion]] ||
- [snapshotVersion compare:[self.localStore lastRemoteSnapshotVersion]] ==
- NSOrderedAscending) {
+ if (snapshotVersion == SnapshotVersion::None() ||
+ snapshotVersion < [self.localStore lastRemoteSnapshotVersion]) {
return;
}
@@ -354,7 +354,7 @@ static const int kMaxPendingWrites = 10;
* on to the SyncEngine.
*/
- (void)processBatchedWatchChanges:(NSArray<FSTWatchChange *> *)changes
- snapshotVersion:(FSTSnapshotVersion *)snapshotVersion {
+ snapshotVersion:(const SnapshotVersion &)snapshotVersion {
FSTWatchChangeAggregator *aggregator =
[[FSTWatchChangeAggregator alloc] initWithSnapshotVersion:snapshotVersion
listenTargets:self.listenTargets
@@ -394,7 +394,7 @@ static const int kMaxPendingWrites = 10;
} else {
// Not a document query.
- FSTDocumentKeySet *trackedRemote = [self.localStore remoteDocumentKeysForTarget:targetID];
+ DocumentKeySet trackedRemote = [self.localStore remoteDocumentKeysForTarget:targetID];
FSTTargetMapping *mapping = remoteEvent.targetChanges[target].mapping;
if (mapping) {
if ([mapping isKindOfClass:[FSTUpdateMapping class]]) {
@@ -407,7 +407,7 @@ static const int kMaxPendingWrites = 10;
}
}
- if (trackedRemote.count != (NSUInteger)filter.count) {
+ if (trackedRemote.size() != static_cast<size_t>(filter.count)) {
FSTLog(@"Existence filter mismatch, resetting mapping");
// Make sure the mismatch is exposed in the remote event
@@ -449,7 +449,8 @@ static const int kMaxPendingWrites = 10;
if (queryData) {
self->_listenTargets[target] =
[queryData queryDataByReplacingSnapshotVersion:change.snapshotVersion
- resumeToken:resumeToken];
+ resumeToken:resumeToken
+ sequenceNumber:queryData.sequenceNumber];
}
}
}];
@@ -566,7 +567,7 @@ static const int kMaxPendingWrites = 10;
}
/** Handles a successful StreamingWriteResponse from the server that contains a mutation result. */
-- (void)writeStreamDidReceiveResponseWithVersion:(FSTSnapshotVersion *)commitVersion
+- (void)writeStreamDidReceiveResponseWithVersion:(const SnapshotVersion &)commitVersion
mutationResults:(NSArray<FSTMutationResult *> *)results {
// This is a response to a write containing mutations and should be correlated to the first
// pending write.
diff --git a/Firestore/Source/Remote/FSTSerializerBeta.h b/Firestore/Source/Remote/FSTSerializerBeta.h
index d96dbeb..cdf5d1f 100644
--- a/Firestore/Source/Remote/FSTSerializerBeta.h
+++ b/Firestore/Source/Remote/FSTSerializerBeta.h
@@ -16,8 +16,10 @@
#import <Foundation/Foundation.h>
+#include "Firestore/core/include/firebase/firestore/timestamp.h"
#include "Firestore/core/src/firebase/firestore/model/database_id.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
@class FSTFieldValue;
@class FSTMaybeDocument;
@@ -27,8 +29,6 @@
@class FSTObjectValue;
@class FSTQuery;
@class FSTQueryData;
-@class FSTSnapshotVersion;
-@class FIRTimestamp;
@class FSTWatchChange;
@class GCFSBatchGetDocumentsResponse;
@@ -61,15 +61,19 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithDatabaseID:(const firebase::firestore::model::DatabaseId *)databaseID
NS_DESIGNATED_INITIALIZER;
-- (GPBTimestamp *)encodedTimestamp:(FIRTimestamp *)timestamp;
-- (FIRTimestamp *)decodedTimestamp:(GPBTimestamp *)timestamp;
+- (GPBTimestamp *)encodedTimestamp:(const firebase::Timestamp &)timestamp;
+- (firebase::Timestamp)decodedTimestamp:(GPBTimestamp *)timestamp;
-- (GPBTimestamp *)encodedVersion:(FSTSnapshotVersion *)version;
-- (FSTSnapshotVersion *)decodedVersion:(GPBTimestamp *)version;
+- (GPBTimestamp *)encodedVersion:(const firebase::firestore::model::SnapshotVersion &)version;
+- (firebase::firestore::model::SnapshotVersion)decodedVersion:(GPBTimestamp *)version;
/** Returns the database ID, such as `projects/{project id}/databases/{database_id}`. */
- (NSString *)encodedDatabaseID;
+/**
+ * Encodes the given document key as a fully qualified name. This includes the
+ * databaseId associated with this FSTSerializerBeta and the key path.
+ */
- (NSString *)encodedDocumentKey:(const firebase::firestore::model::DocumentKey &)key;
- (firebase::firestore::model::DocumentKey)decodedDocumentKey:(NSString *)key;
@@ -93,7 +97,8 @@ NS_ASSUME_NONNULL_BEGIN
- (FSTQuery *)decodedQueryFromQueryTarget:(GCFSTarget_QueryTarget *)target;
- (FSTWatchChange *)decodedWatchChange:(GCFSListenResponse *)watchChange;
-- (FSTSnapshotVersion *)versionFromListenResponse:(GCFSListenResponse *)watchChange;
+- (firebase::firestore::model::SnapshotVersion)versionFromListenResponse:
+ (GCFSListenResponse *)watchChange;
- (GCFSDocument *)encodedDocumentWithFields:(FSTObjectValue *)objectValue
key:(const firebase::firestore::model::DocumentKey &)key;
diff --git a/Firestore/Source/Remote/FSTSerializerBeta.mm b/Firestore/Source/Remote/FSTSerializerBeta.mm
index 5cbfecc..f862ec3 100644
--- a/Firestore/Source/Remote/FSTSerializerBeta.mm
+++ b/Firestore/Source/Remote/FSTSerializerBeta.mm
@@ -33,9 +33,7 @@
#import "FIRFirestoreErrors.h"
#import "FIRGeoPoint.h"
-#import "FIRTimestamp.h"
#import "Firestore/Source/Core/FSTQuery.h"
-#import "Firestore/Source/Core/FSTSnapshotVersion.h"
#import "Firestore/Source/Local/FSTQueryData.h"
#import "Firestore/Source/Model/FSTDocument.h"
#import "Firestore/Source/Model/FSTFieldValue.h"
@@ -55,8 +53,10 @@
#include "Firestore/core/src/firebase/firestore/model/transform_operations.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
#include "absl/memory/memory.h"
+#include "absl/types/optional.h"
namespace util = firebase::firestore::util;
+using firebase::Timestamp;
using firebase::firestore::model::ArrayTransform;
using firebase::firestore::model::DatabaseId;
using firebase::firestore::model::DocumentKey;
@@ -86,25 +86,25 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
-#pragma mark - FSTSnapshotVersion <=> GPBTimestamp
+#pragma mark - SnapshotVersion <=> GPBTimestamp
-- (GPBTimestamp *)encodedTimestamp:(FIRTimestamp *)timestamp {
+- (GPBTimestamp *)encodedTimestamp:(const Timestamp &)timestamp {
GPBTimestamp *result = [GPBTimestamp message];
- result.seconds = timestamp.seconds;
- result.nanos = timestamp.nanoseconds;
+ result.seconds = timestamp.seconds();
+ result.nanos = timestamp.nanoseconds();
return result;
}
-- (FIRTimestamp *)decodedTimestamp:(GPBTimestamp *)timestamp {
- return [[FIRTimestamp alloc] initWithSeconds:timestamp.seconds nanoseconds:timestamp.nanos];
+- (Timestamp)decodedTimestamp:(GPBTimestamp *)timestamp {
+ return Timestamp{timestamp.seconds, timestamp.nanos};
}
-- (GPBTimestamp *)encodedVersion:(FSTSnapshotVersion *)version {
- return [self encodedTimestamp:version.timestamp];
+- (GPBTimestamp *)encodedVersion:(const SnapshotVersion &)version {
+ return [self encodedTimestamp:version.timestamp()];
}
-- (FSTSnapshotVersion *)decodedVersion:(GPBTimestamp *)version {
- return [FSTSnapshotVersion versionWithTimestamp:[self decodedTimestamp:version]];
+- (SnapshotVersion)decodedVersion:(GPBTimestamp *)version {
+ return SnapshotVersion{[self decodedTimestamp:version]};
}
#pragma mark - FIRGeoPoint <=> GTPLatLng
@@ -206,8 +206,8 @@ NS_ASSUME_NONNULL_BEGIN
return [self encodedString:[fieldValue value]];
} else if (fieldClass == [FSTTimestampValue class]) {
- return [self encodedTimestampValue:[fieldValue value]];
-
+ FIRTimestamp *value = static_cast<FIRTimestamp *>([fieldValue value]);
+ return [self encodedTimestampValue:Timestamp{value.seconds, value.nanoseconds}];
} else if (fieldClass == [FSTGeoPointValue class]) {
return [self encodedGeoPointValue:[fieldValue value]];
@@ -250,8 +250,12 @@ NS_ASSUME_NONNULL_BEGIN
case GCFSValue_ValueType_OneOfCase_StringValue:
return [FSTStringValue stringValue:valueProto.stringValue];
- case GCFSValue_ValueType_OneOfCase_TimestampValue:
- return [FSTTimestampValue timestampValue:[self decodedTimestamp:valueProto.timestampValue]];
+ case GCFSValue_ValueType_OneOfCase_TimestampValue: {
+ Timestamp value = [self decodedTimestamp:valueProto.timestampValue];
+ return [FSTTimestampValue
+ timestampValue:[FIRTimestamp timestampWithSeconds:value.seconds()
+ nanoseconds:value.nanoseconds()]];
+ }
case GCFSValue_ValueType_OneOfCase_GeoPointValue:
return [FSTGeoPointValue geoPointValue:[self decodedGeoPoint:valueProto.geoPointValue]];
@@ -303,7 +307,7 @@ NS_ASSUME_NONNULL_BEGIN
return result;
}
-- (GCFSValue *)encodedTimestampValue:(FIRTimestamp *)value {
+- (GCFSValue *)encodedTimestampValue:(const Timestamp &)value {
GCFSValue *result = [GCFSValue message];
result.timestampValue = [self encodedTimestamp:value];
return result;
@@ -429,8 +433,8 @@ NS_ASSUME_NONNULL_BEGIN
FSTAssert(!!response.found, @"Tried to deserialize a found document from a deleted document.");
const DocumentKey key = [self decodedDocumentKey:response.found.name];
FSTObjectValue *value = [self decodedFields:response.found.fields];
- FSTSnapshotVersion *version = [self decodedVersion:response.found.updateTime];
- FSTAssert(![version isEqual:[FSTSnapshotVersion noVersion]],
+ SnapshotVersion version = [self decodedVersion:response.found.updateTime];
+ FSTAssert(version != SnapshotVersion::None(),
@"Got a document response with no snapshot version");
return [FSTDocument documentWithData:value key:key version:version hasLocalMutations:NO];
@@ -439,8 +443,8 @@ NS_ASSUME_NONNULL_BEGIN
- (FSTDeletedDocument *)decodedDeletedDocument:(GCFSBatchGetDocumentsResponse *)response {
FSTAssert(!!response.missing, @"Tried to deserialize a deleted document from a found document.");
const DocumentKey key = [self decodedDocumentKey:response.missing];
- FSTSnapshotVersion *version = [self decodedVersion:response.readTime];
- FSTAssert(![version isEqual:[FSTSnapshotVersion noVersion]],
+ SnapshotVersion version = [self decodedVersion:response.readTime];
+ FSTAssert(version != SnapshotVersion::None(),
@"Got a no document response with no snapshot version");
return [FSTDeletedDocument documentWithKey:key version:version];
}
@@ -668,8 +672,10 @@ NS_ASSUME_NONNULL_BEGIN
- (FSTMutationResult *)decodedMutationResult:(GCFSWriteResult *)mutation {
// NOTE: Deletes don't have an updateTime.
- FSTSnapshotVersion *_Nullable version =
- mutation.updateTime ? [self decodedVersion:mutation.updateTime] : nil;
+ absl::optional<SnapshotVersion> version;
+ if (mutation.updateTime) {
+ version = [self decodedVersion:mutation.updateTime];
+ }
NSMutableArray *_Nullable transformResults = nil;
if (mutation.transformResultsArray.count > 0) {
transformResults = [NSMutableArray array];
@@ -677,7 +683,8 @@ NS_ASSUME_NONNULL_BEGIN
[transformResults addObject:[self decodedFieldValue:result]];
}
}
- return [[FSTMutationResult alloc] initWithVersion:version transformResults:transformResults];
+ return [[FSTMutationResult alloc] initWithVersion:std::move(version)
+ transformResults:transformResults];
}
#pragma mark - FSTQueryData => GCFSTarget proto
@@ -1071,15 +1078,15 @@ NS_ASSUME_NONNULL_BEGIN
}
}
-- (FSTSnapshotVersion *)versionFromListenResponse:(GCFSListenResponse *)watchChange {
+- (SnapshotVersion)versionFromListenResponse:(GCFSListenResponse *)watchChange {
// We have only reached a consistent snapshot for the entire stream if there is a read_time set
// and it applies to all targets (i.e. the list of targets is empty). The backend is guaranteed to
// send such responses.
if (watchChange.responseTypeOneOfCase != GCFSListenResponse_ResponseType_OneOfCase_TargetChange) {
- return [FSTSnapshotVersion noVersion];
+ return SnapshotVersion::None();
}
if (watchChange.targetChange.targetIdsArray.count != 0) {
- return [FSTSnapshotVersion noVersion];
+ return SnapshotVersion::None();
}
return [self decodedVersion:watchChange.targetChange.readTime];
}
@@ -1135,9 +1142,8 @@ NS_ASSUME_NONNULL_BEGIN
- (FSTDocumentWatchChange *)decodedDocumentChange:(GCFSDocumentChange *)change {
FSTObjectValue *value = [self decodedFields:change.document.fields];
const DocumentKey key = [self decodedDocumentKey:change.document.name];
- FSTSnapshotVersion *version = [self decodedVersion:change.document.updateTime];
- FSTAssert(![version isEqual:[FSTSnapshotVersion noVersion]],
- @"Got a document change with no snapshot version");
+ SnapshotVersion version = [self decodedVersion:change.document.updateTime];
+ FSTAssert(version != SnapshotVersion::None(), @"Got a document change with no snapshot version");
FSTMaybeDocument *document =
[FSTDocument documentWithData:value key:key version:version hasLocalMutations:NO];
@@ -1152,8 +1158,8 @@ NS_ASSUME_NONNULL_BEGIN
- (FSTDocumentWatchChange *)decodedDocumentDelete:(GCFSDocumentDelete *)change {
const DocumentKey key = [self decodedDocumentKey:change.document];
- // Note that version might be unset in which case we use [FSTSnapshotVersion noVersion]
- FSTSnapshotVersion *version = [self decodedVersion:change.readTime];
+ // Note that version might be unset in which case we use SnapshotVersion::None()
+ SnapshotVersion version = [self decodedVersion:change.readTime];
FSTMaybeDocument *document = [FSTDeletedDocument documentWithKey:key version:version];
NSArray<NSNumber *> *removedTargetIds = [self decodedIntegerArray:change.removedTargetIdsArray];
diff --git a/Firestore/Source/Remote/FSTStream.h b/Firestore/Source/Remote/FSTStream.h
index fba79d2..3bd8549 100644
--- a/Firestore/Source/Remote/FSTStream.h
+++ b/Firestore/Source/Remote/FSTStream.h
@@ -21,13 +21,13 @@
#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h"
#include "Firestore/core/src/firebase/firestore/core/database_info.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
@class FSTDispatchQueue;
@class FSTMutation;
@class FSTMutationResult;
@class FSTQueryData;
@class FSTSerializerBeta;
-@class FSTSnapshotVersion;
@class FSTWatchChange;
@class FSTWatchStream;
@class FSTWriteStream;
@@ -179,7 +179,7 @@ NS_ASSUME_NONNULL_BEGIN
* WatchChange responses sent back by the server.
*/
- (void)watchStreamDidChange:(FSTWatchChange *)change
- snapshotVersion:(FSTSnapshotVersion *)snapshotVersion;
+ snapshotVersion:(const firebase::firestore::model::SnapshotVersion &)snapshotVersion;
/**
* Called by the FSTWatchStream when the underlying streaming RPC is interrupted for whatever
@@ -250,7 +250,8 @@ NS_ASSUME_NONNULL_BEGIN
* Called by the FSTWriteStream upon receiving a StreamingWriteResponse from the server that
* contains mutation results.
*/
-- (void)writeStreamDidReceiveResponseWithVersion:(FSTSnapshotVersion *)commitVersion
+- (void)writeStreamDidReceiveResponseWithVersion:
+ (const firebase::firestore::model::SnapshotVersion &)commitVersion
mutationResults:(NSArray<FSTMutationResult *> *)results;
/**
diff --git a/Firestore/Source/Remote/FSTStream.mm b/Firestore/Source/Remote/FSTStream.mm
index a96feae..f4ec675 100644
--- a/Firestore/Source/Remote/FSTStream.mm
+++ b/Firestore/Source/Remote/FSTStream.mm
@@ -36,6 +36,7 @@
#include "Firestore/core/src/firebase/firestore/auth/token.h"
#include "Firestore/core/src/firebase/firestore/core/database_info.h"
#include "Firestore/core/src/firebase/firestore/model/database_id.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
#include "Firestore/core/src/firebase/firestore/util/error_apple.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
@@ -44,6 +45,7 @@ using firebase::firestore::auth::CredentialsProvider;
using firebase::firestore::auth::Token;
using firebase::firestore::core::DatabaseInfo;
using firebase::firestore::model::DatabaseId;
+using firebase::firestore::model::SnapshotVersion;
/**
* Initial backoff time in seconds after an error.
@@ -691,7 +693,7 @@ static const NSTimeInterval kIdleTimeout = 60.0;
[self.backoff reset];
FSTWatchChange *change = [_serializer decodedWatchChange:proto];
- FSTSnapshotVersion *snap = [_serializer versionFromListenResponse:proto];
+ SnapshotVersion snap = [_serializer versionFromListenResponse:proto];
[self.delegate watchStreamDidChange:change snapshotVersion:snap];
}
@@ -807,7 +809,7 @@ static const NSTimeInterval kIdleTimeout = 60.0;
// might be causing an error we want to back off from.
[self.backoff reset];
- FSTSnapshotVersion *commitVersion = [_serializer decodedVersion:response.commitTime];
+ SnapshotVersion commitVersion = [_serializer decodedVersion:response.commitTime];
NSMutableArray<GCFSWriteResult *> *protos = response.writeResultsArray;
NSMutableArray<FSTMutationResult *> *results = [NSMutableArray arrayWithCapacity:protos.count];
for (GCFSWriteResult *proto in protos) {
diff --git a/Firestore/Source/Remote/FSTWatchChange.h b/Firestore/Source/Remote/FSTWatchChange.h
index 8f730de..ed80e1a 100644
--- a/Firestore/Source/Remote/FSTWatchChange.h
+++ b/Firestore/Source/Remote/FSTWatchChange.h
@@ -22,7 +22,6 @@
@class FSTExistenceFilter;
@class FSTMaybeDocument;
-@class FSTSnapshotVersion;
NS_ASSUME_NONNULL_BEGIN
diff --git a/Firestore/Source/Util/FSTAsyncQueryListener.h b/Firestore/Source/Util/FSTAsyncQueryListener.h
index 4888268..06471d8 100644
--- a/Firestore/Source/Util/FSTAsyncQueryListener.h
+++ b/Firestore/Source/Util/FSTAsyncQueryListener.h
@@ -18,6 +18,8 @@
#import "Firestore/Source/Core/FSTViewSnapshot.h"
+#include "Firestore/core/src/firebase/firestore/util/executor.h"
+
NS_ASSUME_NONNULL_BEGIN
@class FSTDispatchQueue;
@@ -28,9 +30,8 @@ NS_ASSUME_NONNULL_BEGIN
*/
@interface FSTAsyncQueryListener : NSObject
-- (instancetype)initWithDispatchQueue:(FSTDispatchQueue *)dispatchQueue
- snapshotHandler:(FSTViewSnapshotHandler)snapshotHandler
- NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithExecutor:(firebase::firestore::util::internal::Executor*)executor
+ snapshotHandler:(FSTViewSnapshotHandler)snapshotHandler NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
diff --git a/Firestore/Source/Util/FSTAsyncQueryListener.mm b/Firestore/Source/Util/FSTAsyncQueryListener.mm
index b72ac57..81dd41f 100644
--- a/Firestore/Source/Util/FSTAsyncQueryListener.mm
+++ b/Firestore/Source/Util/FSTAsyncQueryListener.mm
@@ -18,16 +18,18 @@
#import "Firestore/Source/Util/FSTDispatchQueue.h"
+using firebase::firestore::util::internal::Executor;
+
@implementation FSTAsyncQueryListener {
volatile BOOL _muted;
FSTViewSnapshotHandler _snapshotHandler;
- FSTDispatchQueue *_dispatchQueue;
+ Executor *_executor;
}
-- (instancetype)initWithDispatchQueue:(FSTDispatchQueue *)dispatchQueue
- snapshotHandler:(FSTViewSnapshotHandler)snapshotHandler {
+- (instancetype)initWithExecutor:(Executor *)executor
+ snapshotHandler:(FSTViewSnapshotHandler)snapshotHandler {
if (self = [super init]) {
- _dispatchQueue = dispatchQueue;
+ _executor = executor;
_snapshotHandler = snapshotHandler;
}
return self;
@@ -40,11 +42,11 @@
// users just want to turn on notifications "forever" and don't want to have
// to keep track of our handle to keep them going.
return ^(FSTViewSnapshot *_Nullable snapshot, NSError *_Nullable error) {
- [self->_dispatchQueue dispatchAsync:^{
+ self->_executor->Execute([self, snapshot, error] {
if (!self->_muted) {
self->_snapshotHandler(snapshot, error);
}
- }];
+ });
};
}
diff --git a/Firestore/Source/Util/FSTDispatchQueue.mm b/Firestore/Source/Util/FSTDispatchQueue.mm
index 0974359..01b2732 100644
--- a/Firestore/Source/Util/FSTDispatchQueue.mm
+++ b/Firestore/Source/Util/FSTDispatchQueue.mm
@@ -16,154 +16,66 @@
#import <Foundation/Foundation.h>
-#include <atomic>
+#include <memory>
+#include <utility>
#import "Firestore/Source/Util/FSTAssert.h"
#import "Firestore/Source/Util/FSTDispatchQueue.h"
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- * removeDelayedCallback is used by FSTDelayedCallback and so we pre-declare it before the rest of
- * the FSTDispatchQueue private interface.
- */
-@interface FSTDispatchQueue ()
-- (void)removeDelayedCallback:(FSTDelayedCallback *)callback;
-@end
+#include "Firestore/core/src/firebase/firestore/util/async_queue.h"
+#include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h"
+#include "absl/memory/memory.h"
-#pragma mark - FSTDelayedCallback
-
-/**
- * Represents a callback scheduled to be run in the future on an FSTDispatchQueue.
- *
- * It is created via [FSTDelayedCallback createAndScheduleWithQueue].
- *
- * Supports cancellation (via cancel) and early execution (via skipDelay).
- */
-@interface FSTDelayedCallback ()
+using firebase::firestore::util::AsyncQueue;
+using firebase::firestore::util::DelayedOperation;
+using firebase::firestore::util::TimerId;
+using firebase::firestore::util::internal::Executor;
+using firebase::firestore::util::internal::ExecutorLibdispatch;
-@property(nonatomic, strong, readonly) FSTDispatchQueue *queue;
-@property(nonatomic, assign, readonly) FSTTimerID timerID;
-@property(nonatomic, assign, readonly) NSTimeInterval targetTime;
-@property(nonatomic, copy) void (^callback)();
-/** YES if the callback has been run or canceled. */
-@property(nonatomic, getter=isDone) BOOL done;
+NS_ASSUME_NONNULL_BEGIN
-/**
- * Creates and returns an FSTDelayedCallback that has been scheduled on the provided queue with the
- * provided delay.
- *
- * @param queue The FSTDispatchQueue to run the callback on.
- * @param timerID A FSTTimerID identifying the type of the delayed callback.
- * @param delay The delay before the callback should be scheduled.
- * @param callback The callback block to run.
- * @return The created FSTDelayedCallback instance.
- */
-+ (instancetype)createAndScheduleWithQueue:(FSTDispatchQueue *)queue
- timerID:(FSTTimerID)timerID
- delay:(NSTimeInterval)delay
- callback:(void (^)(void))callback;
+#pragma mark - FSTDelayedCallback
-/**
- * Queues the callback to run immediately (if it hasn't already been run or canceled).
- */
-- (void)skipDelay;
+@interface FSTDelayedCallback () {
+ DelayedOperation _impl;
+}
@end
@implementation FSTDelayedCallback
-- (instancetype)initWithQueue:(FSTDispatchQueue *)queue
- timerID:(FSTTimerID)timerID
- targetTime:(NSTimeInterval)targetTime
- callback:(void (^)(void))callback {
+- (instancetype)initWithImpl:(DelayedOperation &&)impl {
if (self = [super init]) {
- _queue = queue;
- _timerID = timerID;
- _targetTime = targetTime;
- _callback = callback;
- _done = NO;
+ _impl = std::move(impl);
}
return self;
}
-+ (instancetype)createAndScheduleWithQueue:(FSTDispatchQueue *)queue
- timerID:(FSTTimerID)timerID
- delay:(NSTimeInterval)delay
- callback:(void (^)(void))callback {
- NSTimeInterval targetTime = [[NSDate date] timeIntervalSince1970] + delay;
- FSTDelayedCallback *delayedCallback = [[FSTDelayedCallback alloc] initWithQueue:queue
- timerID:timerID
- targetTime:targetTime
- callback:callback];
- [delayedCallback startWithDelay:delay];
- return delayedCallback;
-}
-
-/**
- * Starts the timer. This is called immediately after construction by createAndScheduleWithQueue.
- */
-- (void)startWithDelay:(NSTimeInterval)delay {
- dispatch_time_t delayNs = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
- dispatch_after(delayNs, self.queue.queue, ^{
- [self.queue enterCheckedOperation:^{
- [self delayDidElapse];
- }];
- });
-}
-
-- (void)skipDelay {
- [self.queue dispatchAsyncAllowingSameQueue:^{
- [self delayDidElapse];
- }];
-}
-
- (void)cancel {
- [self.queue verifyIsCurrentQueue];
- if (!self.isDone) {
- // PORTING NOTE: There's no way to actually cancel the dispatched callback, but it'll be a no-op
- // since we set done to YES.
- [self markDone];
- }
-}
-
-- (void)delayDidElapse {
- [self.queue verifyIsCurrentQueue];
- if (!self.isDone) {
- [self markDone];
- self.callback();
- }
-}
-
-/**
- * Marks this delayed callback as done, and notifies the FSTDispatchQueue that it should be removed.
- */
-- (void)markDone {
- self.done = YES;
- [self.queue removeDelayedCallback:self];
+ _impl.Cancel();
}
@end
#pragma mark - FSTDispatchQueue
-@interface FSTDispatchQueue ()
-/**
- * Callbacks scheduled to be queued in the future. Callbacks are automatically removed after they
- * are run or canceled.
- */
-@property(nonatomic, strong, readonly) NSMutableArray<FSTDelayedCallback *> *delayedCallbacks;
-
-- (instancetype)initWithQueue:(dispatch_queue_t)queue NS_DESIGNATED_INITIALIZER;
-
-@end
-
@implementation FSTDispatchQueue {
- /**
- * Flag set while an FSTDispatchQueue operation is currently executing. Used for assertion
- * sanity-checks.
- */
- std::atomic<bool> _operationInProgress;
+ std::unique_ptr<AsyncQueue> _impl;
+}
+
++ (TimerId)convertTimerId:(FSTTimerID)objcTimerID {
+ const TimerId converted = static_cast<TimerId>(objcTimerID);
+ switch (converted) {
+ case TimerId::All:
+ case TimerId::ListenStreamIdle:
+ case TimerId::ListenStreamConnectionBackoff:
+ case TimerId::WriteStreamIdle:
+ case TimerId::WriteStreamConnectionBackoff:
+ case TimerId::OnlineStateTimeout:
+ return converted;
+ default:
+ FSTAssert(false, @"Unknown value of enum FSTTimerID.");
+ }
}
+ (instancetype)queueWith:(dispatch_queue_t)dispatchQueue {
@@ -172,141 +84,50 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithQueue:(dispatch_queue_t)queue {
if (self = [super init]) {
- _operationInProgress = false;
_queue = queue;
- _delayedCallbacks = [NSMutableArray array];
+ auto executor = absl::make_unique<ExecutorLibdispatch>(queue);
+ _impl = absl::make_unique<AsyncQueue>(std::move(executor));
}
return self;
}
- (void)verifyIsCurrentQueue {
- FSTAssert([self onTargetQueue],
- @"We are running on the wrong dispatch queue. Expected '%@' Actual: '%@'",
- [self targetQueueLabel], [self currentQueueLabel]);
- FSTAssert(_operationInProgress,
- @"verifyIsCurrentQueue called outside enterCheckedOperation on queue '%@'",
- [self currentQueueLabel]);
+ _impl->VerifyIsCurrentQueue();
}
- (void)enterCheckedOperation:(void (^)(void))block {
- FSTAssert(!_operationInProgress,
- @"enterCheckedOperation may not be called when an operation is in progress");
- @try {
- _operationInProgress = true;
- [self verifyIsCurrentQueue];
- block();
- } @finally {
- _operationInProgress = false;
- }
+ _impl->ExecuteBlocking([block] { block(); });
}
- (void)dispatchAsync:(void (^)(void))block {
- FSTAssert(![self onTargetQueue] || !_operationInProgress,
- @"dispatchAsync called when we are already running on target dispatch queue '%@'",
- [self targetQueueLabel]);
-
- dispatch_async(self.queue, ^{
- [self enterCheckedOperation:block];
- });
+ _impl->Enqueue([block] { block(); });
}
- (void)dispatchAsyncAllowingSameQueue:(void (^)(void))block {
- dispatch_async(self.queue, ^{
- [self enterCheckedOperation:block];
- });
+ _impl->EnqueueRelaxed([block] { block(); });
}
- (void)dispatchSync:(void (^)(void))block {
- FSTAssert(![self onTargetQueue] || !_operationInProgress,
- @"dispatchSync called when we are already running on target dispatch queue '%@'",
- [self targetQueueLabel]);
-
- dispatch_sync(self.queue, ^{
- [self enterCheckedOperation:block];
- });
+ _impl->EnqueueBlocking([block] { block(); });
}
- (FSTDelayedCallback *)dispatchAfterDelay:(NSTimeInterval)delay
timerID:(FSTTimerID)timerID
block:(void (^)(void))block {
- // While not necessarily harmful, we currently don't expect to have multiple callbacks with the
- // same timerID in the queue, so defensively reject them.
- FSTAssert(![self containsDelayedCallbackWithTimerID:timerID],
- @"Attempted to schedule multiple callbacks with id %ld", (unsigned long)timerID);
- FSTDelayedCallback *delayedCallback = [FSTDelayedCallback createAndScheduleWithQueue:self
- timerID:timerID
- delay:delay
- callback:block];
- [self.delayedCallbacks addObject:delayedCallback];
- return delayedCallback;
+ const AsyncQueue::Milliseconds delayMs =
+ std::chrono::milliseconds(static_cast<long long>(delay * 1000));
+ const TimerId convertedTimerId = [FSTDispatchQueue convertTimerId:timerID];
+ DelayedOperation delayed_operation =
+ _impl->EnqueueAfterDelay(delayMs, convertedTimerId, [block] { block(); });
+ return [[FSTDelayedCallback alloc] initWithImpl:std::move(delayed_operation)];
}
- (BOOL)containsDelayedCallbackWithTimerID:(FSTTimerID)timerID {
- NSUInteger matchIndex = [self.delayedCallbacks
- indexOfObjectPassingTest:^BOOL(FSTDelayedCallback *obj, NSUInteger idx, BOOL *stop) {
- return obj.timerID == timerID;
- }];
- return matchIndex != NSNotFound;
+ return _impl->IsScheduled([FSTDispatchQueue convertTimerId:timerID]);
}
- (void)runDelayedCallbacksUntil:(FSTTimerID)lastTimerID {
- dispatch_semaphore_t doneSemaphore = dispatch_semaphore_create(0);
-
- [self dispatchAsync:^{
- FSTAssert(lastTimerID == FSTTimerIDAll || [self containsDelayedCallbackWithTimerID:lastTimerID],
- @"Attempted to run callbacks until missing timer ID: %ld",
- (unsigned long)lastTimerID);
-
- [self sortDelayedCallbacks];
- for (FSTDelayedCallback *callback in self.delayedCallbacks) {
- [callback skipDelay];
- if (lastTimerID != FSTTimerIDAll && callback.timerID == lastTimerID) {
- break;
- }
- }
-
- // Now that the callbacks are queued, we want to enqueue an additional item to release the
- // 'done' semaphore.
- [self dispatchAsyncAllowingSameQueue:^{
- dispatch_semaphore_signal(doneSemaphore);
- }];
- }];
-
- dispatch_semaphore_wait(doneSemaphore, DISPATCH_TIME_FOREVER);
-}
-
-// NOTE: For performance we could store the callbacks sorted (e.g. using std::priority_queue),
-// but this sort only happens in tests (if runDelayedCallbacksUntil: is called), and the size
-// is guaranteed to be small since we don't allow duplicate TimerIds (of which there are only 4).
-- (void)sortDelayedCallbacks {
- // We want to run callbacks in the same order they'd run if they ran naturally.
- [self.delayedCallbacks
- sortUsingComparator:^NSComparisonResult(FSTDelayedCallback *a, FSTDelayedCallback *b) {
- return a.targetTime < b.targetTime
- ? NSOrderedAscending
- : a.targetTime > b.targetTime ? NSOrderedDescending : NSOrderedSame;
- }];
-}
-
-/** Called by FSTDelayedCallback when a callback is run or canceled. */
-- (void)removeDelayedCallback:(FSTDelayedCallback *)callback {
- NSUInteger index = [self.delayedCallbacks indexOfObject:callback];
- FSTAssert(index != NSNotFound, @"Delayed callback not found.");
- [self.delayedCallbacks removeObjectAtIndex:index];
-}
-
-#pragma mark - Private Methods
-
-- (NSString *)currentQueueLabel {
- return [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)];
-}
-
-- (NSString *)targetQueueLabel {
- return [NSString stringWithUTF8String:dispatch_queue_get_label(self.queue)];
-}
-
-- (BOOL)onTargetQueue {
- return [[self currentQueueLabel] isEqualToString:[self targetQueueLabel]];
+ _impl->RunScheduledOperationsUntil([FSTDispatchQueue convertTimerId:lastTimerID]);
}
@end