Coverage Report

Created: 2025-06-10 13:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
0
  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
0
  void Ref() {
36
0
    MutexLock lock(&refs_mutex_);
37
0
    ++refs_;
38
0
  }
39
40
  // Decrease the reference count. Delete if this is the last reference.
41
0
  void Unref() {
42
0
    bool do_delete = false;
43
44
0
    {
45
0
      MutexLock lock(&refs_mutex_);
46
0
      --refs_;
47
0
      assert(refs_ >= 0);
  Branch (47:7): [True: 0, False: 0]
48
0
      if (refs_ <= 0) {
  Branch (48:11): [True: 0, False: 0]
49
0
        do_delete = true;
50
0
      }
51
0
    }
52
53
0
    if (do_delete) {
  Branch (53:9): [True: 0, False: 0]
54
0
      delete this;
55
0
    }
56
0
  }
57
58
0
  uint64_t Size() const {
59
0
    MutexLock lock(&blocks_mutex_);
60
0
    return size_;
61
0
  }
62
63
0
  void Truncate() {
64
0
    MutexLock lock(&blocks_mutex_);
65
0
    for (char*& block : blocks_) {
  Branch (65:23): [True: 0, False: 0]
66
0
      delete[] block;
67
0
    }
68
0
    blocks_.clear();
69
0
    size_ = 0;
70
0
  }
71
72
0
  Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const {
73
0
    MutexLock lock(&blocks_mutex_);
74
0
    if (offset > size_) {
  Branch (74:9): [True: 0, False: 0]
75
0
      return Status::IOError("Offset greater than file size.");
76
0
    }
77
0
    const uint64_t available = size_ - offset;
78
0
    if (n > available) {
  Branch (78:9): [True: 0, False: 0]
79
0
      n = static_cast<size_t>(available);
80
0
    }
81
0
    if (n == 0) {
  Branch (81:9): [True: 0, False: 0]
82
0
      *result = Slice();
83
0
      return Status::OK();
84
0
    }
85
86
0
    assert(offset / kBlockSize <= std::numeric_limits<size_t>::max());
  Branch (86:5): [True: 0, False: 0]
87
0
    size_t block = static_cast<size_t>(offset / kBlockSize);
88
0
    size_t block_offset = offset % kBlockSize;
89
0
    size_t bytes_to_copy = n;
90
0
    char* dst = scratch;
91
92
0
    while (bytes_to_copy > 0) {
  Branch (92:12): [True: 0, False: 0]
93
0
      size_t avail = kBlockSize - block_offset;
94
0
      if (avail > bytes_to_copy) {
  Branch (94:11): [True: 0, False: 0]
95
0
        avail = bytes_to_copy;
96
0
      }
97
0
      memcpy(dst, blocks_[block] + block_offset, avail);
98
99
0
      bytes_to_copy -= avail;
100
0
      dst += avail;
101
0
      block++;
102
0
      block_offset = 0;
103
0
    }
104
105
0
    *result = Slice(scratch, n);
106
0
    return Status::OK();
107
0
  }
108
109
0
  Status Append(const Slice& data) {
110
0
    const char* src = data.data();
111
0
    size_t src_len = data.size();
112
113
0
    MutexLock lock(&blocks_mutex_);
114
0
    while (src_len > 0) {
  Branch (114:12): [True: 0, False: 0]
115
0
      size_t avail;
116
0
      size_t offset = size_ % kBlockSize;
117
118
0
      if (offset != 0) {
  Branch (118:11): [True: 0, False: 0]
119
        // There is some room in the last block.
120
0
        avail = kBlockSize - offset;
121
0
      } else {
122
        // No room in the last block; push new one.
123
0
        blocks_.push_back(new char[kBlockSize]);
124
0
        avail = kBlockSize;
125
0
      }
126
127
0
      if (avail > src_len) {
  Branch (127:11): [True: 0, False: 0]
128
0
        avail = src_len;
129
0
      }
130
0
      memcpy(blocks_.back() + offset, src, avail);
131
0
      src_len -= avail;
132
0
      src += avail;
133
0
      size_ += avail;
134
0
    }
135
136
0
    return Status::OK();
137
0
  }
138
139
 private:
140
  enum { kBlockSize = 8 * 1024 };
141
142
  // Private since only Unref() should be used to delete it.
143
0
  ~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
0
  explicit SequentialFileImpl(FileState* file) : file_(file), pos_(0) {
156
0
    file_->Ref();
157
0
  }
158
159
0
  ~SequentialFileImpl() override { file_->Unref(); }
160
161
0
  Status Read(size_t n, Slice* result, char* scratch) override {
162
0
    Status s = file_->Read(pos_, n, result, scratch);
163
0
    if (s.ok()) {
  Branch (163:9): [True: 0, False: 0]
164
0
      pos_ += result->size();
165
0
    }
166
0
    return s;
167
0
  }
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
0
  explicit RandomAccessFileImpl(FileState* file) : file_(file) { file_->Ref(); }
190
191
0
  ~RandomAccessFileImpl() override { file_->Unref(); }
192
193
  Status Read(uint64_t offset, size_t n, Slice* result,
194
0
              char* scratch) const override {
195
0
    return file_->Read(offset, n, result, scratch);
196
0
  }
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
0
  WritableFileImpl(FileState* file) : file_(file) { file_->Ref(); }
