diff --git a/LiteCore/tests/VectorRecordTest.cc b/LiteCore/tests/VectorRecordTest.cc index 03826fede..85183bdfd 100644 --- a/LiteCore/tests/VectorRecordTest.cc +++ b/LiteCore/tests/VectorRecordTest.cc @@ -19,6 +19,7 @@ #include "c4.hh" #include "HybridClock.hh" #include "VectorRecord.hh" +#include "DeDuplicateEncoder.hh" #include "VersionVector.hh" #include "fleece/Mutable.hh" #include @@ -275,3 +276,42 @@ TEST_CASE_METHOD(DataFileTestFixture, "VectorRecord legacy revIDs", "[VectorReco cerr << "Storage:\n" << doc.dumpStorage(); } } + +TEST_CASE("DeDuplicateEncoder remove-then-reset undercounted dict (CBL-8812)", "[VectorRecord]") { + // Same underlying Fleece bug as MutableTests.cc's "MutableDict remove then re-set a source + // key" (Fleece submodule), but exercised through the actual code path that crashed in the + // field, not just Fleece's own public count()/iterator: DeDuplicateEncoder's Dict handling + // constructs a Dict::iterator, which for a mutable Dict is backed by HeapDict::kvArray() -- + // the function that sizes its cache array from the (buggy, too-low) count(), then overruns + // it once the real iteration yields more entries than that. This is the one call site that + // actually reaches that path in this codebase (VectorRecord::encodeBodyAndExtra). + // + // NOTE: before HeapDict::setting() is fixed, this test does not fail cleanly -- it crashes + // the whole process. HeapDict::kvArray() is reached through Dict::iterator's public + // constructor, which is implemented as the C-linkage function FLDictIterator_Begin(); a C++ + // exception can never cross an extern "C" boundary; it hits std::terminate() instead, + // uncatchable by any caller, debug build or not. So this test is only safe to run -- and + // only meaningful as a regression check -- once that fix is in place. + Doc doc = Doc::fromJSON("{\"a\":1,\"b\":2,\"c\":3}"_sl); // keeps the parsed data alive + Dict source = doc.asDict(); + REQUIRE(source.count() == 3); + + MutableDict dict = source.mutableCopy(); + REQUIRE(dict.count() == 3); + + dict.remove("b"_sl); + dict.set("b"_sl, 20); + REQUIRE(dict.count() == 3); + + Encoder enc; + DeDuplicateEncoder ddenc(enc); + ddenc.writeValue(dict, 1); + alloc_slice encoded = enc.finish(); + + Doc decoded = Doc(encoded); + Dict roundTrip = decoded.asDict(); + REQUIRE(roundTrip.count() == 3); + CHECK(roundTrip.get("a"_sl).asInt() == 1); + CHECK(roundTrip.get("b"_sl).asInt() == 20); + CHECK(roundTrip.get("c"_sl).asInt() == 3); +} diff --git a/vendor/fleece b/vendor/fleece index c85566da7..ca6e2f075 160000 --- a/vendor/fleece +++ b/vendor/fleece @@ -1 +1 @@ -Subproject commit c85566da78083dd6231e4294cc040aa8876dfef9 +Subproject commit ca6e2f075b507bf6b89168dc0051166247d47edf