diff --git a/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.h b/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.h index 453fcce7845..1f0884fe33a 100644 --- a/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.h +++ b/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.h @@ -20,6 +20,7 @@ #import "Firestore/Source/Local/FSTRemoteDocumentCache.h" +#include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/types.h" NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.mm b/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.mm index 22373f8e8d8..4f4070141aa 100644 --- a/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.mm +++ b/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.mm @@ -22,9 +22,11 @@ #import "Firestore/Source/Local/FSTMemoryPersistence.h" #import "Firestore/Source/Model/FSTDocument.h" +#include "Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/document_map.h" +using firebase::firestore::local::MemoryRemoteDocumentCache; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::DocumentKeySet; using firebase::firestore::model::ListenSequenceNumber; @@ -33,104 +35,38 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Returns an estimate of the number of bytes used to store the given - * document key in memory. This is only an estimate and includes the size - * of the segments of the path, but not any object overhead or path separators. - */ -static size_t FSTDocumentKeyByteSize(const DocumentKey &key) { - size_t count = 0; - for (const auto &segment : key.path()) { - count += segment.size(); - } - return count; -} - -@interface FSTMemoryRemoteDocumentCache () - -@end - @implementation FSTMemoryRemoteDocumentCache { - /** Underlying cache of documents. */ - MaybeDocumentMap _docs; + MemoryRemoteDocumentCache _cache; } - (void)addEntry:(FSTMaybeDocument *)document { - _docs = _docs.insert(document.key, document); + _cache.AddEntry(document); } - (void)removeEntryForKey:(const DocumentKey &)key { - _docs = _docs.erase(key); + _cache.RemoveEntry(key); } - (nullable FSTMaybeDocument *)entryForKey:(const DocumentKey &)key { - auto found = _docs.find(key); - return found != _docs.end() ? found->second : nil; + return _cache.Get(key); } - (MaybeDocumentMap)entriesForKeys:(const DocumentKeySet &)keys { - MaybeDocumentMap results; - for (const DocumentKey &key : keys) { - // Make sure each key has a corresponding entry, which is null in case the document is not - // found. - results = results.insert(key, [self entryForKey:key]); - } - return results; + return _cache.GetAll(keys); } - (DocumentMap)documentsMatchingQuery:(FSTQuery *)query { - DocumentMap result; - - // Documents are ordered by key, so we can use a prefix scan to narrow down the documents - // we need to match the query against. - DocumentKey prefix{query.path.Append("")}; - for (auto it = _docs.lower_bound(prefix); it != _docs.end(); ++it) { - const DocumentKey &key = it->first; - if (!query.path.IsPrefixOf(key.path())) { - break; - } - FSTMaybeDocument *maybeDoc = nil; - auto found = _docs.find(key); - if (found != _docs.end()) { - maybeDoc = found->second; - } - if (![maybeDoc isKindOfClass:[FSTDocument class]]) { - continue; - } - FSTDocument *doc = static_cast(maybeDoc); - if ([query matchesDocument:doc]) { - result = result.insert(key, doc); - } - } - - return result; + return _cache.GetMatchingDocuments(query); } - (std::vector)removeOrphanedDocuments: (FSTMemoryLRUReferenceDelegate *)referenceDelegate throughSequenceNumber:(ListenSequenceNumber)upperBound { - std::vector removed; - MaybeDocumentMap updatedDocs = _docs; - for (const auto &kv : _docs) { - const DocumentKey &docKey = kv.first; - if (![referenceDelegate isPinnedAtSequenceNumber:upperBound document:docKey]) { - updatedDocs = updatedDocs.erase(docKey); - removed.push_back(docKey); - } - } - _docs = updatedDocs; - return removed; + return _cache.RemoveOrphanedDocuments(referenceDelegate, upperBound); } - (size_t)byteSizeWithSerializer:(FSTLocalSerializer *)serializer { - size_t count = 0; - for (const auto &kv : _docs) { - const DocumentKey &key = kv.first; - FSTMaybeDocument *doc = kv.second; - count += FSTDocumentKeyByteSize(key); - count += [[serializer encodedMaybeDocument:doc] serializedSize]; - } - return count; + return _cache.CalculateByteSize(serializer); } @end diff --git a/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h b/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h new file mode 100644 index 00000000000..4c23ef54103 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h @@ -0,0 +1,70 @@ +/* + * 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_REMOTE_DOCUMENT_CACHE_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_MEMORY_REMOTE_DOCUMENT_CACHE_H_ + +#if !defined(__OBJC__) +#error "For now, this file must only be included by ObjC source files." +#endif // !defined(__OBJC__) + +#import + +#include + +#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/document_map.h" +#include "Firestore/core/src/firebase/firestore/model/types.h" + +@class FSTLocalSerializer; +@class FSTMaybeDocument; +@class FSTMemoryLRUReferenceDelegate; +@class FSTQuery; + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace local { + +class MemoryRemoteDocumentCache { + public: + void AddEntry(FSTMaybeDocument *document); + void RemoveEntry(const model::DocumentKey &key); + + FSTMaybeDocument *_Nullable Get(const model::DocumentKey &key); + model::MaybeDocumentMap GetAll(const model::DocumentKeySet &keys); + model::DocumentMap GetMatchingDocuments(FSTQuery *query); + + std::vector RemoveOrphanedDocuments( + FSTMemoryLRUReferenceDelegate *reference_delegate, + model::ListenSequenceNumber upper_bound); + + size_t CalculateByteSize(FSTLocalSerializer *serializer); + + private: + /** Underlying cache of documents. */ + model::MaybeDocumentMap docs_; +}; + +} // namespace local +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_MEMORY_REMOTE_DOCUMENT_CACHE_H_ diff --git a/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.mm b/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.mm new file mode 100644 index 00000000000..b7535309e83 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.mm @@ -0,0 +1,125 @@ +/* + * 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_remote_document_cache.h" + +#import "Firestore/Protos/objc/firestore/local/MaybeDocument.pbobjc.h" +#import "Firestore/Source/Core/FSTQuery.h" +#import "Firestore/Source/Local/FSTMemoryPersistence.h" + +using firebase::firestore::model::DocumentKey; +using firebase::firestore::model::DocumentKeySet; +using firebase::firestore::model::DocumentMap; +using firebase::firestore::model::ListenSequenceNumber; +using firebase::firestore::model::MaybeDocumentMap; + +namespace firebase { +namespace firestore { +namespace local { + +namespace { +/** + * Returns an estimate of the number of bytes used to store the given + * document key in memory. This is only an estimate and includes the size + * of the segments of the path, but not any object overhead or path separators. + */ +size_t DocumentKeyByteSize(const DocumentKey &key) { + size_t count = 0; + for (const auto &segment : key.path()) { + count += segment.size(); + } + return count; +} +} // namespace + +void MemoryRemoteDocumentCache::AddEntry(FSTMaybeDocument *document) { + docs_ = docs_.insert(document.key, document); +} + +void MemoryRemoteDocumentCache::RemoveEntry(const DocumentKey &key) { + docs_ = docs_.erase(key); +} + +FSTMaybeDocument *_Nullable MemoryRemoteDocumentCache::Get( + const DocumentKey &key) { + auto found = docs_.find(key); + return found != docs_.end() ? found->second : nil; +} + +MaybeDocumentMap MemoryRemoteDocumentCache::GetAll(const DocumentKeySet &keys) { + MaybeDocumentMap results; + for (const DocumentKey &key : keys) { + // Make sure each key has a corresponding entry, which is null in case the + // document is not found. + // TODO(http://b/32275378): Don't conflate missing / deleted. + results = results.insert(key, Get(key)); + } + return results; +} + +DocumentMap MemoryRemoteDocumentCache::GetMatchingDocuments(FSTQuery *query) { + DocumentMap results; + + // Documents are ordered by key, so we can use a prefix scan to narrow down + // the documents we need to match the query against. + DocumentKey prefix{query.path.Append("")}; + for (auto it = docs_.lower_bound(prefix); it != docs_.end(); ++it) { + const DocumentKey &key = it->first; + if (!query.path.IsPrefixOf(key.path())) { + break; + } + FSTMaybeDocument *maybeDoc = it->second; + if (![maybeDoc isKindOfClass:[FSTDocument class]]) { + continue; + } + FSTDocument *doc = static_cast(maybeDoc); + if ([query matchesDocument:doc]) { + results = results.insert(key, doc); + } + } + return results; +} + +std::vector MemoryRemoteDocumentCache::RemoveOrphanedDocuments( + FSTMemoryLRUReferenceDelegate *reference_delegate, + ListenSequenceNumber upper_bound) { + std::vector removed; + MaybeDocumentMap updated_docs = docs_; + for (const auto &kv : docs_) { + const DocumentKey &key = kv.first; + if (![reference_delegate isPinnedAtSequenceNumber:upper_bound + document:key]) { + updated_docs = updated_docs.erase(key); + removed.push_back(key); + } + } + docs_ = updated_docs; + return removed; +} + +size_t MemoryRemoteDocumentCache::CalculateByteSize( + FSTLocalSerializer *serializer) { + size_t count = 0; + for (const auto &kv : docs_) { + count += DocumentKeyByteSize(kv.first); + count += [[serializer encodedMaybeDocument:kv.second] serializedSize]; + } + return count; +} + +} // namespace local +} // namespace firestore +} // namespace firebase \ No newline at end of file