Ion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
zipassetmanager.cc
Go to the documentation of this file.
1 
19 
20 #include <cstring>
21 #include <vector>
22 
23 #include "ion/base/invalid.h"
24 #include "ion/base/lockguards.h"
25 #include "ion/base/logging.h"
28 #include "ion/base/stringutils.h"
29 #include "ion/port/fileutils.h"
30 
31 #include "third_party/unzip/unzip.h"
33 #include "third_party/zlib/src/contrib/minizip/ioapi.h"
34 
35 namespace ion {
36 namespace base {
37 
38 namespace {
39 
40 static const char kManifestFilename[] = "__asset_manifest__.txt";
41 
42 } // anonymous namespace
43 
44 ZipAssetManager::ZipAssetManager() {}
45 
47  Reset();
48 }
49 
50 bool ZipAssetManager::RegisterAssetData(const void* data, size_t data_size) {
52  zlib_filefunc_def def;
53  voidpf mem_zipfile =
54  mem_simple_create_file(&def, const_cast<void*>(data), data_size);
55  if (void* zipfile = unzAttach(mem_zipfile, &def)) {
57  FileInfo file_info;
58  file_info.zip_handle = zipfile;
59  ZipAssetManager* manager = GetManager();
60  LockGuard guard(&manager->mutex_);
61  manager->zipfiles_.insert(zipfile);
62 
63  bool contains_manifest = false;
64  {
66  static const int kBufSize = 1024;
68  unz_file_info info;
69  do {
70  if (unzGetCurrentFileInfo(
71  zipfile, &info, buf.Get(), kBufSize, 0, 0, 0, 0) == UNZ_OK) {
72 #if ION_DEBUG
73  if (manager->file_cache_.find(buf.Get()) !=
74  manager->file_cache_.end()) {
75  DLOG(WARNING)
76  << "Same file registered multiple times risks use after free "
77  << "if the result of GetFileData is still in use. "
78  << "Duplicate entry: " << buf.Get();
79  }
80 #endif
81  manager->file_cache_[buf.Get()] = file_info;
82  manager->file_cache_[buf.Get()].data_ptr.reset(new std::string());
83  if (strcmp(kManifestFilename, buf.Get()) == 0)
84  contains_manifest = true;
85  }
86  } while (unzGoToNextFile(zipfile) == UNZ_OK);
87  }
88 
90  if (contains_manifest) {
93  const std::vector<std::string> mappings =
95  manager->GetFileDataLocked(kManifestFilename, NULL), "\n");
96  const size_t count = mappings.size();
97  for (size_t i = 0; i < count; ++i) {
98  std::vector<std::string> mapping =
99  base::SplitString(mappings[i], "|");
100  DCHECK_EQ(2U, mapping.size());
101  while (base::StartsWith(mapping[0], "/"))
102  base::RemovePrefix("/", &mapping[0]);
103  FileCache::iterator it =
104  manager->file_cache_.find(port::GetCanonicalFilePath(mapping[0]));
105  DCHECK(it != manager->file_cache_.end());
106  it->second.original_name = mapping[1];
107  it->second.timestamp = std::chrono::system_clock::time_point();
108  port::GetFileModificationTime(mapping[1], &it->second.timestamp);
109  }
111  manager->file_cache_.erase(kManifestFilename);
112  }
113  return true;
114  } else {
115  free(mem_zipfile);
116  return false;
117  }
118 }
119 
120 bool ZipAssetManager::ContainsFile(const std::string& filename) {
121  ZipAssetManager* manager = GetManager();
122  LockGuard guard(&manager->mutex_);
123  return manager->ContainsFileLocked(filename);
124 }
125 
126 bool ZipAssetManager::ContainsFileLocked(const std::string& filename) {
127  return file_cache_.find(filename) != file_cache_.end();
128 }
129 
130 bool ZipAssetManager::IsFileCached(const std::string& filename) {
131  ZipAssetManager* manager = GetManager();
132  LockGuard guard(&manager->mutex_);
133  FileCache::const_iterator it = manager->file_cache_.find(filename);
134  return it != manager->file_cache_.end() &&
135  manager->FileIsCached(it->second);
136 }
137 
138 std::vector<std::string> ZipAssetManager::GetRegisteredFileNames() {
139  ZipAssetManager* manager = GetManager();
140  LockGuard guard(&manager->mutex_);
141  std::vector<std::string> filenames;
142  for (const auto& file : manager->file_cache_)
143  filenames.push_back(file.first);
144  return filenames;
145 }
146 
147 std::shared_ptr<const std::string> ZipAssetManager::GetFileDataPtr(
148  const std::string& filename) {
149  ZipAssetManager* manager = GetManager();
150  LockGuard guard(&manager->mutex_);
151  if (!IsInvalidReference(manager->GetFileDataLocked(filename, NULL))) {
152  FileCache::iterator it = manager->file_cache_.find(filename);
153  return it->second.data_ptr;
154  }
155  return std::shared_ptr<const std::string>();
156 }
157 
158 const std::string& ZipAssetManager::GetFileData(const std::string& filename) {
159  ZipAssetManager* manager = GetManager();
160  LockGuard guard(&manager->mutex_);
161  return manager->GetFileDataLocked(filename, NULL);
162 }
163 
164 bool ZipAssetManager::GetFileDataNoCache(const std::string& filename,
165  std::string* out) {
166  ZipAssetManager* manager = GetManager();
167  LockGuard guard(&manager->mutex_);
168  return !IsInvalidReference(manager->GetFileDataLocked(filename, out));
169 }
170 
171 const std::string& ZipAssetManager::GetFileDataLocked(
172  const std::string& filename, std::string* out) {
173  if (!ContainsFileLocked(filename)) {
175  return InvalidReference<const std::string>();
176  } else {
177  FileCache::iterator it = file_cache_.find(filename);
178  if (!FileIsCached(it->second)) {
180  if (!out)
181  out = it->second.data_ptr.get();
182  else
183  out->clear();
185  unz_file_info info;
186  if (unzLocateFile(it->second.zip_handle, filename.c_str(), 0) == UNZ_OK &&
187  unzOpenCurrentFile(it->second.zip_handle) == UNZ_OK &&
188  unzGetCurrentFileInfo(
189  it->second.zip_handle, &info, 0, 0, 0, 0, 0, 0) ==
190  UNZ_OK) {
192  out->resize(info.uncompressed_size);
194  unzReadCurrentFile(it->second.zip_handle, &(*out)[0],
195  static_cast<unsigned int>(info.uncompressed_size));
196  }
197  return *out;
198  } else if (out) {
200  out->clear();
201  out->swap(*it->second.data_ptr);
202  return *out;
203  } else {
204  return *it->second.data_ptr;
205  }
206  }
207 }
208 
209 bool ZipAssetManager::SetFileData(const std::string& filename,
210  const std::string& source) {
211  ZipAssetManager* manager = GetManager();
212  LockGuard guard(&manager->mutex_);
213  if (!manager->ContainsFileLocked(filename)) {
214  return false;
215  } else {
216  FileCache::iterator it = manager->file_cache_.find(filename);
217  *it->second.data_ptr = source;
218  it->second.timestamp = std::chrono::system_clock::now();
219  return true;
220  }
221 }
222 
223 bool ZipAssetManager::SaveFileData(const std::string& filename) {
224  ZipAssetManager* manager = GetManager();
225  LockGuard guard(&manager->mutex_);
226  const std::string& data = manager->GetFileDataLocked(filename, NULL);
228  FileCache::const_iterator it = manager->file_cache_.find(filename);
229  if (it != manager->file_cache_.end() &&
230  !base::IsInvalidReference(data) && !it->second.original_name.empty()) {
232  if (FILE* fp = port::OpenFile(it->second.original_name, "wb")) {
233  const size_t count =
234  fwrite(data.c_str(), sizeof(data[0]), data.length(), fp);
235  fclose(fp);
236  return count == data.length();
237  }
238  }
239  return false;
240 }
241 
243  ZipAssetManager* manager = GetManager();
244  LockGuard guard(&manager->mutex_);
245  for (std::set<void*>::iterator it = manager->zipfiles_.begin();
246  it != manager->zipfiles_.end(); it++)
247  unzClose(*it);
248  manager->file_cache_.clear();
249  manager->zipfiles_.clear();
250 }
251 
253  const std::string& filename,
254  std::chrono::system_clock::time_point* timestamp) {
255  ZipAssetManager* manager = GetManager();
256  LockGuard guard(&manager->mutex_);
257  std::chrono::system_clock::time_point new_timestamp;
258  FileCache::iterator it = manager->file_cache_.find(filename);
259  if (it != manager->file_cache_.end() && !it->second.original_name.empty()) {
260  const bool found =
261  port::GetFileModificationTime(it->second.original_name, &new_timestamp);
262  if (found && new_timestamp > it->second.timestamp) {
263  it->second.timestamp = new_timestamp;
265  if (FILE* fp = port::OpenFile(it->second.original_name, "rb")) {
267  fseek(fp, 0, SEEK_END);
268  const size_t length = ftell(fp);
269  rewind(fp);
270 
272  it->second.data_ptr->resize(length);
273  fread(&((*it->second.data_ptr)[0]), sizeof(char), length, fp);
274  fclose(fp);
275  }
276  *timestamp = new_timestamp;
277  return true;
278  }
279  }
280 
281  return false;
282 }
283 
284 ZipAssetManager* ZipAssetManager::GetManager() {
288  return s_manager;
289 }
290 
291 bool ZipAssetManager::FileIsCached(const FileInfo& info) {
292  return !info.data_ptr->empty();
293 }
294 
295 } // namespace base
296 } // namespace ion
bool IsInvalidReference(const T &value)
IsInvalidReference() returns true if a passed const reference of type T has an address of InvalidRefe...
Definition: invalid.h:41
kShortTerm is used for objects that are very transient in nature, such as scratch memory used to comp...
Definition: allocator.h:36
bool StartsWith(const std::string &target, const std::string &start)
Returns whether target begins with start.
Definition: stringutils.h:76
T * Get() const
Returns a pointer to the allocated T instance(s).
#define ION_DECLARE_SAFE_STATIC_POINTER(type, variable)
Declare a static non-array pointer and calls a default constructor.
static bool RegisterAssetData(const void *data, size_t data_size)
Registers a data pointer with a certain length with the manager.
ZipAssetManager manages all zipfile assets in Ion.
static void Reset()
Resets the manager back to its initial, empty state.
#define DLOG(severity)
Same as LOG(severity), but only logs in debug mode.
Definition: logging.h:230
static bool SetFileData(const std::string &filename, const std::string &source)
Sets the data of the passed filename if the manager contains it.
#define DCHECK(expr)
Definition: logging.h:331
static bool UpdateFileIfChanged(const std::string &filename, std::chrono::system_clock::time_point *timestamp)
If the source file of a zipped file is available on disk (based on the file's manifest), this function updates the cached unzipped data from the source file if it has changed since the data was registered and the source file is readable.
static std::vector< std::string > GetRegisteredFileNames()
Returns the list of registered filenames.
std::vector< std::string > ION_API SplitString(const std::string &str, const std::string &delimiters)
Splits a string into a vector of substrings, given a set of delimiter characters (expressed as a stri...
Definition: stringutils.cc:187
uint32 length
static const std::string & GetFileData(const std::string &filename)
Returns the data of the passed filename if the manager contains it.
bool RemovePrefix(const std::string &prefix, std::string *target)
Removes prefix from the beginning of target if target starts with it.
Definition: stringutils.h:102
A LockGuard locks a mutex when created, and unlocks it when destroyed.
Definition: lockguards.h:90
static bool ContainsFile(const std::string &filename)
Returns whether the manager contains the passed filename.
static bool SaveFileData(const std::string &filename)
Attempts to save the latest cached data of the passed filename back to the original source file on di...
Copyright 2016 Google Inc.
~ZipAssetManager()
The destructor is public so that the StaticDeleter that destroys the manager can access it...
#define DCHECK_EQ(val1, val2)
Definition: logging.h:332
bool GetFileModificationTime(const std::string &path, std::chrono::system_clock::time_point *time)
Reads the last modification time of the passed file path into time and returns true, iff the file exists.
Definition: fileutils.cc:74
static bool IsFileCached(const std::string &filename)
Returns whether the file is cached in the manager.
std::string GetCanonicalFilePath(const std::string &path)
Returns a canonical version of a file path string.
Definition: fileutils.cc:40
static std::shared_ptr< const std::string > GetFileDataPtr(const std::string &filename)
Returns the shared pointer to the data of the passed filename if the manager contains it...
FILE * OpenFile(const std::string &path, const std::string &mode)
Opens the file at path and returns a FILE pointer suitable for passing to fread, fwrite, fclose, etc.
Definition: fileutils.cc:180
This template class can be used in situations where you want to allocate an object that is not necess...
static bool GetFileDataNoCache(const std::string &filename, std::string *out)
As above but the decompressed bytes are not internally cached.