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