/bitcoin/src/leveldb/helpers/memenv/memenv.cc
Line | Count | Source |
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 | 0 | 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 | 0 | void Ref() { |
36 | 0 | MutexLock lock(&refs_mutex_); |
37 | 0 | ++refs_; |
38 | 0 | } |
39 | | |
40 | | // Decrease the reference count. Delete if this is the last reference. |
41 | 0 | void Unref() { |
42 | 0 | bool do_delete = false; |
43 | |
|
44 | 0 | { |
45 | 0 | MutexLock lock(&refs_mutex_); |
46 | 0 | --refs_; |
47 | 0 | assert(refs_ >= 0); Branch (47:7): [True: 0, False: 0]
|
48 | 0 | if (refs_ <= 0) { Branch (48:11): [True: 0, False: 0]
|
49 | 0 | do_delete = true; |
50 | 0 | } |
51 | 0 | } |
52 | | |
53 | 0 | if (do_delete) { Branch (53:9): [True: 0, False: 0]
|
54 | 0 | delete this; |
55 | 0 | } |
56 | 0 | } |
57 | | |
58 | 0 | uint64_t Size() const { |
59 | 0 | MutexLock lock(&blocks_mutex_); |
60 | 0 | return size_; |
61 | 0 | } |
62 | | |
63 | 0 | void Truncate() { |
64 | 0 | MutexLock lock(&blocks_mutex_); |
65 | 0 | for (char*& block : blocks_) { Branch (65:23): [True: 0, False: 0]
|
66 | 0 | delete[] block; |
67 | 0 | } |
68 | 0 | blocks_.clear(); |
69 | 0 | size_ = 0; |
70 | 0 | } |
71 | | |
72 | 0 | Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const { |
73 | 0 | MutexLock lock(&blocks_mutex_); |
74 | 0 | if (offset > size_) { Branch (74:9): [True: 0, False: 0]
|
75 | 0 | return Status::IOError("Offset greater than file size."); |
76 | 0 | } |
77 | 0 | const uint64_t available = size_ - offset; |
78 | 0 | if (n > available) { Branch (78:9): [True: 0, False: 0]
|
79 | 0 | n = static_cast<size_t>(available); |
80 | 0 | } |
81 | 0 | if (n == 0) { Branch (81:9): [True: 0, False: 0]
|
82 | 0 | *result = Slice(); |
83 | 0 | return Status::OK(); |
84 | 0 | } |
85 | | |
86 | 0 | assert(offset / kBlockSize <= std::numeric_limits<size_t>::max()); Branch (86:5): [True: 0, False: 0]
|
87 | 0 | size_t block = static_cast<size_t>(offset / kBlockSize); |
88 | 0 | size_t block_offset = offset % kBlockSize; |
89 | 0 | size_t bytes_to_copy = n; |
90 | 0 | char* dst = scratch; |
91 | |
|
92 | 0 | while (bytes_to_copy > 0) { Branch (92:12): [True: 0, False: 0]
|
93 | 0 | size_t avail = kBlockSize - block_offset; |
94 | 0 | if (avail > bytes_to_copy) { Branch (94:11): [True: 0, False: 0]
|
95 | 0 | avail = bytes_to_copy; |
96 | 0 | } |
97 | 0 | memcpy(dst, blocks_[block] + block_offset, avail); |
98 | |
|
99 | 0 | bytes_to_copy -= avail; |
100 | 0 | dst += avail; |
101 | 0 | block++; |
102 | 0 | block_offset = 0; |
103 | 0 | } |
104 | |
|
105 | 0 | *result = Slice(scratch, n); |
106 | 0 | return Status::OK(); |
107 | 0 | } |
108 | | |
109 | 0 | Status Append(const Slice& data) { |
110 | 0 | const char* src = data.data(); |
111 | 0 | size_t src_len = data.size(); |
112 | |
|
113 | 0 | MutexLock lock(&blocks_mutex_); |
114 | 0 | while (src_len > 0) { Branch (114:12): [True: 0, False: 0]
|
115 | 0 | size_t avail; |
116 | 0 | size_t offset = size_ % kBlockSize; |
117 | |
|
118 | 0 | if (offset != 0) { Branch (118:11): [True: 0, False: 0]
|
119 | | // There is some room in the last block. |
120 | 0 | avail = kBlockSize - offset; |
121 | 0 | } else { |
122 | | // No room in the last block; push new one. |
123 | 0 | blocks_.push_back(new char[kBlockSize]); |
124 | 0 | avail = kBlockSize; |
125 | 0 | } |
126 | |
|
127 | 0 | if (avail > src_len) { Branch (127:11): [True: 0, False: 0]
|
128 | 0 | avail = src_len; |
129 | 0 | } |
130 | 0 | memcpy(blocks_.back() + offset, src, avail); |
131 | 0 | src_len -= avail; |
132 | 0 | src += avail; |
133 | 0 | size_ += avail; |
134 | 0 | } |
135 | |
|
136 | 0 | return Status::OK(); |
137 | 0 | } |
138 | | |
139 | | private: |
140 | | enum { kBlockSize = 8 * 1024 }; |
141 | | |
142 | | // Private since only Unref() should be used to delete it. |
143 | 0 | ~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 | 0 | explicit SequentialFileImpl(FileState* file) : file_(file), pos_(0) { |
156 | 0 | file_->Ref(); |
157 | 0 | } |
158 | | |
159 | 0 | ~SequentialFileImpl() override { file_->Unref(); } |
160 | | |
161 | 0 | Status Read(size_t n, Slice* result, char* scratch) override { |
162 | 0 | Status s = file_->Read(pos_, n, result, scratch); |
163 | 0 | if (s.ok()) { Branch (163:9): [True: 0, False: 0]
|
164 | 0 | pos_ += result->size(); |
165 | 0 | } |
166 | 0 | return s; |
167 | 0 | } |
168 | | |
169 | 0 | Status Skip(uint64_t n) override { |
170 | 0 | if (pos_ > file_->Size()) { Branch (170:9): [True: 0, False: 0]
|
171 | 0 | return Status::IOError("pos_ > file_->Size()"); |
172 | 0 | } |
173 | 0 | const uint64_t available = file_->Size() - pos_; |
174 | 0 | if (n > available) { Branch (174:9): [True: 0, False: 0]
|
175 | 0 | n = available; |
176 | 0 | } |
177 | 0 | pos_ += n; |
178 | 0 | return Status::OK(); |
179 | 0 | } |
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 | 0 | explicit RandomAccessFileImpl(FileState* file) : file_(file) { file_->Ref(); } |
190 | | |
191 | 0 | ~RandomAccessFileImpl() override { file_->Unref(); } |
192 | | |
193 | | Status Read(uint64_t offset, size_t n, Slice* result, |
194 | 0 | char* scratch) const override { |
195 | 0 | return file_->Read(offset, n, result, scratch); |
196 | 0 | } |
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 | 0 | WritableFileImpl(FileState* file) : file_(file) { file_->Ref(); } |
206 | | |
207 | 0 | ~WritableFileImpl() override { file_->Unref(); } |
208 | | |
209 | 0 | Status Append(const Slice& data) override { return file_->Append(data); } |
210 | | |
211 | 0 | Status Close() override { return Status::OK(); } |
212 | 0 | Status Flush() override { return Status::OK(); } |
213 | 0 | 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 | | 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 | 0 | explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) {} |
228 | | |
229 | 0 | ~InMemoryEnv() override { |
230 | 0 | for (const auto& kvp : file_map_) { Branch (230:26): [True: 0, False: 0]
|
231 | 0 | kvp.second->Unref(); |
232 | 0 | } |
233 | 0 | } |
234 | | |
235 | | // Partial implementation of the Env interface. |
236 | | Status NewSequentialFile(const std::string& fname, |
237 | 0 | SequentialFile** result) override { |
238 | 0 | MutexLock lock(&mutex_); |
239 | 0 | if (file_map_.find(fname) == file_map_.end()) { Branch (239:9): [True: 0, False: 0]
|
240 | 0 | *result = nullptr; |
241 | 0 | return Status::IOError(fname, "File not found"); |
242 | 0 | } |
243 | | |
244 | 0 | *result = new SequentialFileImpl(file_map_[fname]); |
245 | 0 | return Status::OK(); |
246 | 0 | } |
247 | | |
248 | | Status NewRandomAccessFile(const std::string& fname, |
249 | 0 | RandomAccessFile** result) override { |
250 | 0 | MutexLock lock(&mutex_); |
251 | 0 | if (file_map_.find(fname) == file_map_.end()) { Branch (251:9): [True: 0, False: 0]
|
252 | 0 | *result = nullptr; |
253 | 0 | return Status::IOError(fname, "File not found"); |
254 | 0 | } |
255 | | |
256 | 0 | *result = new RandomAccessFileImpl(file_map_[fname]); |
257 | 0 | return Status::OK(); |
258 | 0 | } |
259 | | |
260 | | Status NewWritableFile(const std::string& fname, |
261 | 0 | WritableFile** result) override { |
262 | 0 | MutexLock lock(&mutex_); |
263 | 0 | FileSystem::iterator it = file_map_.find(fname); |
264 | |
|
265 | 0 | FileState* file; |
266 | 0 | if (it == file_map_.end()) { Branch (266:9): [True: 0, False: 0]
|
267 | | // File is not currently open. |
268 | 0 | file = new FileState(); |
269 | 0 | file->Ref(); |
270 | 0 | file_map_[fname] = file; |
271 | 0 | } else { |
272 | 0 | file = it->second; |
273 | 0 | file->Truncate(); |
274 | 0 | } |
275 | |
|
276 | 0 | *result = new WritableFileImpl(file); |
277 | 0 | return Status::OK(); |
278 | 0 | } |
279 | | |
280 | | Status NewAppendableFile(const std::string& fname, |
281 | 0 | 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) { Branch (285:9): [True: 0, False: 0]
|
286 | 0 | file = new FileState(); |
287 | 0 | file->Ref(); |
288 | 0 | } |
289 | 0 | *result = new WritableFileImpl(file); |
290 | 0 | return Status::OK(); |
291 | 0 | } |
292 | | |
293 | 0 | bool FileExists(const std::string& fname) override { |
294 | 0 | MutexLock lock(&mutex_); |
295 | 0 | return file_map_.find(fname) != file_map_.end(); |
296 | 0 | } |
297 | | |
298 | | Status GetChildren(const std::string& dir, |
299 | 0 | std::vector<std::string>* result) override { |
300 | 0 | MutexLock lock(&mutex_); |
301 | 0 | result->clear(); |
302 | |
|
303 | 0 | for (const auto& kvp : file_map_) { Branch (303:26): [True: 0, False: 0]
|
304 | 0 | const std::string& filename = kvp.first; |
305 | |
|
306 | 0 | if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' && Branch (306:11): [True: 0, False: 0]
Branch (306:11): [True: 0, False: 0]
Branch (306:48): [True: 0, False: 0]
|
307 | 0 | Slice(filename).starts_with(Slice(dir))) { Branch (307:11): [True: 0, False: 0]
|
308 | 0 | result->push_back(filename.substr(dir.size() + 1)); |
309 | 0 | } |
310 | 0 | } |
311 | |
|
312 | 0 | return Status::OK(); |
313 | 0 | } |
314 | | |
315 | | void DeleteFileInternal(const std::string& fname) |
316 | 0 | EXCLUSIVE_LOCKS_REQUIRED(mutex_) { |
317 | 0 | if (file_map_.find(fname) == file_map_.end()) { Branch (317:9): [True: 0, False: 0]
|
318 | 0 | return; |
319 | 0 | } |
320 | | |
321 | 0 | file_map_[fname]->Unref(); |
322 | 0 | file_map_.erase(fname); |
323 | 0 | } |
324 | | |
325 | 0 | Status DeleteFile(const std::string& fname) override { |
326 | 0 | MutexLock lock(&mutex_); |
327 | 0 | if (file_map_.find(fname) == file_map_.end()) { Branch (327:9): [True: 0, False: 0]
|
328 | 0 | return Status::IOError(fname, "File not found"); |
329 | 0 | } |
330 | | |
331 | 0 | DeleteFileInternal(fname); |
332 | 0 | return Status::OK(); |
333 | 0 | } |
334 | | |
335 | 0 | 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()) { Branch (341:9): [True: 0, False: 0]
|
342 | 0 | return Status::IOError(fname, "File not found"); |
343 | 0 | } |
344 | | |
345 | 0 | *file_size = file_map_[fname]->Size(); |
346 | 0 | return Status::OK(); |
347 | 0 | } |
348 | | |
349 | | Status RenameFile(const std::string& src, |
350 | 0 | const std::string& target) override { |
351 | 0 | MutexLock lock(&mutex_); |
352 | 0 | if (file_map_.find(src) == file_map_.end()) { Branch (352:9): [True: 0, False: 0]
|
353 | 0 | return Status::IOError(src, "File not found"); |
354 | 0 | } |
355 | | |
356 | 0 | DeleteFileInternal(target); |
357 | 0 | file_map_[target] = file_map_[src]; |
358 | 0 | file_map_.erase(src); |
359 | 0 | return Status::OK(); |
360 | 0 | } |
361 | | |
362 | 0 | Status LockFile(const std::string& fname, FileLock** lock) override { |
363 | 0 | *lock = new FileLock; |
364 | 0 | return Status::OK(); |
365 | 0 | } |
366 | | |
367 | 0 | Status UnlockFile(FileLock* lock) override { |
368 | 0 | delete lock; |
369 | 0 | return Status::OK(); |
370 | 0 | } |
371 | | |
372 | 0 | Status GetTestDirectory(std::string* path) override { |
373 | 0 | *path = "/test"; |
374 | 0 | return Status::OK(); |
375 | 0 | } |
376 | | |
377 | 0 | Status NewLogger(const std::string& fname, Logger** result) override { |
378 | 0 | *result = new NoOpLogger; |
379 | 0 | return Status::OK(); |
380 | 0 | } |
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 | 0 | Env* NewMemEnv(Env* base_env) { return new InMemoryEnv(base_env); } |
393 | | |
394 | | } // namespace leveldb |