Branch data Line data Source code
1 : : // Copyright (c) 2012-present The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #ifndef BITCOIN_DBWRAPPER_H
6 : : #define BITCOIN_DBWRAPPER_H
7 : :
8 : : #include <attributes.h>
9 : : #include <serialize.h>
10 : : #include <span.h>
11 : : #include <streams.h>
12 : : #include <util/byte_units.h>
13 : : #include <util/check.h>
14 : : #include <util/fs.h>
15 : :
16 : : #include <cstddef>
17 : : #include <exception>
18 : : #include <memory>
19 : : #include <optional>
20 : : #include <stdexcept>
21 : : #include <string>
22 : :
23 : : namespace leveldb {
24 : : class Env;
25 : : } // namespace leveldb
26 : :
27 : : static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
28 : : static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
29 : : static const size_t DBWRAPPER_MAX_FILE_SIZE{32_MiB};
30 : :
31 : : //! User-controlled performance and debug options.
32 : : struct DBOptions {
33 : : //! Compact database on startup.
34 : : bool force_compact = false;
35 : : };
36 : :
37 : : //! Application-specific storage settings.
38 [ + - ]: 45550 : struct DBParams {
39 : : //! Location in the filesystem where leveldb data will be stored.
40 : : fs::path path;
41 : : //! Configures various leveldb cache settings.
42 : : size_t cache_bytes;
43 : : //! If true, use leveldb's memory environment.
44 : : bool memory_only = false;
45 : : //! If true, remove all existing data.
46 : : bool wipe_data = false;
47 : : //! If true, store data obfuscated via simple XOR. If false, XOR with a
48 : : //! zero'd byte array.
49 : : bool obfuscate = false;
50 : : //! Passed-through options.
51 : : DBOptions options{};
52 : : //! If non-null, use this as the leveldb::Env instead of the default.
53 : : //! Caller retains ownership.
54 : : leveldb::Env* testing_env = nullptr;
55 : : //! Maximum LevelDB SST file size. Larger values reduce the frequency
56 : : //! of compactions but increase their duration.
57 : : size_t max_file_size = DBWRAPPER_MAX_FILE_SIZE;
58 : : };
59 : :
60 : : class dbwrapper_error : public std::runtime_error
61 : : {
62 : : public:
63 [ # # ]: 0 : explicit dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {}
64 : : };
65 : :
66 : : class CDBWrapper;
67 : :
68 : : /** These should be considered an implementation detail of the specific database.
69 : : */
70 : : namespace dbwrapper_private {
71 : :
72 : : /** Work around circular dependency, as well as for testing in dbwrapper_tests.
73 : : * Database obfuscation should be considered an implementation detail of the
74 : : * specific database.
75 : : */
76 : : const Obfuscation& GetObfuscation(const CDBWrapper&);
77 : : }; // namespace dbwrapper_private
78 : :
79 : : bool DestroyDB(const std::string& path_str);
80 : :
81 : : /** Batch of changes queued to be written to a CDBWrapper */
82 : : class CDBBatch
83 : : {
84 : : friend class CDBWrapper;
85 : :
86 : : private:
87 : : const CDBWrapper &parent;
88 : :
89 : : struct WriteBatchImpl;
90 : : const std::unique_ptr<WriteBatchImpl> m_impl_batch;
91 : :
92 : : DataStream m_key_scratch{};
93 : : DataStream m_value_scratch{};
94 : :
95 : : void WriteImpl(std::span<const std::byte> key, DataStream& value);
96 : : void EraseImpl(std::span<const std::byte> key);
97 : :
98 : : public:
99 : : /**
100 : : * @param[in] _parent CDBWrapper that this batch is to be submitted to
101 : : */
102 : : explicit CDBBatch(const CDBWrapper& _parent);
103 : : ~CDBBatch();
104 : : void Clear();
105 : :
106 : : template <typename K, typename V>
107 : 8633037 : void Write(const K& key, const V& value)
108 : : {
109 : 8633037 : ScopedDataStreamUsage scoped_key{m_key_scratch}, scoped_value{m_value_scratch};
110 [ + - ]: 8633037 : m_key_scratch << key;
111 [ + - ]: 8633037 : m_value_scratch << value;
112 [ - + + - ]: 8633037 : WriteImpl(m_key_scratch, m_value_scratch);
113 : 8633037 : }
114 : :
115 : : template <typename K>
116 : 6743675 : void Erase(const K& key)
117 : : {
118 : 6743675 : ScopedDataStreamUsage scoped_key{m_key_scratch};
119 [ + - ]: 6743675 : m_key_scratch << key;
120 [ - + + - ]: 6743675 : EraseImpl(m_key_scratch);
121 : 6743675 : }
122 : :
123 : : size_t ApproximateSize() const;
124 : : };
125 : :
126 : : class CDBIterator
127 : : {
128 : : public:
129 : : struct IteratorImpl;
130 : :
131 : : private:
132 : : const CDBWrapper &parent;
133 : : const std::unique_ptr<IteratorImpl> m_impl_iter;
134 : : DataStream m_scratch{};
135 : :
136 : : void SeekImpl(std::span<const std::byte> key);
137 : : std::span<const std::byte> GetKeyImpl() const;
138 : : std::span<const std::byte> GetValueImpl() const;
139 : :
140 : : public:
141 : :
142 : : /**
143 : : * @param[in] _parent Parent CDBWrapper instance.
144 : : * @param[in] _piter The original leveldb iterator.
145 : : */
146 : : CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter);
147 : : ~CDBIterator();
148 : :
149 : : bool Valid() const;
150 : :
151 : : void SeekToFirst();
152 : :
153 : 102964 : template<typename K> void Seek(const K& key) {
154 : 102964 : ScopedDataStreamUsage scoped_scratch{m_scratch};
155 [ + - ]: 102964 : m_scratch << key;
156 [ - + + - ]: 102964 : SeekImpl(m_scratch);
157 : 102964 : }
158 : :
159 : : void Next();
160 : :
161 : 4405720 : template<typename K> bool GetKey(K& key) {
162 : : try {
163 [ + - + + ]: 4405720 : SpanReader ssKey{GetKeyImpl()};
164 : 4405720 : ssKey >> key;
165 [ - + ]: 439 : } catch (const std::exception&) {
166 : : return false;
167 : : }
168 : : return true;
169 : : }
170 : :
171 : 4404466 : template<typename V> bool GetValue(V& value) {
172 : : try {
173 : 4404466 : ScopedDataStreamUsage scoped_scratch{m_scratch};
174 [ + - + - ]: 4404466 : m_scratch.write(GetValueImpl());
175 [ + - - + ]: 4404466 : dbwrapper_private::GetObfuscation(parent)(m_scratch);
176 [ + - ]: 4404466 : m_scratch >> value;
177 [ - - ]: 4404466 : } catch (const std::exception&) {
178 : : return false;
179 : : }
180 : 4404466 : return true;
181 : : }
182 : : };
183 : :
184 : : struct LevelDBContext;
185 : :
186 : : class CDBWrapper
187 : : {
188 : : friend const Obfuscation& dbwrapper_private::GetObfuscation(const CDBWrapper&);
189 : : private:
190 : : //! holds all leveldb-specific fields of this class
191 : : std::unique_ptr<LevelDBContext> m_db_context;
192 : :
193 : : //! the name of this database
194 : : std::string m_name;
195 : :
196 : : //! optional XOR-obfuscation of the database
197 : : Obfuscation m_obfuscation;
198 : :
199 : : //! obfuscation key storage key, null-prefixed to avoid collisions
200 : : inline static const std::string OBFUSCATION_KEY{"\000obfuscate_key", 14}; // explicit size to avoid truncation at leading \0
201 : :
202 : : std::optional<std::string> ReadImpl(std::span<const std::byte> key) const;
203 : : bool ExistsImpl(std::span<const std::byte> key) const;
204 : : size_t EstimateSizeImpl(std::span<const std::byte> key1, std::span<const std::byte> key2) const;
205 [ - + ]: 21741222 : auto& DBContext() const LIFETIMEBOUND { return *Assert(m_db_context); }
206 : :
207 : : public:
208 : : CDBWrapper(const DBParams& params);
209 : : ~CDBWrapper();
210 : :
211 : : CDBWrapper(const CDBWrapper&) = delete;
212 : : CDBWrapper& operator=(const CDBWrapper&) = delete;
213 : :
214 : : template <typename K, typename V>
215 : 7102269 : bool Read(const K& key, V& value) const
216 : : {
217 [ + - ]: 7102269 : DataStream ssKey{};
218 [ + - ]: 7102269 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
219 [ - + ]: 7102269 : ssKey << key;
220 [ + - ]: 7102269 : std::optional<std::string> strValue{ReadImpl(ssKey)};
221 [ + + ]: 7102269 : if (!strValue) {
222 : : return false;
223 : : }
224 : : try {
225 : 3698541 : std::span ssValue{MakeWritableByteSpan(*strValue)};
226 [ + - ]: 3698541 : m_obfuscation(ssValue);
227 [ + - ]: 10800810 : SpanReader{ssValue} >> value;
228 [ - - ]: 0 : } catch (const std::exception&) {
229 : : return false;
230 : : }
231 : : return true;
232 : 7102269 : }
233 : :
234 : : template <typename K, typename V>
235 : 6371 : void Write(const K& key, const V& value, bool fSync = false)
236 : : {
237 : 6371 : CDBBatch batch(*this);
238 [ + - ]: 6371 : batch.Write(key, value);
239 [ + - ]: 6371 : WriteBatch(batch, fSync);
240 : 6371 : }
241 : :
242 : : template <typename K>
243 : 6389 : bool Exists(const K& key) const
244 : : {
245 [ + - ]: 6389 : DataStream ssKey{};
246 [ + - ]: 6389 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
247 [ - + ]: 6389 : ssKey << key;
248 [ + - ]: 6389 : return ExistsImpl(ssKey);
249 : 6389 : }
250 : :
251 : : template <typename K>
252 : 439 : void Erase(const K& key, bool fSync = false)
253 : : {
254 : 439 : CDBBatch batch(*this);
255 [ + - ]: 439 : batch.Erase(key);
256 [ + - ]: 439 : WriteBatch(batch, fSync);
257 : 439 : }
258 : :
259 : : void WriteBatch(CDBBatch& batch, bool fSync = false);
260 : :
261 : : // Get an estimate of LevelDB memory usage (in bytes).
262 : : size_t DynamicMemoryUsage() const;
263 : :
264 : : CDBIterator* NewIterator();
265 : :
266 : : /**
267 : : * Return true if the database managed by this class contains no entries.
268 : : */
269 : : bool IsEmpty();
270 : :
271 : : template<typename K>
272 : 99320 : size_t EstimateSize(const K& key_begin, const K& key_end) const
273 : : {
274 [ + - ]: 99320 : DataStream ssKey1{}, ssKey2{};
275 [ + - ]: 99320 : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
276 [ + - ]: 99320 : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
277 [ + - ]: 99320 : ssKey1 << key_begin;
278 [ - + ]: 99320 : ssKey2 << key_end;
279 [ - + + - ]: 99320 : return EstimateSizeImpl(ssKey1, ssKey2);
280 : 99320 : }
281 : : };
282 : :
283 : : #endif // BITCOIN_DBWRAPPER_H
|