From 7403c0f907df694af8c9ee7d43dc65041242ca21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Fri, 24 Apr 2026 22:14:17 +0200 Subject: [PATCH] dbwrapper: guard `CDBBatch` scratch streams `CDBBatch` already owns key and value serialization buffers, but manually reserved and cleared them on each call. Reserve those scratch buffers once in the constructor, then guard `Write()` and `Erase()` use with `ScopedDataStreamUsage`, requiring empty streams on entry and clearing them on every exit path. `WriteImpl()` and `EraseImpl()` pass the bytes immediately to LevelDB's write batch, which copies them and does not retain pointers. `Clear()` now asserts the guards left both scratch streams clean. Co-authored-by: Andrew Toth --- src/dbwrapper.cpp | 10 +++++++--- src/dbwrapper.h | 24 ++++++++++-------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index 3c574f89391..5bfdc75e654 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -163,6 +163,8 @@ CDBBatch::CDBBatch(const CDBWrapper& _parent) : parent{_parent}, m_impl_batch{std::make_unique()} { + m_key_scratch.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); + m_value_scratch.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE); Clear(); }; @@ -171,13 +173,15 @@ CDBBatch::~CDBBatch() = default; void CDBBatch::Clear() { m_impl_batch->batch.Clear(); + assert(m_key_scratch.empty()); + assert(m_value_scratch.empty()); } -void CDBBatch::WriteImpl(std::span key, DataStream& ssValue) +void CDBBatch::WriteImpl(std::span key, DataStream& value) { leveldb::Slice slKey(CharCast(key.data()), key.size()); - dbwrapper_private::GetObfuscation(parent)(ssValue); - leveldb::Slice slValue(CharCast(ssValue.data()), ssValue.size()); + dbwrapper_private::GetObfuscation(parent)(value); + leveldb::Slice slValue(CharCast(value.data()), value.size()); m_impl_batch->batch.Put(slKey, slValue); } diff --git a/src/dbwrapper.h b/src/dbwrapper.h index 4710af16d9e..6957f32721d 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -79,10 +79,10 @@ private: struct WriteBatchImpl; const std::unique_ptr m_impl_batch; - DataStream ssKey{}; - DataStream ssValue{}; + DataStream m_key_scratch{}; + DataStream m_value_scratch{}; - void WriteImpl(std::span key, DataStream& ssValue); + void WriteImpl(std::span key, DataStream& value); void EraseImpl(std::span key); public: @@ -96,22 +96,18 @@ public: template void Write(const K& key, const V& value) { - ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); - ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE); - ssKey << key; - ssValue << value; - WriteImpl(ssKey, ssValue); - ssKey.clear(); - ssValue.clear(); + ScopedDataStreamUsage scoped_key{m_key_scratch}, scoped_value{m_value_scratch}; + m_key_scratch << key; + m_value_scratch << value; + WriteImpl(m_key_scratch, m_value_scratch); } template void Erase(const K& key) { - ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); - ssKey << key; - EraseImpl(ssKey); - ssKey.clear(); + ScopedDataStreamUsage scoped_key{m_key_scratch}; + m_key_scratch << key; + EraseImpl(m_key_scratch); } size_t ApproximateSize() const;