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