From 60ca3bc86846178d590e5c271518230205ff31a0 Mon Sep 17 00:00:00 2001 From: Jianmin Zhao Date: Fri, 17 Nov 2023 16:15:36 -0800 Subject: [PATCH] Add a separate property, isCacheable, to SharedKeys. If cacheable, Dict::get(key &keyToFind) will cache the string key to integer key and enhance the performance of the ensuing gets. This is good if the doucment is not changed. For PersistentSharedKey, the library manages the property: it is cacheable only if it is not in a transaction when Dict::get is called. For SharedKeys, the client manages it. If it anticipates the sharedKeys may outlive the underlying document changes, it should call SharedKeys::disableCaching(). --- API/fleece/Expert.hh | 1 + API/fleece/FLExpert.h | 3 +++ .../xcschemes/Fleece Test.xcscheme | 4 +-- Fleece/API_Impl/Fleece.cc | 1 + Fleece/Core/Dict.cc | 2 +- Fleece/Core/Dict.hh | 2 ++ Fleece/Core/SharedKeys.cc | 2 ++ Fleece/Core/SharedKeys.hh | 8 ++++-- Tests/SharedKeysTests.cc | 27 +++++++++++++++++++ Xcode/xcconfigs/Project_Debug.xcconfig | 3 +++ 10 files changed, 48 insertions(+), 5 deletions(-) diff --git a/API/fleece/Expert.hh b/API/fleece/Expert.hh index 8350b671..29de8f8d 100644 --- a/API/fleece/Expert.hh +++ b/API/fleece/Expert.hh @@ -110,6 +110,7 @@ namespace fleece { inline void writeState(const Encoder &enc); unsigned count() const {return FLSharedKeys_Count(_sk);} void revertToCount(unsigned count) {FLSharedKeys_RevertToCount(_sk, count);} + void disableCaching() {if (_sk) FLSharedKeys_DisableCaching(_sk);} operator FLSharedKeys FL_NULLABLE () const {return _sk;} bool operator== (SharedKeys other) const {return _sk == other._sk;} diff --git a/API/fleece/FLExpert.h b/API/fleece/FLExpert.h index 1667dc87..893603cb 100644 --- a/API/fleece/FLExpert.h +++ b/API/fleece/FLExpert.h @@ -154,6 +154,9 @@ extern "C" { /** Reverts an FLSharedKeys by "forgetting" any keys added since it had the count `oldCount`. */ FLEECE_PUBLIC void FLSharedKeys_RevertToCount(FLSharedKeys, unsigned oldCount) FLAPI; + /** Disable caching of the SharedKeys.. */ + FLEECE_PUBLIC void FLSharedKeys_DisableCaching(FLSharedKeys) FLAPI; + /** Increments the reference count of an FLSharedKeys. */ FLEECE_PUBLIC FLSharedKeys FL_NULLABLE FLSharedKeys_Retain(FLSharedKeys FL_NULLABLE) FLAPI; diff --git a/Fleece.xcodeproj/xcshareddata/xcschemes/Fleece Test.xcscheme b/Fleece.xcodeproj/xcshareddata/xcschemes/Fleece Test.xcscheme index dc8613ab..8d902e27 100644 --- a/Fleece.xcodeproj/xcshareddata/xcschemes/Fleece Test.xcscheme +++ b/Fleece.xcodeproj/xcshareddata/xcschemes/Fleece Test.xcscheme @@ -1,7 +1,7 @@ + version = "1.8"> @@ -31,7 +31,7 @@ loa FLSliceResult FLSharedKeys_GetStateData(FLSharedKeys sk) FLAPI {return toSliceResult(sk->stateData());} FLString FLSharedKeys_Decode(FLSharedKeys sk, int key) FLAPI {return sk->decode(key);} void FLSharedKeys_RevertToCount(FLSharedKeys sk, unsigned c) FLAPI {sk->revertToCount(c);} +void FLSharedKeys_DisableCaching(FLSharedKeys sk) FLAPI { sk->disableCaching(); } FLSharedKeys FLSharedKeys_NewWithRead(FLSharedKeysReadCallback callback, void* FL_NULLABLE context) FLAPI { return retain(new FLPersistentSharedKeys(callback, context)); diff --git a/Fleece/Core/Dict.cc b/Fleece/Core/Dict.cc index 758f1b19..0587d172 100644 --- a/Fleece/Core/Dict.cc +++ b/Fleece/Core/Dict.cc @@ -132,7 +132,7 @@ namespace fleece { namespace impl { // shared key, because the transaction might be rolled back. If the found // shared key is rolled back as part of rolling back the transaction, continuing // to use it would lead to incorrect lookup results. - keyToFind._hasNumericKey = !sharedKeys->isInTransaction(); + keyToFind._hasNumericKey = sharedKeys->isCacheable(); return get(keyToFind._numericKey); } } diff --git a/Fleece/Core/Dict.hh b/Fleece/Core/Dict.hh index 211abbfe..6e0d2af5 100644 --- a/Fleece/Core/Dict.hh +++ b/Fleece/Core/Dict.hh @@ -70,7 +70,9 @@ namespace fleece { namespace impl { slice string() const noexcept {return _rawString;} int compare(const key &k) const noexcept {return _rawString.compare(k._rawString);} key(const key&) =delete; +#ifndef LITECORE_CPPTEST private: +#endif void setSharedKeys(SharedKeys*); slice const _rawString; diff --git a/Fleece/Core/SharedKeys.cc b/Fleece/Core/SharedKeys.cc index d5c3c11b..8c35e81c 100644 --- a/Fleece/Core/SharedKeys.cc +++ b/Fleece/Core/SharedKeys.cc @@ -252,6 +252,7 @@ namespace fleece { namespace impl { LOCK(_refreshMutex); throwIf(_inTransaction, SharedKeysStateError, "already in transaction"); _inTransaction = true; + disableCaching(); read(); // Catch up with any external changes } @@ -260,6 +261,7 @@ namespace fleece { namespace impl { if (_inTransaction) { _committedPersistedCount = _persistedCount; _inTransaction = false; + enableCaching(); } } diff --git a/Fleece/Core/SharedKeys.hh b/Fleece/Core/SharedKeys.hh index 2c4f202b..44d3f390 100644 --- a/Fleece/Core/SharedKeys.hh +++ b/Fleece/Core/SharedKeys.hh @@ -106,8 +106,6 @@ namespace fleece { namespace impl { bool isUnknownKey(int key) const FLPURE; - bool isInTransaction() const FLPURE {return _inTransaction;} - virtual bool refresh() {return false;} static const size_t kMaxCount = 2048; // Max number of keys to store @@ -124,7 +122,12 @@ namespace fleece { namespace impl { void setPlatformStringForKey(int key, PlatformString) const; PlatformString platformStringForKey(int key) const; + bool isCacheable() const FLPURE { return _isCacheable; } + void disableCaching() { _isCacheable = false; } + protected: + void enableCaching() { _isCacheable = true; } + virtual ~SharedKeys(); /** Determines whether a new string should be added. Default implementation returns true @@ -143,6 +146,7 @@ namespace fleece { namespace impl { mutable std::mutex _mutex; unsigned _count {0}; bool _inTransaction {true}; // (for PersistentSharedKeys) + bool _isCacheable {true}; // SharedKeys are cacheable unless explicitly disabled. mutable std::vector _platformStringsByKey; // Reverse mapping, int->platform key ConcurrentMap _table; // Hash table mapping slice->int std::array _byKey; // Reverse mapping, int->slice diff --git a/Tests/SharedKeysTests.cc b/Tests/SharedKeysTests.cc index 7cdc27d4..30b0fe5c 100644 --- a/Tests/SharedKeysTests.cc +++ b/Tests/SharedKeysTests.cc @@ -430,9 +430,15 @@ TEST_CASE("encoding", "[SharedKeys]") { Dict::key typeKey("type"_sl), attsKey("_attachments"_sl); const Value *v = root->get(typeKey); +#ifdef LITECORE_CPPTEST + CHECK(typeKey._hasNumericKey); +#endif REQUIRE(v); REQUIRE(v->asString() == "animal"_sl); const Dict *atts = root->get(attsKey)->asDict(); +#ifdef LITECORE_CPPTEST + CHECK(attsKey._hasNumericKey); +#endif REQUIRE(atts); REQUIRE(atts->get("thumbnail.jpg"_sl) != nullptr); REQUIRE(atts->get(typeKey) != nullptr); @@ -442,6 +448,27 @@ TEST_CASE("encoding", "[SharedKeys]") { Dict::key thumbKey("thumbnail.jpg"_sl); REQUIRE(atts->get(thumbKey) != nullptr); } + SECTION("Dict::key Not Cache") { + sk->disableCaching(); + + // Use a Dict::key: + Dict::key typeKey("type"_sl), attsKey("_attachments"_sl); +#ifdef LITECORE_CPPTEST + CHECK(!typeKey._hasNumericKey); +#endif + const Value *v = root->get(typeKey); + REQUIRE(v); + REQUIRE(v->asString() == "animal"_sl); + + const Dict *atts = root->get(attsKey)->asDict(); +#ifdef LITECORE_CPPTEST + CHECK(!attsKey._hasNumericKey); +#endif + REQUIRE(atts); + REQUIRE(atts->get("thumbnail.jpg"_sl) != nullptr); + REQUIRE(atts->get(typeKey) != nullptr); + REQUIRE(atts->get(attsKey) == nullptr); + } SECTION("Path lookup") { Path attsTypePath("_attachments.type"); const Value *t = attsTypePath.eval(root); diff --git a/Xcode/xcconfigs/Project_Debug.xcconfig b/Xcode/xcconfigs/Project_Debug.xcconfig index 9a810a8b..0906912b 100644 --- a/Xcode/xcconfigs/Project_Debug.xcconfig +++ b/Xcode/xcconfigs/Project_Debug.xcconfig @@ -13,3 +13,6 @@ GCC_OPTIMIZATION_LEVEL = 0 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1 ENABLE_TESTABILITY = YES MTL_ENABLE_DEBUG_INFO = YES + +GCC_PREPROCESSOR_DEFINITIONS[config=Test_EE] = $(GCC_PREPROCESSOR_DEFINITIONS) LITECORE_CPPTEST +GCC_PREPROCESSOR_DEFINITIONS[config=Test_CE] = $(GCC_PREPROCESSOR_DEFINITIONS) LITECORE_CPPTEST