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