206
207
0
  ~WritableFileImpl() override { file_->Unref(); }
208
209
0
  Status Append(const Slice& data) override { return file_->Append(data); }
210
211
0
  Status Close() override { return Status::OK(); }
212
0
  Status Flush() override { return Status::OK(); }
213
0
  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
0
  explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) {}
228
229
0
  ~InMemoryEnv() override {
230
0
    for (const auto& kvp : file_map_) {
  Branch (230:26): [True: 0, False: 0]
231
0
      kvp.second->Unref();
232
0
    }
233
0
  }
234
235
  // Partial implementation of the Env interface.
236
  Status NewSequentialFile(const std::string& fname,
237
0
                           SequentialFile** result) override {
238
0
    MutexLock lock(&mutex_);
239
0
    if (file_map_.find(fname) == file_map_.end()) {
  Branch (239:9): [True: 0, False: 0]
240
0
      *result = nullptr;
241
0
      return Status::IOError(fname, "File not found");
242
0
    }
243
244
0
    *result = new SequentialFileImpl(file_map_[fname]);
245
0
    return Status::OK();
246
0
  }
247
248
  Status NewRandomAccessFile(const std::string& fname,
249
0
                             RandomAccessFile** result) override {
250
0
    MutexLock lock(&mutex_);
251
0
    if (file_map_.find(fname) == file_map_.end()) {
  Branch (251:9): [True: 0, False: 0]
252
0
      *result = nullptr;
253
0
      return Status::IOError(fname, "File not found");
254
0
    }
255
256
0
    *result = new RandomAccessFileImpl(file_map_[fname]);
257
0
    return Status::OK();
258
0
  }
259
260
  Status NewWritableFile(const std::string& fname,
261
0
                         WritableFile** result) override {
262
0
    MutexLock lock(&mutex_);
263
0
    FileSystem::iterator it = file_map_.find(fname);
264
265
0
    FileState* file;
266
0
    if (it == file_map_.end()) {
  Branch (266:9): [True: 0, False: 0]
267
      // File is not currently open.
268
0
      file = new FileState();
269
0
      file->Ref();
270
0
      file_map_[fname] = file;
271
0
    } else {
272
0
      file = it->second;
273
0
      file->Truncate();
274
0
    }
275
276
0
    *result = new WritableFileImpl(file);
277
0
    return Status::OK();
278
0
  }
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
0
  bool FileExists(const std::string& fname) override {
294
0
    MutexLock lock(&mutex_);
295
0
    return file_map_.find(fname) != file_map_.end();
296
0
  }
297
298
  Status GetChildren(const std::string& dir,
299
0
                     std::vector<std::string>* result) override {
300
0
    MutexLock lock(&mutex_);
301
0
    result->clear();
302
303
0
    for (const auto& kvp : file_map_) {
  Branch (303:26): [True: 0, False: 0]
304
0
      const std::string& filename = kvp.first;
305
306
0
      if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' &&
  Branch (306:11): [True: 0, False: 0]
  Branch (306:11): [True: 0, False: 0]
  Branch (306:48): [True: 0, False: 0]
307
0
          Slice(filename).starts_with(Slice(dir))) {
  Branch (307:11): [True: 0, False: 0]
308
0
        result->push_back(filename.substr(dir.size() + 1));
309
0
      }
310
0
    }
311
312
0
    return Status::OK();
313
0
  }
314
315
  void DeleteFileInternal(const std::string& fname)
316
0
      EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
317
0
    if (file_map_.find(fname) == file_map_.end()) {
  Branch (317:9): [True: 0, False: 0]
318
0
      return;
319
0
    }
320
321
0
    file_map_[fname]->Unref();
322
0
    file_map_.erase(fname);
323
0
  }
324
325
0
  Status DeleteFile(const std::string& fname) override {
326
0
    MutexLock lock(&mutex_);
327
0
    if (file_map_.find(fname) == file_map_.end()) {
  Branch (327:9): [True: 0, False: 0]
328
0
      return Status::IOError(fname, "File not found");
329
0
    }
330
331
0
    DeleteFileInternal(fname);
332
0
    return Status::OK();
333
0
  }
334
335
0
  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
0
                    const std::string& target) override {
351
0
    MutexLock lock(&mutex_);
352
0
    if (file_map_.find(src) == file_map_.end()) {
  Branch (352:9): [True: 0, False: 0]
353
0
      return Status::IOError(src, "File not found");
354
0
    }
355
356
0
    DeleteFileInternal(target);
357
0
    file_map_[target] = file_map_[src];
358
0
    file_map_.erase(src);
359
0
    return Status::OK();
360
0
  }
361
362
0
  Status LockFile(const std::string& fname, FileLock** lock) override {
363
0
    *lock = new FileLock;
364
0
    return Status::OK();
365
0
  }
366
367
0
  Status UnlockFile(FileLock* lock) override {
368
0
    delete lock;
369
0
    return Status::OK();
370
0
  }
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
0
Env* NewMemEnv(Env* base_env) { return new InMemoryEnv(base_env); }
393
394
}  // namespace leveldb