diff --git a/Firestore/Source/Local/FSTMemoryQueryCache.mm b/Firestore/Source/Local/FSTMemoryQueryCache.mm index 44bf33f7458..0708ecd45c3 100644 --- a/Firestore/Source/Local/FSTMemoryQueryCache.mm +++ b/Firestore/Source/Local/FSTMemoryQueryCache.mm @@ -18,6 +18,7 @@ #import +#include #include #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h" @@ -26,9 +27,12 @@ #import "Firestore/Source/Local/FSTQueryData.h" #import "Firestore/Source/Local/FSTReferenceSet.h" +#include "Firestore/core/src/firebase/firestore/local/memory_query_cache.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" +#include "absl/memory/memory.h" +using firebase::firestore::local::MemoryQueryCache; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::DocumentKeySet; using firebase::firestore::model::ListenSequenceNumber; @@ -37,33 +41,13 @@ NS_ASSUME_NONNULL_BEGIN -@interface FSTMemoryQueryCache () - -/** Maps a query to the data about that query. */ -@property(nonatomic, strong, readonly) NSMutableDictionary *queries; - -/** A ordered bidirectional mapping between documents and the remote target IDs. */ -@property(nonatomic, strong, readonly) FSTReferenceSet *references; - -/** The highest numbered target ID encountered. */ -@property(nonatomic, assign) TargetId highestTargetID; - -@property(nonatomic, assign) ListenSequenceNumber highestListenSequenceNumber; - -@end - @implementation FSTMemoryQueryCache { - FSTMemoryPersistence *_persistence; - /** The last received snapshot version. */ - SnapshotVersion _lastRemoteSnapshotVersion; + std::unique_ptr _cache; } - (instancetype)initWithPersistence:(FSTMemoryPersistence *)persistence { if (self = [super init]) { - _persistence = persistence; - _queries = [NSMutableDictionary dictionary]; - _references = [[FSTReferenceSet alloc] init]; - _lastRemoteSnapshotVersion = SnapshotVersion::None(); + _cache = absl::make_unique(persistence); } return self; } @@ -72,112 +56,74 @@ - (instancetype)initWithPersistence:(FSTMemoryPersistence *)persistence { #pragma mark Query tracking - (TargetId)highestTargetID { - return _highestTargetID; + return _cache->highest_target_id(); } - (ListenSequenceNumber)highestListenSequenceNumber { - return _highestListenSequenceNumber; + return _cache->highest_listen_sequence_number(); } - (const SnapshotVersion &)lastRemoteSnapshotVersion { - return _lastRemoteSnapshotVersion; + return _cache->last_remote_snapshot_version(); } - (void)setLastRemoteSnapshotVersion:(SnapshotVersion)snapshotVersion { - _lastRemoteSnapshotVersion = std::move(snapshotVersion); + _cache->set_last_remote_snapshot_version(std::move(snapshotVersion)); } - (void)addQueryData:(FSTQueryData *)queryData { - self.queries[queryData.query] = queryData; - if (queryData.targetID > self.highestTargetID) { - self.highestTargetID = queryData.targetID; - } - if (queryData.sequenceNumber > self.highestListenSequenceNumber) { - self.highestListenSequenceNumber = queryData.sequenceNumber; - } + _cache->AddTarget(queryData); } - (void)updateQueryData:(FSTQueryData *)queryData { - self.queries[queryData.query] = queryData; - if (queryData.targetID > self.highestTargetID) { - self.highestTargetID = queryData.targetID; - } - if (queryData.sequenceNumber > self.highestListenSequenceNumber) { - self.highestListenSequenceNumber = queryData.sequenceNumber; - } + _cache->UpdateTarget(queryData); } - (int32_t)count { - return (int32_t)[self.queries count]; + return _cache->count(); } - (void)removeQueryData:(FSTQueryData *)queryData { - [self.queries removeObjectForKey:queryData.query]; - [self.references removeReferencesForID:queryData.targetID]; + _cache->RemoveTarget(queryData); } - (nullable FSTQueryData *)queryDataForQuery:(FSTQuery *)query { - return self.queries[query]; + return _cache->GetTarget(query); } - (void)enumerateTargetsUsingBlock:(void (^)(FSTQueryData *queryData, BOOL *stop))block { - [self.queries - enumerateKeysAndObjectsUsingBlock:^(FSTQuery *key, FSTQueryData *queryData, BOOL *stop) { - block(queryData, stop); - }]; + _cache->EnumerateTargets(block); } - (int)removeQueriesThroughSequenceNumber:(ListenSequenceNumber)sequenceNumber liveQueries:(NSDictionary *)liveQueries { - NSMutableArray *toRemove = [NSMutableArray array]; - [self.queries - enumerateKeysAndObjectsUsingBlock:^(FSTQuery *query, FSTQueryData *queryData, BOOL *stop) { - if (queryData.sequenceNumber <= sequenceNumber) { - if (liveQueries[@(queryData.targetID)] == nil) { - [toRemove addObject:query]; - [self.references removeReferencesForID:queryData.targetID]; - } - } - }]; - [self.queries removeObjectsForKeys:toRemove]; - return (int)[toRemove count]; + return _cache->RemoveTargets(sequenceNumber, liveQueries); } #pragma mark Reference tracking - (void)addMatchingKeys:(const DocumentKeySet &)keys forTargetID:(TargetId)targetID { - [self.references addReferencesToKeys:keys forID:targetID]; - for (const DocumentKey &key : keys) { - [_persistence.referenceDelegate addReference:key]; - } + _cache->AddMatchingKeys(keys, targetID); } - (void)removeMatchingKeys:(const DocumentKeySet &)keys forTargetID:(TargetId)targetID { - [self.references removeReferencesToKeys:keys forID:targetID]; - for (const DocumentKey &key : keys) { - [_persistence.referenceDelegate removeReference:key]; - } + _cache->RemoveMatchingKeys(keys, targetID); } - (void)removeMatchingKeysForTargetID:(TargetId)targetID { - [self.references removeReferencesForID:targetID]; + _cache->RemoveAllKeysForTarget(targetID); } - (DocumentKeySet)matchingKeysForTargetID:(TargetId)targetID { - return [self.references referencedKeysForID:targetID]; + return _cache->GetMatchingKeys(targetID); } - (BOOL)containsKey:(const firebase::firestore::model::DocumentKey &)key { - return [self.references containsKey:key]; + return _cache->Contains(key); } - (size_t)byteSizeWithSerializer:(FSTLocalSerializer *)serializer { - __block size_t count = 0; - [self.queries - enumerateKeysAndObjectsUsingBlock:^(FSTQuery *key, FSTQueryData *queryData, BOOL *stop) { - count += [[serializer encodedQueryData:queryData] serializedSize]; - }]; - return count; + return _cache->CalculateByteSize(serializer); } @end diff --git a/Firestore/core/src/firebase/firestore/local/memory_query_cache.h b/Firestore/core/src/firebase/firestore/local/memory_query_cache.h new file mode 100644 index 00000000000..83d99af4d1b --- /dev/null +++ b/Firestore/core/src/firebase/firestore/local/memory_query_cache.h @@ -0,0 +1,123 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_MEMORY_QUERY_CACHE_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_MEMORY_QUERY_CACHE_H_ + +#if !defined(__OBJC__) +#error "For now, this file must only be included by ObjC source files." +#endif // !defined(__OBJC__) + +#import + +#include +#include + +#include "Firestore/core/src/firebase/firestore/model/document_key_set.h" +#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" +#include "Firestore/core/src/firebase/firestore/model/types.h" + +@class FSTLocalSerializer; +@class FSTMemoryPersistence; +@class FSTQuery; +@class FSTQueryData; +@class FSTReferenceSet; + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace local { + +typedef void (^TargetEnumerator)(FSTQueryData*, BOOL*); + +class MemoryQueryCache { + public: + explicit MemoryQueryCache(FSTMemoryPersistence* persistence); + + // Target-related methods + void AddTarget(FSTQueryData* query_data); + + void UpdateTarget(FSTQueryData* query_data); + + void RemoveTarget(FSTQueryData* query_data); + + FSTQueryData* _Nullable GetTarget(FSTQuery* query); + + void EnumerateTargets(TargetEnumerator block); + + int RemoveTargets(model::ListenSequenceNumber upper_bound, + NSDictionary* live_targets); + + // Key-related methods + void AddMatchingKeys(const model::DocumentKeySet& keys, + model::TargetId target_id); + + void RemoveMatchingKeys(const model::DocumentKeySet& keys, + model::TargetId target_id); + + void RemoveAllKeysForTarget(model::TargetId target_id); + + model::DocumentKeySet GetMatchingKeys(model::TargetId target_id); + + bool Contains(const model::DocumentKey& key); + + // Other methods and accessors + size_t CalculateByteSize(FSTLocalSerializer* serializer); + + int32_t count() const { + return static_cast([queries_ count]); + } + + model::ListenSequenceNumber highest_listen_sequence_number() const { + return highest_listen_sequence_number_; + } + + model::TargetId highest_target_id() const { + return highest_target_id_; + } + + const model::SnapshotVersion& last_remote_snapshot_version() const { + return last_remote_snapshot_version_; + } + + void set_last_remote_snapshot_version(model::SnapshotVersion version) { + last_remote_snapshot_version_ = std::move(version); + } + + private: + FSTMemoryPersistence* persistence_; + /** The highest sequence number encountered */ + model::ListenSequenceNumber highest_listen_sequence_number_; + /** The highest numbered target ID encountered. */ + model::TargetId highest_target_id_; + /** The last received snapshot version. */ + model::SnapshotVersion last_remote_snapshot_version_; + + /** Maps a query to the data about that query. */ + NSMutableDictionary* queries_; + /** A ordered bidirectional mapping between documents and the remote target + * IDs. */ + FSTReferenceSet* references_; +}; + +} // namespace local +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_MEMORY_QUERY_CACHE_H_ diff --git a/Firestore/core/src/firebase/firestore/local/memory_query_cache.mm b/Firestore/core/src/firebase/firestore/local/memory_query_cache.mm new file mode 100644 index 00000000000..6d0c1ba0b80 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/local/memory_query_cache.mm @@ -0,0 +1,137 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/local/memory_query_cache.h" +#import + +#import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h" +#import "Firestore/Source/Core/FSTQuery.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" + +using firebase::firestore::model::DocumentKey; +using firebase::firestore::model::DocumentKeySet; +using firebase::firestore::model::ListenSequenceNumber; +using firebase::firestore::model::SnapshotVersion; +using firebase::firestore::model::TargetId; + +namespace firebase { +namespace firestore { +namespace local { + +NS_ASSUME_NONNULL_BEGIN + +MemoryQueryCache::MemoryQueryCache(FSTMemoryPersistence* persistence) + : persistence_(persistence), + highest_listen_sequence_number_(ListenSequenceNumber(0)), + highest_target_id_(TargetId(0)), + last_remote_snapshot_version_(SnapshotVersion::None()), + queries_([NSMutableDictionary dictionary]), + references_([[FSTReferenceSet alloc] init]) { +} + +void MemoryQueryCache::AddTarget(FSTQueryData* query_data) { + queries_[query_data.query] = query_data; + if (query_data.targetID > highest_target_id_) { + highest_target_id_ = query_data.targetID; + } + if (query_data.sequenceNumber > highest_listen_sequence_number_) { + highest_listen_sequence_number_ = query_data.sequenceNumber; + } +} + +void MemoryQueryCache::UpdateTarget(FSTQueryData* query_data) { + // For the memory query cache, adds and updates are treated the same. + AddTarget(query_data); +} + +void MemoryQueryCache::RemoveTarget(FSTQueryData* query_data) { + [queries_ removeObjectForKey:query_data.query]; + [references_ removeReferencesForID:query_data.targetID]; +} + +FSTQueryData* _Nullable MemoryQueryCache::GetTarget(FSTQuery* query) { + return queries_[query]; +} + +void MemoryQueryCache::EnumerateTargets(TargetEnumerator block) { + [queries_ enumerateKeysAndObjectsUsingBlock:^( + FSTQuery* query, FSTQueryData* query_data, BOOL* stop) { + block(query_data, stop); + }]; +} + +int MemoryQueryCache::RemoveTargets( + model::ListenSequenceNumber upper_bound, + NSDictionary* live_targets) { + NSMutableArray* toRemove = [NSMutableArray array]; + [queries_ enumerateKeysAndObjectsUsingBlock:^( + FSTQuery* query, FSTQueryData* queryData, BOOL* stop) { + if (queryData.sequenceNumber <= upper_bound) { + if (live_targets[@(queryData.targetID)] == nil) { + [toRemove addObject:query]; + [references_ removeReferencesForID:queryData.targetID]; + } + } + }]; + [queries_ removeObjectsForKeys:toRemove]; + return (int)[toRemove count]; +} + +void MemoryQueryCache::AddMatchingKeys(const DocumentKeySet& keys, + TargetId target_id) { + [references_ addReferencesToKeys:keys forID:target_id]; + for (const DocumentKey& key : keys) { + [persistence_.referenceDelegate addReference:key]; + } +} + +void MemoryQueryCache::RemoveMatchingKeys(const DocumentKeySet& keys, + TargetId target_id) { + [references_ removeReferencesToKeys:keys forID:target_id]; + for (const DocumentKey& key : keys) { + [persistence_.referenceDelegate removeReference:key]; + } +} + +void MemoryQueryCache::RemoveAllKeysForTarget(TargetId target_id) { + [references_ removeReferencesForID:target_id]; +} + +DocumentKeySet MemoryQueryCache::GetMatchingKeys(TargetId target_id) { + return [references_ referencedKeysForID:target_id]; +} + +bool MemoryQueryCache::Contains(const DocumentKey& key) { + return [references_ containsKey:key]; +} + +size_t MemoryQueryCache::CalculateByteSize(FSTLocalSerializer* serializer) { + __block size_t count = 0; + [queries_ enumerateKeysAndObjectsUsingBlock:^( + FSTQuery* query, FSTQueryData* query_data, BOOL* stop) { + count += [[serializer encodedQueryData:query_data] serializedSize]; + }]; + return count; +} + +NS_ASSUME_NONNULL_END + +} // namespace local +} // namespace firestore +} // namespace firebase \ No newline at end of file