Shaka Packager SDK
memory_file.cc
1 // Copyright 2015 Google Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include "packager/file/memory_file.h"
8 
9 #include <string.h> // for memcpy
10 
11 #include <algorithm>
12 #include <map>
13 #include <memory>
14 #include <set>
15 
16 #include "packager/base/logging.h"
17 #include "packager/base/synchronization/lock.h"
18 
19 namespace shaka {
20 namespace {
21 
22 // A helper filesystem object. This holds the data for the memory files.
23 class FileSystem {
24  public:
25  ~FileSystem() {}
26 
27  static FileSystem* Instance() {
28  static FileSystem instance;
29  return &instance;
30  }
31 
32  void Delete(const std::string& file_name) {
33  base::AutoLock auto_lock(lock_);
34 
35  if (open_files_.find(file_name) != open_files_.end()) {
36  LOG(ERROR) << "File '" << file_name
37  << "' is still open. Deleting an open MemoryFile is not "
38  "allowed. Exit without deleting the file.";
39  return;
40  }
41 
42  files_.erase(file_name);
43  }
44 
45  void DeleteAll() {
46  base::AutoLock auto_lock(lock_);
47  if (!open_files_.empty()) {
48  LOG(ERROR) << "There are still files open. Deleting an open MemoryFile "
49  "is not allowed. Exit without deleting the file.";
50  return;
51  }
52  files_.clear();
53  }
54 
55  std::vector<uint8_t>* Open(const std::string& file_name,
56  const std::string& mode) {
57  base::AutoLock auto_lock(lock_);
58 
59  if (open_files_.find(file_name) != open_files_.end()) {
60  NOTIMPLEMENTED() << "File '" << file_name
61  << "' is already open. MemoryFile does not support "
62  "open the same file before it is closed.";
63  return nullptr;
64  }
65 
66  auto iter = files_.find(file_name);
67  if (mode == "r") {
68  if (iter == files_.end())
69  return nullptr;
70  } else if (mode == "w") {
71  if (iter != files_.end())
72  iter->second.clear();
73  } else {
74  NOTIMPLEMENTED() << "File mode '" << mode
75  << "' not supported by MemoryFile";
76  return nullptr;
77  }
78 
79  open_files_[file_name] = mode;
80  return &files_[file_name];
81  }
82 
83  bool Close(const std::string& file_name) {
84  base::AutoLock auto_lock(lock_);
85 
86  auto iter = open_files_.find(file_name);
87  if (iter == open_files_.end()) {
88  LOG(ERROR) << "Cannot close file '" << file_name
89  << "' which is not open.";
90  return false;
91  }
92 
93  open_files_.erase(iter);
94  return true;
95  }
96 
97  private:
98  FileSystem(const FileSystem&) = delete;
99  FileSystem& operator=(const FileSystem&) = delete;
100 
101  FileSystem() = default;
102 
103  // Filename to file data map.
104  std::map<std::string, std::vector<uint8_t>> files_;
105  // Filename to file open modes map.
106  std::map<std::string, std::string> open_files_;
107 
108  base::Lock lock_;
109 };
110 
111 } // namespace
112 
113 MemoryFile::MemoryFile(const std::string& file_name, const std::string& mode)
114  : File(file_name), mode_(mode), file_(NULL), position_(0) {}
115 
116 MemoryFile::~MemoryFile() {}
117 
118 bool MemoryFile::Close() {
119  if (!FileSystem::Instance()->Close(file_name()))
120  return false;
121  delete this;
122  return true;
123 }
124 
125 int64_t MemoryFile::Read(void* buffer, uint64_t length) {
126  const uint64_t size = Size();
127  DCHECK_LE(position_, size);
128  if (position_ >= size)
129  return 0;
130 
131  const uint64_t bytes_to_read = std::min(length, size - position_);
132  memcpy(buffer, &(*file_)[position_], bytes_to_read);
133  position_ += bytes_to_read;
134  return bytes_to_read;
135 }
136 
137 int64_t MemoryFile::Write(const void* buffer, uint64_t length) {
138  // If length is zero, we won't resize the buffer and it is possible for
139  // |position| to equal the length of the buffer. This will cause a segfault
140  // when indexing into the buffer for the memcpy.
141  if (length == 0) {
142  return 0;
143  }
144 
145  const uint64_t size = Size();
146  if (size < position_ + length) {
147  file_->resize(position_ + length);
148  }
149 
150  memcpy(&(*file_)[position_], buffer, length);
151  position_ += length;
152  return length;
153 }
154 
155 int64_t MemoryFile::Size() {
156  DCHECK(file_);
157  return file_->size();
158 }
159 
160 bool MemoryFile::Flush() {
161  return true;
162 }
163 
164 bool MemoryFile::Seek(uint64_t position) {
165  if (Size() < static_cast<int64_t>(position))
166  return false;
167 
168  position_ = position;
169  return true;
170 }
171 
172 bool MemoryFile::Tell(uint64_t* position) {
173  *position = position_;
174  return true;
175 }
176 
177 bool MemoryFile::Open() {
178  file_ = FileSystem::Instance()->Open(file_name(), mode_);
179  if (!file_)
180  return false;
181 
182  position_ = 0;
183  return true;
184 }
185 
186 void MemoryFile::DeleteAll() {
187  FileSystem::Instance()->DeleteAll();
188 }
189 
190 void MemoryFile::Delete(const std::string& file_name) {
191  FileSystem::Instance()->Delete(file_name);
192 }
193 
194 } // namespace shaka
All the methods that are virtual are virtual for mocking.