| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium 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. | |
| 4 | |
| 5 #include "base/data_pack.h" | |
| 6 | |
| 7 #include <errno.h> | |
| 8 | |
| 9 #include "base/file_util.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/metrics/histogram.h" | |
| 12 #include "base/ref_counted_memory.h" | |
| 13 #include "base/string_piece.h" | |
| 14 | |
| 15 // For details of the file layout, see | |
| 16 // http://dev.chromium.org/developers/design-documents/linuxresourcesandlocalize
dstrings | |
| 17 | |
| 18 namespace { | |
| 19 | |
| 20 // A word is four bytes. | |
| 21 static const size_t kWord = 4; | |
| 22 | |
| 23 static const uint32 kFileFormatVersion = 1; | |
| 24 // Length of file header: version and entry count. | |
| 25 static const size_t kHeaderLength = 2 * sizeof(uint32); | |
| 26 | |
| 27 struct DataPackEntry { | |
| 28 uint32 resource_id; | |
| 29 uint32 file_offset; | |
| 30 uint32 length; | |
| 31 | |
| 32 static int CompareById(const void* void_key, const void* void_entry) { | |
| 33 uint32 key = *reinterpret_cast<const uint32*>(void_key); | |
| 34 const DataPackEntry* entry = | |
| 35 reinterpret_cast<const DataPackEntry*>(void_entry); | |
| 36 if (key < entry->resource_id) { | |
| 37 return -1; | |
| 38 } else if (key > entry->resource_id) { | |
| 39 return 1; | |
| 40 } else { | |
| 41 return 0; | |
| 42 } | |
| 43 } | |
| 44 }; | |
| 45 | |
| 46 COMPILE_ASSERT(sizeof(DataPackEntry) == 12, size_of_header_must_be_twelve); | |
| 47 | |
| 48 // We're crashing when trying to load a pak file on Windows. Add some error | |
| 49 // codes for logging. | |
| 50 // http://crbug.com/58056 | |
| 51 enum LoadErrors { | |
| 52 INIT_FAILED = 1, | |
| 53 BAD_VERSION, | |
| 54 INDEX_TRUNCATED, | |
| 55 ENTRY_NOT_FOUND, | |
| 56 | |
| 57 LOAD_ERRORS_COUNT, | |
| 58 }; | |
| 59 | |
| 60 } // anonymous namespace | |
| 61 | |
| 62 namespace base { | |
| 63 | |
| 64 // In .cc for MemoryMappedFile dtor. | |
| 65 DataPack::DataPack() : resource_count_(0) { | |
| 66 } | |
| 67 DataPack::~DataPack() { | |
| 68 } | |
| 69 | |
| 70 bool DataPack::Load(const FilePath& path) { | |
| 71 mmap_.reset(new file_util::MemoryMappedFile); | |
| 72 if (!mmap_->Initialize(path)) { | |
| 73 DLOG(ERROR) << "Failed to mmap datapack"; | |
| 74 UMA_HISTOGRAM_ENUMERATION("DataPack.Load", INIT_FAILED, | |
| 75 LOAD_ERRORS_COUNT); | |
| 76 return false; | |
| 77 } | |
| 78 | |
| 79 // Parse the header of the file. | |
| 80 // First uint32: version; second: resource count. | |
| 81 const uint32* ptr = reinterpret_cast<const uint32*>(mmap_->data()); | |
| 82 uint32 version = ptr[0]; | |
| 83 if (version != kFileFormatVersion) { | |
| 84 LOG(ERROR) << "Bad data pack version: got " << version << ", expected " | |
| 85 << kFileFormatVersion; | |
| 86 UMA_HISTOGRAM_ENUMERATION("DataPack.Load", BAD_VERSION, | |
| 87 LOAD_ERRORS_COUNT); | |
| 88 mmap_.reset(); | |
| 89 return false; | |
| 90 } | |
| 91 resource_count_ = ptr[1]; | |
| 92 | |
| 93 // Sanity check the file. | |
| 94 // 1) Check we have enough entries. | |
| 95 if (kHeaderLength + resource_count_ * sizeof(DataPackEntry) > | |
| 96 mmap_->length()) { | |
| 97 LOG(ERROR) << "Data pack file corruption: too short for number of " | |
| 98 "entries specified."; | |
| 99 UMA_HISTOGRAM_ENUMERATION("DataPack.Load", INDEX_TRUNCATED, | |
| 100 LOAD_ERRORS_COUNT); | |
| 101 mmap_.reset(); | |
| 102 return false; | |
| 103 } | |
| 104 // 2) Verify the entries are within the appropriate bounds. | |
| 105 for (size_t i = 0; i < resource_count_; ++i) { | |
| 106 const DataPackEntry* entry = reinterpret_cast<const DataPackEntry*>( | |
| 107 mmap_->data() + kHeaderLength + (i * sizeof(DataPackEntry))); | |
| 108 if (entry->file_offset + entry->length > mmap_->length()) { | |
| 109 LOG(ERROR) << "Entry #" << i << " in data pack points off end of file. " | |
| 110 << "Was the file corrupted?"; | |
| 111 UMA_HISTOGRAM_ENUMERATION("DataPack.Load", ENTRY_NOT_FOUND, | |
| 112 LOAD_ERRORS_COUNT); | |
| 113 mmap_.reset(); | |
| 114 return false; | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 return true; | |
| 119 } | |
| 120 | |
| 121 bool DataPack::GetStringPiece(uint32 resource_id, StringPiece* data) const { | |
| 122 // It won't be hard to make this endian-agnostic, but it's not worth | |
| 123 // bothering to do right now. | |
| 124 #if defined(__BYTE_ORDER) | |
| 125 // Linux check | |
| 126 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, | |
| 127 datapack_assumes_little_endian); | |
| 128 #elif defined(__BIG_ENDIAN__) | |
| 129 // Mac check | |
| 130 #error DataPack assumes little endian | |
| 131 #endif | |
| 132 | |
| 133 DataPackEntry* target = reinterpret_cast<DataPackEntry*>( | |
| 134 bsearch(&resource_id, mmap_->data() + kHeaderLength, resource_count_, | |
| 135 sizeof(DataPackEntry), DataPackEntry::CompareById)); | |
| 136 if (!target) { | |
| 137 return false; | |
| 138 } | |
| 139 | |
| 140 data->set(mmap_->data() + target->file_offset, target->length); | |
| 141 return true; | |
| 142 } | |
| 143 | |
| 144 RefCountedStaticMemory* DataPack::GetStaticMemory(uint32 resource_id) const { | |
| 145 base::StringPiece piece; | |
| 146 if (!GetStringPiece(resource_id, &piece)) | |
| 147 return NULL; | |
| 148 | |
| 149 return new RefCountedStaticMemory( | |
| 150 reinterpret_cast<const unsigned char*>(piece.data()), piece.length()); | |
| 151 } | |
| 152 | |
| 153 // static | |
| 154 bool DataPack::WritePack(const FilePath& path, | |
| 155 const std::map<uint32, StringPiece>& resources) { | |
| 156 FILE* file = file_util::OpenFile(path, "wb"); | |
| 157 if (!file) | |
| 158 return false; | |
| 159 | |
| 160 if (fwrite(&kFileFormatVersion, 1, kWord, file) != kWord) { | |
| 161 LOG(ERROR) << "Failed to write file version"; | |
| 162 file_util::CloseFile(file); | |
| 163 return false; | |
| 164 } | |
| 165 | |
| 166 // Note: the python version of this function explicitly sorted keys, but | |
| 167 // std::map is a sorted associative container, we shouldn't have to do that. | |
| 168 uint32 entry_count = resources.size(); | |
| 169 if (fwrite(&entry_count, 1, kWord, file) != kWord) { | |
| 170 LOG(ERROR) << "Failed to write entry count"; | |
| 171 file_util::CloseFile(file); | |
| 172 return false; | |
| 173 } | |
| 174 | |
| 175 // Each entry is 3 uint32s. | |
| 176 uint32 index_length = entry_count * 3 * kWord; | |
| 177 uint32 data_offset = kHeaderLength + index_length; | |
| 178 for (std::map<uint32, StringPiece>::const_iterator it = resources.begin(); | |
| 179 it != resources.end(); ++it) { | |
| 180 if (fwrite(&it->first, 1, kWord, file) != kWord) { | |
| 181 LOG(ERROR) << "Failed to write id for " << it->first; | |
| 182 file_util::CloseFile(file); | |
| 183 return false; | |
| 184 } | |
| 185 | |
| 186 if (fwrite(&data_offset, 1, kWord, file) != kWord) { | |
| 187 LOG(ERROR) << "Failed to write offset for " << it->first; | |
| 188 file_util::CloseFile(file); | |
| 189 return false; | |
| 190 } | |
| 191 | |
| 192 uint32 len = it->second.length(); | |
| 193 if (fwrite(&len, 1, kWord, file) != kWord) { | |
| 194 LOG(ERROR) << "Failed to write length for " << it->first; | |
| 195 file_util::CloseFile(file); | |
| 196 return false; | |
| 197 } | |
| 198 | |
| 199 data_offset += len; | |
| 200 } | |
| 201 | |
| 202 for (std::map<uint32, StringPiece>::const_iterator it = resources.begin(); | |
| 203 it != resources.end(); ++it) { | |
| 204 if (fwrite(it->second.data(), it->second.length(), 1, file) != 1) { | |
| 205 LOG(ERROR) << "Failed to write data for " << it->first; | |
| 206 file_util::CloseFile(file); | |
| 207 return false; | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 file_util::CloseFile(file); | |
| 212 | |
| 213 return true; | |
| 214 } | |
| 215 | |
| 216 } // namespace base | |
| OLD | NEW |