Coverage Report

Created: 2026-06-12 16:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/leveldb/helpers/memenv/memenv.cc
Line
Count
Source
1
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file. See the AUTHORS file for names of contributors.
4
5
#include "helpers/memenv/memenv.h"
6
7
#include <string.h>
8
9
#include <limits>
10
#include <map>
11
#include <string>
12
#include <vector>
13
14
#include "leveldb/env.h"
15
#include "leveldb/status.h"
16
#include "port/port.h"
17
#include "port/thread_annotations.h"
18
#include "util/mutexlock.h"
19
20
namespace leveldb {
21
22
namespace {
23
24
class FileState {
25
 public:
26
  // FileStates are reference counted. The initial reference count is zero
27
  // and the caller must call Ref() at least once.
28
10.0k
  FileState() : refs_(0), size_(0) {}
29
30
  // No copying allowed.
31
  FileState(const FileState&) = delete;
32
  FileState& operator=(const FileState&) = delete;
33
34
  // Increase the reference count.
35
24.3k
  void Ref() {
36
24.3k
    MutexLock lock(&refs_mutex_);
37
24.3k
    ++refs_;
38
24.3k
  }
39
40
  // Decrease the reference count. Delete if this is the last reference.
41
24.3k
  void Unref() {
42
24.3k
    bool do_delete = false;
43
44
24.3k
    {
45
24.3k
      MutexLock lock(&refs_mutex_);
46
24.3k
      --refs_;
47
24.3k
      assert(refs_ >= 0);
  Branch (47:7): [True: 24.3k, False: 0]
48
24.3k
      if (refs_ <= 0) {
  Branch (48:11): [True: 10.0k, False: 14.2k]
49
10.0k
        do_delete = true;
50
10.0k
      }
51
24.3k
    }
52
53
24.3k
    if (do_delete) {
  Branch (53:9): [True: 10.0k, False: 14.2k]
54
10.0k
      delete this;
55
10.0k
    }
56
24.3k
  }
57
58
0
  uint64_t Size() const {
59
0
    MutexLock lock(&blocks_mutex_);
60
0
    return size_;
61
0
  }
62
63
10.0k
  void Truncate() {
64
10.0k
    MutexLock lock(&blocks_mutex_);
65
244k
    for (char*& block : blocks_) {
  Branch (65:23): [True: 244k, False: 10.0k]
66
244k
      delete[] block;
67
244k
    }
68
10.0k
    blocks_.clear();
69
10.0k
    size_ = 0;
70
10.0k
  }
71
72
9.14k
  Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const {
73
9.14k
    MutexLock lock(&blocks_mutex_);
74
9.14k
    if (offset > size_) {
  Branch (74:9): [True: 0, False: 9.14k]
75
0
      return Status::IOError("Offset greater than file size.");
76
0
    }
77
9.14k
    const uint64_t available = size_ - offset;
78
9.14k
    if (n > available) {
  Branch (78:9): [True: 4.70k, False: 4.44k]
79
4.70k
      n = static_cast<size_t>(available);
80
4.70k
    }
81
9.14k
    if (n == 0) {
  Branch (81:9): [True: 1.56k, False: 7.57k]
82
1.56k
      *result = Slice();
83
1.56k
      return Status::OK();
84
1.56k
    }
85
86
9.14k
    assert(offset / kBlockSize <= std::numeric_limits<size_t>::max());
  Branch (86:5): [True: 7.57k, False: 0]
87
7.57k
    size_t block = static_cast<size_t>(offset / kBlockSize);
88
7.57k
    size_t block_offset = offset % kBlockSize;
89
7.57k
    size_t bytes_to_copy = n;
90
7.57k
    char* dst = scratch;
91
92
15.8k
    while (bytes_to_copy > 0) {
  Branch (92:12): [True: 8.31k, False: 7.57k]
93
8.31k
      size_t avail = kBlockSize - block_offset;
94
8.31k
      if (avail > bytes_to_copy) {
  Branch (94:11): [True: 7.57k, False: 742]
95
7.57k
        avail = bytes_to_copy;
96
7.57k
      }
97
8.31k
      memcpy(dst, blocks_[block] + block_offset, avail);
98
99
8.31k
      bytes_to_copy -= avail;
100
8.31k
      dst += avail;
101
8.31k
      block++;
102
8.31k
      block_offset = 0;
103
8.31k
    }
104
105
7.57k
    *result = Slice(scratch, n);
106
7.57k
    return Status::OK();
107
7.57k
  }
108
109
170k
  Status Append(const Slice& data) {
110
170k
    const char* src = data.data();
111
170k
    size_t src_len = data.size();
112
113
170k
    MutexLock lock(&blocks_mutex_);
114
528k
    while (src_len > 0) {
  Branch (114:12): [True: 358k, False: 170k]
115
358k
      size_t avail;
116
358k
      size_t offset = size_ % kBlockSize;
117
118
358k
      if (offset != 0) {
  Branch (118:11): [True: 114k, False: 244k]
119
        // There is some room in the last block.
120
114k
        avail = kBlockSize - offset;
121
244k
      } else {
122
        // No room in the last block; push new one.
123
244k
        blocks_.push_back(new char[kBlockSize]);
124
244k
        avail = kBlockSize;
125
244k
      }
126
127
358k
      if (avail > src_len) {
  Branch (127:11): [True: 124k, False: 234k]
128
124k
        avail = src_len;
129
124k
      }
130
358k
      memcpy(blocks_.back() + offset, src, avail);
131
358k
      src_len -= avail;
132
358k
      src += avail;
133
358k
      size_ += avail;
134
358k
    }
135
136
170k
    return Status::OK();
137
170k
  }
138
139
 private:
140
  enum { kBlockSize = 8 * 1024 };
141
142
  // Private since only Unref() should be used to delete it.
143
10.0k
  ~FileState() { Truncate(); }
144
145
  port::Mutex refs_mutex_;
146
  int refs_ GUARDED_BY(refs_mutex_);
147
148
  mutable port::Mutex blocks_mutex_;
149
  std::vector<char*> blocks_ GUARDED_BY(blocks_mutex_);
150
  uint64_t size_ GUARDED_BY(blocks_mutex_);
151
};
152
153
class SequentialFileImpl : public SequentialFile {
154
 public:
155
3.13k
  explicit SequentialFileImpl(FileState* file) : file_(file), pos_(0) {
156
3.13k
    file_->Ref();
157
3.13k
  }
158
159
3.13k
  ~SequentialFileImpl() override { file_->Unref(); }
160
161
4.70k
  Status Read(size_t n, Slice* result, char* scratch) override {
162
4.70k
    Status s = file_->Read(pos_, n, result, scratch);
163
4.70k
    if (s.ok()) {
  Branch (163:9): [True: 4.70k, False: 0]
164
4.70k
      pos_ += result->size();
165
4.70k
    }
166
4.70k
    return s;
167
4.70k
  }
168
169
0
  Status Skip(uint64_t n) override {
170
0
    if (pos_ > file_->Size()) {
  Branch (170:9): [True: 0, False: 0]
171
0
      return Status::IOError("pos_ > file_->Size()");
172
0
    }
173
0
    const uint64_t available = file_->Size() - pos_;
174
0
    if (n > available) {
  Branch (174:9): [True: 0, False: 0]
175
0
      n = available;
176
0
    }
177
0
    pos_ += n;
178
0
    return Status::OK();
179
0
  }
180
181
0
  virtual std::string GetName() const override { return "[memenv]"; }
182
 private:
183
  FileState* file_;
184
  uint64_t pos_;
185
};
186
187
class RandomAccessFileImpl : public RandomAccessFile {
188
 public:
189
1.11k
  explicit RandomAccessFileImpl(FileState* file) : file_(file) { file_->Ref(); }
190
191
1.11k
  ~RandomAccessFileImpl() override { file_->Unref(); }
192
193
  Status Read(uint64_t offset, size_t n, Slice* result,
194
4.44k
              char* scratch) const override {
195
4.44k
    return file_->Read(offset, n, result, scratch);
196
4.44k
  }
197
198
0
  virtual std::string GetName() const override { return "[memenv]"; }
199
 private:
200
  FileState* file_;
201
};
202
203
class WritableFileImpl : public WritableFile {
204
 public:
205
10.0k
  WritableFileImpl(FileState* file) : file_(file) { file_->Ref(); }
206
207
10.0k
  ~WritableFileImpl() override { file_->Unref(); }
208
209
170k
  Status Append(const Slice& data) override { return file_->Append(data); }
210
211
5.81k
  Status Close() override { return Status::OK(); }
212
79.6k
  Status Flush() override { return Status::OK(); }
213
14.3k
  Status Sync() override { return Status::OK(); }
214
215
0
  virtual std::string GetName() const override { return "[memenv]"; }
216
 private:
217
  FileState* file_;
218
};
219
220
class NoOpLogger : public Logger {
221
 public:
222
0
  void Logv(const char* format, va_list ap) override {}
223
};
224
225
class InMemoryEnv : public EnvWrapper {
226
 public:
227
1.56k
  explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) {}
228
229
1.56k
  ~InMemoryEnv() override {
230
5.81k
    for (const auto& kvp : file_map_) {
  Branch (230:26): [True: 5.81k, False: 1.56k]
231
5.81k
      kvp.second->Unref();
232
5.81k
    }
233
1.56k
  }
234
235
  // Partial implementation of the Env interface.
236
  Status NewSequentialFile(const std::string& fname,
237
3.13k
                           SequentialFile** result) override {
238
3.13k
    MutexLock lock(&mutex_);
239
3.13k
    if (file_map_.find(fname) == file_map_.end()) {
  Branch (239:9): [True: 0, False: 3.13k]
240
0
      *result = nullptr;
241
0
      return Status::IOError(fname, "File not found");
242
0
    }
243
244
3.13k
    *result = new SequentialFileImpl(file_map_[fname]);
245
3.13k
    return Status::OK();
246
3.13k
  }
247
248
  Status NewRandomAccessFile(const std::string& fname,
249
1.11k
                             RandomAccessFile** result) override {
250
1.11k
    MutexLock lock(&mutex_);
251
1.11k
    if (file_map_.find(fname) == file_map_.end()) {
  Branch (251:9): [True: 0, False: 1.11k]
252
0
      *result = nullptr;
253
0
      return Status::IOError(fname, "File not found");
254
0
    }
255
256
1.11k
    *result = new RandomAccessFileImpl(file_map_[fname]);
257
1.11k
    return Status::OK();
258
1.11k
  }
259
260
  Status NewWritableFile(const std::string& fname,
261
10.0k
                         WritableFile** result) override {
262
10.0k
    MutexLock lock(&mutex_);
263
10.0k
    FileSystem::iterator it = file_map_.find(fname);
264
265
10.0k
    FileState* file;
266
10.0k
    if (it == file_map_.end()) {
  Branch (266:9): [True: 10.0k, False: 0]
267
      // File is not currently open.
268
10.0k
      file = new FileState();
269
10.0k
      file->Ref();
270
10.0k
      file_map_[fname] = file;
271
10.0k
    } else {
272
0
      file = it->second;
273
0
      file->Truncate();
274
0
    }
275
276
10.0k
    *result = new WritableFileImpl(file);
277
10.0k
    return Status::OK();
278
10.0k
  }
279
280
  Status NewAppendableFile(const std::string& fname,
281
0
                           WritableFile** result) override {
282
0
    MutexLock lock(&mutex_);
283
0
    FileState** sptr = &file_map_[fname];
284
0
    FileState* file = *sptr;
285
0
    if (file == nullptr) {
  Branch (285:9): [True: 0, False: 0]
286
0
      file = new FileState();
287
0
      file->Ref();
288
0
    }
289
0
    *result = new WritableFileImpl(file);
290
0
    return Status::OK();
291
0
  }
292
293
1.56k
  bool FileExists(const std::string& fname) override {
294
1.56k
    MutexLock lock(&mutex_);
295
1.56k
    return file_map_.find(fname) != file_map_.end();
296
1.56k
  }
297
298
  Status GetChildren(const std::string& dir,
299
4.24k
                     std::vector<std::string>* result) override {
300
4.24k
    MutexLock lock(&mutex_);
301
4.24k
    result->clear();
302
303
15.6k
    for (const auto& kvp : file_map_) {
  Branch (303:26): [True: 15.6k, False: 4.24k]
304
15.6k
      const std::string& filename = kvp.first;
305
306
15.6k
      if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' &&
  Branch (306:11): [True: 15.6k, False: 0]
  Branch (306:11): [True: 15.6k, False: 0]
  Branch (306:48): [True: 15.6k, False: 0]
307
15.6k
          Slice(filename).starts_with(Slice(dir))) {
  Branch (307:11): [True: 15.6k, False: 0]
308
15.6k
        result->push_back(filename.substr(dir.size() + 1));
309
15.6k
      }
310
15.6k
    }
311
312
4.24k
    return Status::OK();
313
4.24k
  }
314
315
  void DeleteFileInternal(const std::string& fname)
316
5.81k
      EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
317
5.81k
    if (file_map_.find(fname) == file_map_.end()) {
  Branch (317:9): [True: 1.56k, False: 4.24k]
318
1.56k
      return;
319
1.56k
    }
320
321
4.24k
    file_map_[fname]->Unref();
322
4.24k
    file_map_.erase(fname);
323
4.24k
  }
