Merge pull request #41 from fsb4000/miniupnpc
[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 = 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