/root/bitcoin/src/dbwrapper.cpp
Line | Count | Source |
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 | | #include <dbwrapper.h> |
6 | | |
7 | | #include <leveldb/cache.h> |
8 | | #include <leveldb/db.h> |
9 | | #include <leveldb/env.h> |
10 | | #include <leveldb/filter_policy.h> |
11 | | #include <leveldb/helpers/memenv/memenv.h> |
12 | | #include <leveldb/iterator.h> |
13 | | #include <leveldb/options.h> |
14 | | #include <leveldb/slice.h> |
15 | | #include <leveldb/status.h> |
16 | | #include <leveldb/write_batch.h> |
17 | | #include <random.h> |
18 | | #include <serialize.h> |
19 | | #include <span.h> |
20 | | #include <streams.h> |
21 | | #include <util/byte_units.h> |
22 | | #include <util/fs.h> |
23 | | #include <util/fs_helpers.h> |
24 | | #include <util/log.h> |
25 | | #include <util/obfuscation.h> |
26 | | #include <util/strencodings.h> |
27 | | |
28 | | #include <algorithm> |
29 | | #include <cassert> |
30 | | #include <cstdarg> |
31 | | #include <cstdint> |
32 | | #include <cstdio> |
33 | | #include <memory> |
34 | | #include <optional> |
35 | | #include <utility> |
36 | | |
37 | 34.0M | static auto CharCast(const std::byte* data) { return reinterpret_cast<const char*>(data); } |
38 | | |
39 | | bool DestroyDB(const std::string& path_str) |
40 | 0 | { |
41 | 0 | return leveldb::DestroyDB(path_str, {}).ok(); |
42 | 0 | } |
43 | | |
44 | | /** Handle database error by throwing dbwrapper_error exception. |
45 | | */ |
46 | | static void HandleError(const leveldb::Status& status) |
47 | 3.55M | { |
48 | 3.55M | if (status.ok()) Branch (48:9): [True: 3.55M, False: 0]
|
49 | 3.55M | return; |
50 | 0 | const std::string errmsg = "Fatal LevelDB error: " + status.ToString(); |
51 | 0 | LogError("%s", errmsg); |
52 | 0 | LogInfo("You can use -debug=leveldb to get more complete diagnostic messages"); |
53 | 0 | throw dbwrapper_error(errmsg); |
54 | 3.55M | } |
55 | | |
56 | | class CBitcoinLevelDBLogger : public leveldb::Logger { |
57 | | public: |
58 | | // This code is adapted from posix_logger.h, which is why it is using vsprintf. |
59 | | // Please do not do this in normal code |
60 | 1.92M | void Logv(const char * format, va_list ap) override { |
61 | 1.92M | if (!util::log::ShouldDebugLog(BCLog::LEVELDB)) { Branch (61:17): [True: 1.92M, False: 0]
|
62 | 1.92M | return; |
63 | 1.92M | } |
64 | 0 | char buffer[500]; |
65 | 0 | for (int iter = 0; iter < 2; iter++) { Branch (65:32): [True: 0, False: 0]
|
66 | 0 | char* base; |
67 | 0 | int bufsize; |
68 | 0 | if (iter == 0) { Branch (68:21): [True: 0, False: 0]
|
69 | 0 | bufsize = sizeof(buffer); |
70 | 0 | base = buffer; |
71 | 0 | } |
72 | 0 | else { |
73 | 0 | bufsize = 30000; |
74 | 0 | base = new char[bufsize]; |
75 | 0 | } |
76 | 0 | char* p = base; |
77 | 0 | char* limit = base + bufsize; |
78 | | |
79 | | // Print the message |
80 | 0 | if (p < limit) { Branch (80:21): [True: 0, False: 0]
|
81 | 0 | va_list backup_ap; |
82 | 0 | va_copy(backup_ap, ap); |
83 | | // Do not use vsnprintf elsewhere in bitcoin source code, see above. |
84 | 0 | p += vsnprintf(p, limit - p, format, backup_ap); |
85 | 0 | va_end(backup_ap); |
86 | 0 | } |
87 | | |
88 | | // Truncate to available space if necessary |
89 | 0 | if (p >= limit) { Branch (89:21): [True: 0, False: 0]
|
90 | 0 | if (iter == 0) { Branch (90:25): [True: 0, False: 0]
|
91 | 0 | continue; // Try again with larger buffer |
92 | 0 | } |
93 | 0 | else { |
94 | 0 | p = limit - 1; |
95 | 0 | } |
96 | 0 | } |
97 | | |
98 | | // Add newline if necessary |
99 | 0 | if (p == base || p[-1] != '\n') { Branch (99:21): [True: 0, False: 0]
Branch (99:34): [True: 0, False: 0]
|
100 | 0 | *p++ = '\n'; |
101 | 0 | } |
102 | |
|
103 | 0 | assert(p <= limit); Branch (103:17): [True: 0, False: 0]
|
104 | 0 | base[std::min(bufsize - 1, (int)(p - base))] = '\0'; |
105 | 0 | LogDebug(BCLog::LEVELDB, "%s\n", util::RemoveSuffixView(base, "\n")); |
106 | 0 | if (base != buffer) { Branch (106:21): [True: 0, False: 0]
|
107 | 0 | delete[] base; |
108 | 0 | } |
109 | 0 | break; |
110 | 0 | } |
111 | 0 | } |
112 | | }; |
113 | | |
114 | 111k | static void SetMaxOpenFiles(leveldb::Options *options) { |
115 | | // On most platforms the default setting of max_open_files (which is 1000) |
116 | | // is optimal. On Windows using a large file count is OK because the handles |
117 | | // do not interfere with select() loops. On 64-bit Unix hosts this value is |
118 | | // also OK, because up to that amount LevelDB will use an mmap |
119 | | // implementation that does not use extra file descriptors (the fds are |
120 | | // closed after being mmap'ed). |
121 | | // |
122 | | // Increasing the value beyond the default is dangerous because LevelDB will |
123 | | // fall back to a non-mmap implementation when the file count is too large. |
124 | | // On 32-bit Unix host we should decrease the value because the handles use |
125 | | // up real fds, and we want to avoid fd exhaustion issues. |
126 | | // |
127 | | // See PR #12495 for further discussion. |
128 | | |
129 | 111k | int default_open_files = options->max_open_files; |
130 | 111k | #ifndef WIN32 |
131 | 111k | if (sizeof(void*) < 8) { Branch (131:9): [Folded - Ignored]
|
132 | 0 | options->max_open_files = 64; |
133 | 0 | } |
134 | 111k | #endif |
135 | 111k | LogDebug(BCLog::LEVELDB, "LevelDB using max_open_files=%d (default=%d)\n", |
136 | 111k | options->max_open_files, default_open_files); |
137 | 111k | } |
138 | | |
139 | | static leveldb::Options GetOptions(size_t nCacheSize, bool bloom_filter) |
140 | 111k | { |
141 | 111k | leveldb::Options options; |
142 | 111k | options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); |
143 | 111k | options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously |
144 | 111k | options.filter_policy = bloom_filter ? leveldb::NewBloomFilterPolicy(10) : nullptr; Branch (144:29): [True: 100k, False: 10.2k]
|
145 | 111k | options.compression = leveldb::kNoCompression; |
146 | 111k | options.info_log = new CBitcoinLevelDBLogger(); |
147 | 111k | if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) { Branch (147:9): [Folded - Ignored]
Branch (147:40): [Folded - Ignored]
Branch (147:71): [Folded - Ignored]
|
148 | | // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error |
149 | | // on corruption in later versions. |
150 | 111k | options.paranoid_checks = true; |
151 | 111k | } |
152 | 111k | SetMaxOpenFiles(&options); |
153 | 111k | return options; |
154 | 111k | } |
155 | | |
156 | | bool CDBWrapper::HasKeyStartingWith(const fs::path& path, uint8_t prefix) |
157 | 0 | { |
158 | 0 | if (!fs::exists(path / "CURRENT")) return false; Branch (158:9): [True: 0, False: 0]
|
159 | | |
160 | 0 | CBitcoinLevelDBLogger logger; |
161 | 0 | leveldb::Options options; |
162 | 0 | options.paranoid_checks = true; |
163 | | // Avoid creating or rotating LevelDB's LOG files during this probe. |
164 | 0 | options.info_log = &logger; |
165 | |
|
166 | 0 | leveldb::DB* raw_db; |
167 | 0 | HandleError(leveldb::DB::Open(options, fs::PathToString(path), &raw_db)); |
168 | 0 | const std::unique_ptr<leveldb::DB> db{raw_db}; |
169 | |
|
170 | 0 | leveldb::ReadOptions iteroptions; |
171 | 0 | iteroptions.verify_checksums = true; |
172 | 0 | iteroptions.fill_cache = false; |
173 | 0 | const std::unique_ptr<leveldb::Iterator> it{db->NewIterator(iteroptions)}; |
174 | 0 | const leveldb::Slice prefix_slice{reinterpret_cast<const char*>(&prefix), sizeof(prefix)}; |
175 | 0 | it->Seek(prefix_slice); |
176 | 0 | HandleError(it->status()); |
177 | 0 | return it->Valid() && it->key().starts_with(prefix_slice); Branch (177:12): [True: 0, False: 0]
Branch (177:27): [True: 0, False: 0]
|
178 | 0 | } |
179 | | |
180 | | struct CDBBatch::WriteBatchImpl { |
181 | | leveldb::WriteBatch batch; |
182 | | }; |
183 | | |
184 | | CDBBatch::CDBBatch(const CDBWrapper& _parent) |
185 | 3.44M | : parent{_parent}, |
186 | 3.44M | m_impl_batch{std::make_unique<CDBBatch::WriteBatchImpl>()} |
187 | 3.44M | { |
188 | 3.44M | m_key_scratch.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); |
189 | 3.44M | m_value_scratch.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE); |
190 | 3.44M | Clear(); |
191 | 3.44M | }; |
192 | | |
193 | 3.44M | CDBBatch::~CDBBatch() = default; |
194 | | |
195 | | void CDBBatch::Clear() |
196 | 3.45M | { |
197 | 3.45M | m_impl_batch->batch.Clear(); |
198 | 3.45M | assert(m_key_scratch.empty()); Branch (198:5): [True: 3.45M, False: 0]
|
199 | 3.45M | assert(m_value_scratch.empty()); Branch (199:5): [True: 3.45M, False: 0]
|
200 | 3.45M | } |
201 | | |
202 | | void CDBBatch::WriteImpl(std::span<const std::byte> key, DataStream& value) |
203 | 9.80M | { |
204 | 9.80M | leveldb::Slice slKey(CharCast(key.data()), key.size()); |
205 | 9.80M | dbwrapper_private::GetObfuscation(parent)(value); |
206 | 9.80M | leveldb::Slice slValue(CharCast(value.data()), value.size()); |
207 | 9.80M | m_impl_batch->batch.Put(slKey, slValue); |
208 | 9.80M | } |
209 | | |
210 | | void CDBBatch::EraseImpl(std::span<const std::byte> key) |
211 | 6.49M | { |
212 | 6.49M | leveldb::Slice slKey(CharCast(key.data()), key.size()); |
213 | 6.49M | m_impl_batch->batch.Delete(slKey); |
214 | 6.49M | } |
215 | | |
216 | | size_t CDBBatch::ApproximateSize() const |
217 | 370k | { |
218 | 370k | return m_impl_batch->batch.ApproximateSize(); |
219 | 370k | } |
220 | | |
221 | | struct LevelDBContext { |
222 | | //! custom environment this database is using (may be nullptr in case of default environment) |
223 | | leveldb::Env* penv; |
224 | | |
225 | | //! database options used |
226 | | leveldb::Options options; |
227 | | |
228 | | //! options used when reading from the database |
229 | | leveldb::ReadOptions readoptions; |
230 | | |
231 | | //! options used when iterating over values of the database |
232 | | leveldb::ReadOptions iteroptions; |
233 | | |
234 | | //! options used when writing to the database |
235 | | leveldb::WriteOptions writeoptions; |
236 | | |
237 | | //! options used when sync writing to the database |
238 | | leveldb::WriteOptions syncoptions; |
239 | | |
240 | | //! the database itself |
241 | | leveldb::DB* pdb; |
242 | | }; |
243 | | |
244 | | CDBWrapper::CDBWrapper(const DBParams& params) |
245 | 111k | : m_db_context{std::make_unique<LevelDBContext>()}, m_name{fs::PathToString(params.path.stem())} |
246 | 111k | { |
247 | 111k | DBContext().penv = nullptr; |
248 | 111k | DBContext().readoptions.verify_checksums = true; |
249 | 111k | DBContext().iteroptions.verify_checksums = true; |
250 | 111k | DBContext().iteroptions.fill_cache = false; |
251 | 111k | DBContext().syncoptions.sync = true; |
252 | 111k | DBContext().options = GetOptions(params.cache_bytes, params.bloom_filter); |
253 | 111k | DBContext().options.create_if_missing = true; |
254 | 111k | DBContext().options.max_file_size = params.max_file_size; |
255 | 111k | assert(!(params.testing_env && params.memory_only)); Branch (255:5): [True: 95.5k, False: 15.6k]
Branch (255:5): [True: 0, False: 95.5k]
Branch (255:5): [True: 111k, False: 0]
|
256 | 111k | if (params.testing_env) { Branch (256:9): [True: 95.5k, False: 15.6k]
|
257 | 95.5k | DBContext().options.env = params.testing_env; |
258 | 95.5k | } else if (params.memory_only) { Branch (258:16): [True: 15.6k, False: 0]
|
259 | 15.6k | DBContext().penv = leveldb::NewMemEnv(leveldb::Env::Default()); |
260 | 15.6k | DBContext().options.env = DBContext().penv; |
261 | 15.6k | } |
262 | 111k | if (!params.memory_only) { Branch (262:9): [True: 95.5k, False: 15.6k]
|
263 | 95.5k | if (params.wipe_data) { Branch (263:13): [True: 0, False: 95.5k]
|
264 | 0 | LogInfo("Wiping LevelDB in %s", fs::PathToString(params.path)); |
265 | 0 | leveldb::Status result = leveldb::DestroyDB(fs::PathToString(params.path), DBContext().options); |
266 | 0 | HandleError(result); |
267 | 0 | } |
268 | 95.5k | if (!params.testing_env) { Branch (268:13): [True: 0, False: 95.5k]
|
269 | 0 | TryCreateDirectories(params.path); |
270 | 0 | } |
271 | 95.5k | LogInfo("Opening LevelDB in %s", fs::PathToString(params.path)); |
272 | 95.5k | } |
273 | | // PathToString() return value is safe to pass to leveldb open function, |
274 | | // because on POSIX leveldb passes the byte string directly to ::open(), and |
275 | | // on Windows it converts from UTF-8 to UTF-16 before calling ::CreateFileW |
276 | | // (see env_posix.cc and env_windows.cc). |
277 | 111k | leveldb::Status status = leveldb::DB::Open(DBContext().options, fs::PathToString(params.path), &DBContext().pdb); |
278 | 111k | HandleError(status); |
279 | 111k | LogInfo("Opened LevelDB successfully"); |
280 | | |
281 | 111k | if (params.options.force_compact) { Branch (281:9): [True: 30.8k, False: 80.3k]
|
282 | 30.8k | LogInfo("Starting database compaction of %s", fs::PathToString(params.path)); |
283 | 30.8k | CompactFull(); |
284 | 30.8k | LogInfo("Finished database compaction of %s", fs::PathToString(params.path)); |
285 | 30.8k | } |
286 | | |
287 | 111k | if (!Read(OBFUSCATION_KEY, m_obfuscation) && params.obfuscate && IsEmpty()) { Branch (287:9): [True: 45.5k, False: 65.6k]
Branch (287:50): [True: 6.45k, False: 39.1k]
Branch (287:70): [True: 6.45k, False: 0]
|
288 | | // Generate and write the new obfuscation key. |
289 | 6.45k | const Obfuscation obfuscation{FastRandomContext{}.randbytes<Obfuscation::KEY_SIZE>()}; |
290 | 6.45k | assert(!m_obfuscation); // Make sure the key is written without obfuscation. Branch (290:9): [True: 6.45k, False: 0]
|
291 | 6.45k | Write(OBFUSCATION_KEY, obfuscation); |
292 | 6.45k | m_obfuscation = obfuscation; |
293 | 6.45k | LogInfo("Wrote new obfuscation key for %s: %s", fs::PathToString(params.path), m_obfuscation.HexKey()); |
294 | 6.45k | } |
295 | 111k | LogInfo("Using obfuscation key for %s: %s", fs::PathToString(params.path), m_obfuscation.HexKey()); |
296 | 111k | } |
297 | | |
298 | | CDBWrapper::~CDBWrapper() |
299 | 111k | { |
300 | 111k | delete DBContext().pdb; |
301 | 111k | DBContext().pdb = nullptr; |
302 | 111k | delete DBContext().options.filter_policy; |
303 | 111k | DBContext().options.filter_policy = nullptr; |
304 | 111k | delete DBContext().options.info_log; |
305 | 111k | DBContext().options.info_log = nullptr; |
306 | 111k | delete DBContext().options.block_cache; |
307 | 111k | DBContext().options.block_cache = nullptr; |
308 | 111k | delete DBContext().penv; |
309 | 111k | DBContext().options.env = nullptr; |
310 | 111k | } |
311 | | |
312 | | void CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync) |
313 | 3.44M | { |
314 | 3.44M | const bool log_memory = util::log::ShouldDebugLog(BCLog::LEVELDB); |
315 | 3.44M | double mem_before = 0; |
316 | 3.44M | if (log_memory) { Branch (316:9): [True: 86, False: 3.44M]
|
317 | 86 | mem_before = DynamicMemoryUsage() / double(1_MiB); |
318 | 86 | } |
319 | 3.44M | leveldb::Status status = DBContext().pdb->Write(fSync ? DBContext().syncoptions : DBContext().writeoptions, &batch.m_impl_batch->batch); Branch (319:53): [True: 273k, False: 3.17M]
|
320 | 3.44M | HandleError(status); |
321 | 3.44M | if (log_memory) { Branch (321:9): [True: 86, False: 3.44M]
|
322 | 86 | double mem_after{DynamicMemoryUsage() / double(1_MiB)}; |
323 | 86 | LogDebug(BCLog::LEVELDB, "WriteBatch memory usage: db=%s, before=%.1fMiB, after=%.1fMiB\n", |
324 | 86 | m_name, mem_before, mem_after); |
325 | 86 | } |
326 | 3.44M | } |
327 | | |
328 | | std::optional<std::string> CDBWrapper::GetProperty(const std::string& property) const |
329 | 11.3k | { |
330 | 11.3k | if (std::string value; DBContext().pdb->GetProperty(property, &value)) return value; Branch (330:28): [True: 11.3k, False: 0]
|
331 | 0 | return std::nullopt; |
332 | 11.3k | } |
333 | | |
334 | 126k | void CDBWrapper::CompactFull() { DBContext().pdb->CompactRange(nullptr, nullptr); } |
335 | | |
336 | | size_t CDBWrapper::DynamicMemoryUsage() const |
337 | 11.3k | { |
338 | 11.3k | std::optional<size_t> parsed; |
339 | 11.3k | if (auto memory{GetProperty("leveldb.approximate-memory-usage")}; !memory || !(parsed = ToIntegral<size_t>(*memory))) { Branch (339:71): [True: 0, False: 11.3k]
Branch (339:71): [True: 0, False: 11.3k]
Branch (339:82): [True: 0, False: 11.3k]
|
340 | 0 | LogDebug(BCLog::LEVELDB, "Failed to get approximate-memory-usage property\n"); |
341 | 0 | return 0; |
342 | 0 | } |
343 | 11.3k | return parsed.value(); |
344 | 11.3k | } |
345 | | |
346 | | std::optional<std::string> CDBWrapper::ReadImpl(std::span<const std::byte> key) const |
347 | 7.31M | { |
348 | 7.31M | leveldb::Slice slKey(CharCast(key.data()), key.size()); |
349 | 7.31M | std::string strValue; |
350 | 7.31M | leveldb::Status status = DBContext().pdb->Get(DBContext().readoptions, slKey, &strValue); |
351 | 7.31M | if (!status.ok()) { Branch (351:9): [True: 3.30M, False: 4.01M]
|
352 | 3.30M | if (status.IsNotFound()) Branch (352:13): [True: 3.30M, False: 12]
|
353 | 3.30M | return std::nullopt; |
354 | 12 | LogError("LevelDB read failure: %s", status.ToString()); |
355 | 12 | HandleError(status); |
356 | 12 | } |
357 | 4.01M | return strValue; |
358 | 7.31M | } |
359 | | |
360 | | bool CDBWrapper::ExistsImpl(std::span<const std::byte> key) const |
361 | 53.2k | { |
362 | 53.2k | leveldb::Slice slKey(CharCast(key.data()), key.size()); |
363 | | |
364 | 53.2k | std::string strValue; |
365 | 53.2k | leveldb::Status status = DBContext().pdb->Get(DBContext().readoptions, slKey, &strValue); |
366 | 53.2k | if (!status.ok()) { Branch (366:9): [True: 29.4k, False: 23.8k]
|
367 | 29.4k | if (status.IsNotFound()) Branch (367:13): [True: 29.4k, False: 0]
|
368 | 29.4k | return false; |
369 | 0 | LogError("LevelDB read failure: %s", status.ToString()); |
370 | 0 | HandleError(status); |
371 | 0 | } |
372 | 23.8k | return true; |
373 | 53.2k | } |
374 | | |
375 | | size_t CDBWrapper::EstimateSizeImpl(std::span<const std::byte> key1, std::span<const std::byte> key2) const |
376 | 155k | { |
377 | 155k | leveldb::Slice slKey1(CharCast(key1.data()), key1.size()); |
378 | 155k | leveldb::Slice slKey2(CharCast(key2.data()), key2.size()); |
379 | 155k | uint64_t size = 0; |
380 | 155k | leveldb::Range range(slKey1, slKey2); |
381 | 155k | DBContext().pdb->GetApproximateSizes(&range, 1, &size); |
382 | 155k | return size; |
383 | 155k | } |
384 | | |
385 | | bool CDBWrapper::IsEmpty() |
386 | 9.35k | { |
387 | 9.35k | std::unique_ptr<CDBIterator> it(NewIterator()); |
388 | 9.35k | it->SeekToFirst(); |
389 | 9.35k | return !(it->Valid()); |
390 | 9.35k | } |
391 | | |
392 | | struct CDBIterator::IteratorImpl { |
393 | | const std::unique_ptr<leveldb::Iterator> iter; |
394 | | |
395 | 272k | explicit IteratorImpl(leveldb::Iterator* _iter) : iter{_iter} {} |
396 | | }; |
397 | | |
398 | 272k | CDBIterator::CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter) : parent(_parent), |
399 | 272k | m_impl_iter(std::move(_piter)) |
400 | 272k | { |
401 | 272k | m_scratch.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); |
402 | 272k | } |
403 | | |
404 | | CDBIterator* CDBWrapper::NewIterator() |
405 | 272k | { |
406 | 272k | return new CDBIterator{*this, std::make_unique<CDBIterator::IteratorImpl>(DBContext().pdb->NewIterator(DBContext().iteroptions))}; |
407 | 272k | } |
408 | | |
409 | | void CDBIterator::SeekImpl(std::span<const std::byte> key) |
410 | 205k | { |
411 | 205k | leveldb::Slice slKey(CharCast(key.data()), key.size()); |
412 | 205k | m_impl_iter->iter->Seek(slKey); |
413 | 205k | } |
414 | | |
415 | | std::span<const std::byte> CDBIterator::GetKeyImpl() const |
416 | 7.50M | { |
417 | | // The returned span borrows from the current iterator entry and is only |
418 | | // valid until the iterator is advanced. |
419 | 7.50M | return MakeByteSpan(m_impl_iter->iter->key()); |
420 | 7.50M | } |
421 | | |
422 | | std::span<const std::byte> CDBIterator::GetValueImpl() const |
423 | 7.31M | { |
424 | 7.31M | return MakeByteSpan(m_impl_iter->iter->value()); |
425 | 7.31M | } |
426 | | |
427 | 272k | CDBIterator::~CDBIterator() = default; |
428 | 7.69M | bool CDBIterator::Valid() const { return m_impl_iter->iter->Valid(); } |
429 | 106k | void CDBIterator::SeekToFirst() { m_impl_iter->iter->SeekToFirst(); } |
430 | 7.34M | void CDBIterator::Next() { m_impl_iter->iter->Next(); } |
431 | | |
432 | | namespace dbwrapper_private { |
433 | | |
434 | | const Obfuscation& GetObfuscation(const CDBWrapper& w) |
435 | 17.1M | { |
436 | 17.1M | return w.m_obfuscation; |
437 | 17.1M | } |
438 | | |
439 | | } // namespace dbwrapper_private |