blob: 5879de121456a7c5c16457eb36d85c64ad0a1b61 [file] [log] [blame]
gabor@google.coma7d6c312011-09-15 21:01:32 +00001// 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
16namespace leveldb {
17
18namespace {
19
20class 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
148class 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
183class 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
202class 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
dgrogan@chromium.org29c68f12012-10-12 19:13:17 +0000224class NoOpLogger : public Logger {
225 public:
226 virtual void Logv(const char* format, va_list ap) { }
227};
228
gabor@google.coma7d6c312011-09-15 21:01:32 +0000229class 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
dgrogan@chromium.org29c68f12012-10-12 19:13:17 +0000366 virtual Status NewLogger(const std::string& fname, Logger** result) {
367 *result = new NoOpLogger;
368 return Status::OK();
369 }
370
gabor@google.coma7d6c312011-09-15 21:01:32 +0000371 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
hans@chromium.org45b99402011-10-31 17:34:55 +0000378} // namespace
gabor@google.coma7d6c312011-09-15 21:01:32 +0000379
380Env* NewMemEnv(Env* base_env) {
381 return new InMemoryEnv(base_env);
382}
383
hans@chromium.org45b99402011-10-31 17:34:55 +0000384} // namespace leveldb