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