Line data Source code
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 520 : FileState() : refs_(0), size_(0) {}
25 :
26 : // Increase the reference count.
27 624 : void Ref() {
28 624 : MutexLock lock(&refs_mutex_);
29 624 : ++refs_;
30 624 : }
31 :
32 : // Decrease the reference count. Delete if this is the last reference.
33 624 : void Unref() {
34 624 : bool do_delete = false;
35 :
36 : {
37 624 : MutexLock lock(&refs_mutex_);
38 624 : --refs_;
39 624 : assert(refs_ >= 0);
40 624 : if (refs_ <= 0) {
41 260 : do_delete = true;
42 : }
43 : }
44 :
45 624 : if (do_delete) {
46 260 : delete this;
47 : }
48 624 : }
49 :
50 0 : uint64_t Size() const { return size_; }
51 :
52 156 : Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const {
53 156 : if (offset > size_) {
54 0 : return Status::IOError("Offset greater than file size.");
55 : }
56 156 : const uint64_t available = size_ - offset;
57 156 : if (n > available) {
58 156 : n = static_cast<size_t>(available);
59 : }
60 156 : if (n == 0) {
61 52 : *result = Slice();
62 : return Status::OK();
63 : }
64 :
65 : assert(offset / kBlockSize <= SIZE_MAX);
66 104 : size_t block = static_cast<size_t>(offset / kBlockSize);
67 104 : size_t block_offset = offset % kBlockSize;
68 :
69 104 : if (n <= kBlockSize - block_offset) {
70 : // The requested bytes are all in the first block.
71 208 : *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 0 : while (bytes_to_copy > 0) {
79 0 : size_t avail = kBlockSize - block_offset;
80 0 : if (avail > bytes_to_copy) {
81 0 : avail = bytes_to_copy;
82 : }
83 0 : memcpy(dst, blocks_[block] + block_offset, avail);
84 :
85 0 : bytes_to_copy -= avail;
86 0 : dst += avail;
87 0 : block++;
88 0 : block_offset = 0;
89 : }
90 :
91 0 : *result = Slice(scratch, n);
92 : return Status::OK();
93 : }
94 :
95 622 : Status Append(const Slice& data) {
96 622 : const char* src = data.data();
97 622 : size_t src_len = data.size();
98 :
99 1866 : while (src_len > 0) {
100 : size_t avail;
101 622 : size_t offset = size_ % kBlockSize;
102 :
103 622 : if (offset != 0) {
104 : // There is some room in the last block.
105 362 : avail = kBlockSize - offset;
106 : } else {
107 : // No room in the last block; push new one.
108 520 : blocks_.push_back(new char[kBlockSize]);
109 260 : avail = kBlockSize;
110 : }
111 :
112 622 : if (avail > src_len) {
113 622 : avail = src_len;
114 : }
115 1244 : memcpy(blocks_.back() + offset, src, avail);
116 622 : src_len -= avail;
117 622 : src += avail;
118 622 : size_ += avail;
119 : }
120 :
121 622 : return Status::OK();
122 : }
123 :
124 : private:
125 : // Private since only Unref() should be used to delete it.
126 780 : ~FileState() {
127 2080 : for (std::vector<char*>::iterator i = blocks_.begin(); i != blocks_.end();
128 : ++i) {
129 260 : delete [] *i;
130 : }
131 260 : }
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 208 : explicit SequentialFileImpl(FileState* file) : file_(file), pos_(0) {
152 104 : file_->Ref();
153 104 : }
154 :
155 208 : ~SequentialFileImpl() {
156 104 : file_->Unref();
157 104 : }
158 :
159 156 : virtual Status Read(size_t n, Slice* result, char* scratch) {
160 156 : Status s = file_->Read(pos_, n, result, scratch);
161 156 : if (s.ok()) {
162 156 : pos_ += result->size();
163 : }
164 156 : return s;
165 : }
166 :
167 0 : virtual Status Skip(uint64_t n) {
168 0 : if (pos_ > file_->Size()) {
169 0 : return Status::IOError("pos_ > file_->Size()");
170 : }
171 0 : const uint64_t available = file_->Size() - pos_;
172 0 : if (n > available) {
173 0 : n = available;
174 : }
175 0 : 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 0 : explicit RandomAccessFileImpl(FileState* file) : file_(file) {
187 0 : file_->Ref();
188 0 : }
189 :
190 0 : ~RandomAccessFileImpl() {
191 0 : file_->Unref();
192 0 : }
193 :
194 0 : virtual Status Read(uint64_t offset, size_t n, Slice* result,
195 : char* scratch) const {
196 0 : return file_->Read(offset, n, result, scratch);
197 : }
198 :
199 : private:
200 : FileState* file_;
201 : };
202 :
203 : class WritableFileImpl : public WritableFile {
204 : public:
205 520 : WritableFileImpl(FileState* file) : file_(file) {
206 260 : file_->Ref();
207 260 : }
208 :
209 520 : ~WritableFileImpl() {
210 260 : file_->Unref();
211 260 : }
212 :
213 622 : virtual Status Append(const Slice& data) {
214 622 : return file_->Append(data);
215 : }
216 :
217 312 : virtual Status Close() { return Status::OK(); }
218 518 : virtual Status Flush() { return Status::OK(); }
219 362 : virtual Status Sync() { return Status::OK(); }
220 :
221 : private:
222 : FileState* file_;
223 : };
224 :
225 156 : class NoOpLogger : public Logger {
226 : public:
227 52 : virtual void Logv(const char* format, va_list ap) { }
228 : };
229 :
230 : class InMemoryEnv : public EnvWrapper {
231 : public:
232 156 : explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) { }
233 :
234 208 : virtual ~InMemoryEnv() {
235 520 : for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){
236 156 : i->second->Unref();
237 : }
238 104 : }
239 :
240 : // Partial implementation of the Env interface.
241 104 : virtual Status NewSequentialFile(const std::string& fname,
242 : SequentialFile** result) {
243 104 : MutexLock lock(&mutex_);
244 312 : if (file_map_.find(fname) == file_map_.end()) {
245 0 : *result = NULL;
246 0 : return Status::IOError(fname, "File not found");
247 : }
248 :
249 104 : *result = new SequentialFileImpl(file_map_[fname]);
250 : return Status::OK();
251 : }
252 :
253 0 : virtual Status NewRandomAccessFile(const std::string& fname,
254 : RandomAccessFile** result) {
255 0 : MutexLock lock(&mutex_);
256 0 : if (file_map_.find(fname) == file_map_.end()) {
257 0 : *result = NULL;
258 0 : return Status::IOError(fname, "File not found");
259 : }
260 :
261 0 : *result = new RandomAccessFileImpl(file_map_[fname]);
262 : return Status::OK();
263 : }
264 :
265 260 : virtual Status NewWritableFile(const std::string& fname,
266 : WritableFile** result) {
267 260 : MutexLock lock(&mutex_);
268 780 : if (file_map_.find(fname) != file_map_.end()) {
269 0 : DeleteFileInternal(fname);
270 : }
271 :
272 520 : FileState* file = new FileState();
273 260 : file->Ref();
274 260 : file_map_[fname] = file;
275 :
276 260 : *result = new WritableFileImpl(file);
277 260 : return Status::OK();
278 : }
279 :
280 52 : virtual bool FileExists(const std::string& fname) {
281 52 : MutexLock lock(&mutex_);
282 260 : return file_map_.find(fname) != file_map_.end();
283 : }
284 :
285 104 : virtual Status GetChildren(const std::string& dir,
286 : std::vector<std::string>* result) {
287 104 : MutexLock lock(&mutex_);
288 : result->clear();
289 :
290 1040 : for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){
291 312 : const std::string& filename = i->first;
292 :
293 2184 : if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' &&
294 936 : Slice(filename).starts_with(Slice(dir))) {
295 624 : result->push_back(filename.substr(dir.size() + 1));
296 : }
297 : }
298 :
299 104 : return Status::OK();
300 : }
301 :
302 156 : void DeleteFileInternal(const std::string& fname) {
303 468 : if (file_map_.find(fname) == file_map_.end()) {
304 156 : return;
305 : }
306 :
307 104 : file_map_[fname]->Unref();
308 104 : file_map_.erase(fname);
309 : }
310 :
311 52 : virtual Status DeleteFile(const std::string& fname) {
312 52 : MutexLock lock(&mutex_);
313 156 : if (file_map_.find(fname) == file_map_.end()) {
314 0 : return Status::IOError(fname, "File not found");
315 : }
316 :
317 52 : DeleteFileInternal(fname);
318 : return Status::OK();
319 : }
320 :
321 104 : virtual Status CreateDir(const std::string& dirname) {
322 104 : return Status::OK();
323 : }
324 :
325 0 : virtual Status DeleteDir(const std::string& dirname) {
326 0 : return Status::OK();
327 : }
328 :
329 0 : virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) {
330 0 : MutexLock lock(&mutex_);
331 0 : if (file_map_.find(fname) == file_map_.end()) {
332 0 : return Status::IOError(fname, "File not found");
333 : }
334 :
335 0 : *file_size = file_map_[fname]->Size();
336 : return Status::OK();
337 : }
338 :
339 156 : virtual Status RenameFile(const std::string& src,
340 : const std::string& target) {
341 156 : MutexLock lock(&mutex_);
342 468 : if (file_map_.find(src) == file_map_.end()) {
343 52 : return Status::IOError(src, "File not found");
344 : }
345 :
346 104 : DeleteFileInternal(target);
347 104 : file_map_[target] = file_map_[src];
348 104 : file_map_.erase(src);
349 : return Status::OK();
350 : }
351 :
352 52 : virtual Status LockFile(const std::string& fname, FileLock** lock) {
353 104 : *lock = new FileLock;
354 52 : return Status::OK();
355 : }
356 :
357 52 : virtual Status UnlockFile(FileLock* lock) {
358 52 : delete lock;
359 52 : return Status::OK();
360 : }
361 :
362 0 : virtual Status GetTestDirectory(std::string* path) {
363 : *path = "/test";
364 0 : return Status::OK();
365 : }
366 :
367 52 : virtual Status NewLogger(const std::string& fname, Logger** result) {
368 104 : *result = new NoOpLogger;
369 52 : 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 52 : Env* NewMemEnv(Env* base_env) {
382 52 : return new InMemoryEnv(base_env);
383 : }
384 :
385 : } // namespace leveldb
|