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