324
325
2.67k
  Status DeleteFile(const std::string& fname) override {
326
2.67k
    MutexLock lock(&mutex_);
327
2.67k
    if (file_map_.find(fname) == file_map_.end()) {
  Branch (327:9): [True: 0, False: 2.67k]
328
0
      return Status::IOError(fname, "File not found");
329
0
    }
330
331
2.67k
    DeleteFileInternal(fname);
332
2.67k
    return Status::OK();
333
2.67k
  }
334
335
1.56k
  Status CreateDir(const std::string& dirname) override { return Status::OK(); }
336
337
0
  Status DeleteDir(const std::string& dirname) override { return Status::OK(); }
338
339
0
  Status GetFileSize(const std::string& fname, uint64_t* file_size) override {
340
0
    MutexLock lock(&mutex_);
341
0
    if (file_map_.find(fname) == file_map_.end()) {
  Branch (341:9): [True: 0, False: 0]
342
0
      return Status::IOError(fname, "File not found");
343
0
    }
344
345
0
    *file_size = file_map_[fname]->Size();
346
0
    return Status::OK();
347
0
  }
348
349
  Status RenameFile(const std::string& src,
350
3.13k
                    const std::string& target) override {
351
3.13k
    MutexLock lock(&mutex_);
352
3.13k
    if (file_map_.find(src) == file_map_.end()) {
  Branch (352:9): [True: 0, False: 3.13k]
353
0
      return Status::IOError(src, "File not found");
354
0
    }
355
356
3.13k
    DeleteFileInternal(target);
357
3.13k
    file_map_[target] = file_map_[src];
358
3.13k
    file_map_.erase(src);
359
3.13k
    return Status::OK();
360
3.13k
  }
361
362
1.56k
  Status LockFile(const std::string& fname, FileLock** lock) override {
363
1.56k
    *lock = new FileLock;
364
1.56k
    return Status::OK();
365
1.56k
  }
366
367
1.56k
  Status UnlockFile(FileLock* lock) override {
368
1.56k
    delete lock;
369
1.56k
    return Status::OK();
370
1.56k
  }
371
372
0
  Status GetTestDirectory(std::string* path) override {
373
0
    *path = "/test";
374
0
    return Status::OK();
375
0
  }
376
377
0
  Status NewLogger(const std::string& fname, Logger** result) override {
378
0
    *result = new NoOpLogger;
379
0
    return Status::OK();
380
0
  }
381
382
 private:
383
  // Map from filenames to FileState objects, representing a simple file system.
384
  typedef std::map<std::string, FileState*> FileSystem;
385
386
  port::Mutex mutex_;
387
  FileSystem file_map_ GUARDED_BY(mutex_);
388
};
389
390
}  // namespace
391
392
1.56k
Env* NewMemEnv(Env* base_env) { return new InMemoryEnv(base_env); }
393
394
}  // namespace leveldb