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