Index: ui/base/resource/data_pack.cc |
diff --git a/ui/base/resource/data_pack.cc b/ui/base/resource/data_pack.cc |
index 3d07d985a2a7acb35e10dab926e17cc515fafa57..511149d4d9f7185b4f7f0d53caea08d098b07f28 100644 |
--- a/ui/base/resource/data_pack.cc |
+++ b/ui/base/resource/data_pack.cc |
@@ -24,31 +24,14 @@ |
namespace { |
-static const uint32_t kFileFormatVersion = 4; |
-// Length of file header: version, entry count and text encoding type. |
-static const size_t kHeaderLength = 2 * sizeof(uint32_t) + sizeof(uint8_t); |
- |
-#pragma pack(push, 2) |
-struct DataPackEntry { |
- uint16_t resource_id; |
- uint32_t file_offset; |
- |
- static int CompareById(const void* void_key, const void* void_entry) { |
- uint16_t key = *reinterpret_cast<const uint16_t*>(void_key); |
- const DataPackEntry* entry = |
- reinterpret_cast<const DataPackEntry*>(void_entry); |
- if (key < entry->resource_id) { |
- return -1; |
- } else if (key > entry->resource_id) { |
- return 1; |
- } else { |
- return 0; |
- } |
- } |
-}; |
-#pragma pack(pop) |
- |
-static_assert(sizeof(DataPackEntry) == 6, "size of entry must be six"); |
+static const uint32_t kFileFormatV4 = 4; |
+static const uint32_t kFileFormatV5 = 5; |
+// int32(version), int32(resource_count), int8(encoding) |
+static const size_t kHeaderLengthV4 = 2 * sizeof(uint32_t) + sizeof(uint8_t); |
+// int32(version), int8(encoding), 3 bytes padding, |
+// int16(resource_count), int16(alias_count) |
+static const size_t kHeaderLengthV5 = |
+ sizeof(uint32_t) + sizeof(uint8_t) * 4 + sizeof(uint16_t) * 2; |
// We're crashing when trying to load a pak file on Windows. Add some error |
// codes for logging. |
@@ -102,6 +85,30 @@ void MaybePrintResourceId(uint16_t resource_id) { |
namespace ui { |
+#pragma pack(push, 2) |
+struct DataPack::Entry { |
+ uint16_t resource_id; |
+ uint32_t file_offset; |
+ |
+ static int CompareById(const void* void_key, const void* void_entry) { |
+ uint16_t key = *reinterpret_cast<const uint16_t*>(void_key); |
+ const Entry* entry = reinterpret_cast<const Entry*>(void_entry); |
+ return key - entry->resource_id; |
+ } |
+}; |
+ |
+struct DataPack::Alias { |
+ uint16_t resource_id; |
+ uint16_t entry_index; |
+ |
+ static int CompareById(const void* void_key, const void* void_entry) { |
+ uint16_t key = *reinterpret_cast<const uint16_t*>(void_key); |
+ const Alias* entry = reinterpret_cast<const Alias*>(void_entry); |
+ return key - entry->resource_id; |
+ } |
+}; |
+#pragma pack(pop) |
+ |
// Abstraction of a data source (memory mapped file or in-memory buffer). |
class DataPack::DataSource { |
public: |
@@ -149,9 +156,15 @@ class DataPack::BufferDataSource : public DataPack::DataSource { |
}; |
DataPack::DataPack(ui::ScaleFactor scale_factor) |
- : resource_count_(0), |
+ : resource_table_(nullptr), |
+ resource_count_(0), |
+ alias_table_(nullptr), |
+ alias_count_(0), |
text_encoding_type_(BINARY), |
scale_factor_(scale_factor) { |
+ // Static assert must be within a DataPack member to appease visiblity rules. |
+ static_assert(sizeof(Entry) == 6, "size of Entry must be 6"); |
+ static_assert(sizeof(Alias) == 4, "size of Alias must be 4"); |
} |
DataPack::~DataPack() { |
@@ -193,29 +206,37 @@ bool DataPack::LoadFromBuffer(base::StringPiece buffer) { |
} |
bool DataPack::LoadImpl(std::unique_ptr<DataPack::DataSource> data_source) { |
- // Sanity check the header of the file. |
- if (kHeaderLength > data_source->GetLength()) { |
+ const uint8_t* data = data_source->GetData(); |
+ size_t data_length = data_source->GetLength(); |
+ // Parse the version and check for truncated header. |
+ uint32_t version = 0; |
+ if (data_length > sizeof(version)) |
+ version = reinterpret_cast<const uint32_t*>(data)[0]; |
+ size_t header_length = |
+ version == kFileFormatV4 ? kHeaderLengthV4 : kHeaderLengthV5; |
+ if (version == 0 || data_length < header_length) { |
DLOG(ERROR) << "Data pack file corruption: incomplete file header."; |
LogDataPackError(HEADER_TRUNCATED); |
return false; |
} |
// Parse the header of the file. |
- // First uint32_t: version; second: resource count; |
- const uint32_t* ptr = |
- reinterpret_cast<const uint32_t*>(data_source->GetData()); |
- uint32_t version = ptr[0]; |
- if (version != kFileFormatVersion) { |
+ if (version == kFileFormatV4) { |
+ resource_count_ = reinterpret_cast<const uint32_t*>(data)[1]; |
+ alias_count_ = 0; |
+ text_encoding_type_ = static_cast<TextEncodingType>(data[8]); |
+ } else if (version == kFileFormatV5) { |
+ // Version 5 added the alias table and changed the header format. |
+ text_encoding_type_ = static_cast<TextEncodingType>(data[4]); |
+ resource_count_ = reinterpret_cast<const uint16_t*>(data)[4]; |
+ alias_count_ = reinterpret_cast<const uint16_t*>(data)[5]; |
+ } else { |
LOG(ERROR) << "Bad data pack version: got " << version << ", expected " |
- << kFileFormatVersion; |
+ << kFileFormatV4 << " or " << kFileFormatV5; |
LogDataPackError(BAD_VERSION); |
return false; |
} |
- resource_count_ = ptr[1]; |
- // third: text encoding. |
- const uint8_t* ptr_encoding = reinterpret_cast<const uint8_t*>(ptr + 2); |
- text_encoding_type_ = static_cast<TextEncodingType>(*ptr_encoding); |
if (text_encoding_type_ != UTF8 && text_encoding_type_ != UTF16 && |
text_encoding_type_ != BINARY) { |
LOG(ERROR) << "Bad data pack text encoding: got " << text_encoding_type_ |
@@ -227,35 +248,63 @@ bool DataPack::LoadImpl(std::unique_ptr<DataPack::DataSource> data_source) { |
// Sanity check the file. |
// 1) Check we have enough entries. There's an extra entry after the last item |
// which gives the length of the last item. |
- if (kHeaderLength + (resource_count_ + 1) * sizeof(DataPackEntry) > |
- data_source->GetLength()) { |
- LOG(ERROR) << "Data pack file corruption: too short for number of " |
- "entries specified."; |
+ size_t resource_table_size = (resource_count_ + 1) * sizeof(Entry); |
+ size_t alias_table_size = alias_count_ * sizeof(Alias); |
+ if (header_length + resource_table_size + alias_table_size > data_length) { |
+ LOG(ERROR) << "Data pack file corruption: " |
+ << "too short for number of entries."; |
LogDataPackError(INDEX_TRUNCATED); |
return false; |
} |
+ |
+ resource_table_ = reinterpret_cast<const Entry*>(&data[header_length]); |
+ alias_table_ = reinterpret_cast<const Alias*>( |
+ &data[header_length + resource_table_size]); |
+ |
// 2) Verify the entries are within the appropriate bounds. There's an extra |
// entry after the last item which gives us the length of the last item. |
for (size_t i = 0; i < resource_count_ + 1; ++i) { |
- const DataPackEntry* entry = reinterpret_cast<const DataPackEntry*>( |
- data_source->GetData() + kHeaderLength + (i * sizeof(DataPackEntry))); |
- if (entry->file_offset > data_source->GetLength()) { |
- LOG(ERROR) << "Entry #" << i << " in data pack points off end of file. " |
- << "Was the file corrupted?"; |
+ if (resource_table_[i].file_offset > data_length) { |
+ LOG(ERROR) << "Data pack file corruption: " |
+ << "Entry #" << i << " past end."; |
LogDataPackError(ENTRY_NOT_FOUND); |
return false; |
} |
} |
- data_source_ = std::move(data_source); |
+ // 3) Verify the aliases are within the appropriate bounds. |
+ for (size_t i = 0; i < alias_count_; ++i) { |
+ if (alias_table_[i].entry_index >= resource_count_) { |
+ LOG(ERROR) << "Data pack file corruption: " |
+ << "Alias #" << i << " past end."; |
+ LogDataPackError(ENTRY_NOT_FOUND); |
+ return false; |
+ } |
+ } |
+ data_source_ = std::move(data_source); |
return true; |
} |
+const DataPack::Entry* DataPack::LookupEntryById(uint16_t resource_id) const { |
+ // Search the resource table first as most resources will be in there. |
+ const Entry* ret = reinterpret_cast<const Entry*>( |
+ bsearch(&resource_id, resource_table_, resource_count_, sizeof(Entry), |
+ Entry::CompareById)); |
+ if (ret == nullptr) { |
+ // Search the alias table for the ~10% of entries which are aliases. |
+ const Alias* alias = reinterpret_cast<const Alias*>( |
+ bsearch(&resource_id, alias_table_, alias_count_, sizeof(Alias), |
+ Alias::CompareById)); |
+ if (alias != nullptr) { |
+ ret = &resource_table_[alias->entry_index]; |
+ } |
+ } |
+ return ret; |
+} |
+ |
bool DataPack::HasResource(uint16_t resource_id) const { |
- return !!bsearch(&resource_id, data_source_->GetData() + kHeaderLength, |
- resource_count_, sizeof(DataPackEntry), |
- DataPackEntry::CompareById); |
+ return !!LookupEntryById(resource_id); |
} |
bool DataPack::GetStringPiece(uint16_t resource_id, |
@@ -271,20 +320,19 @@ bool DataPack::GetStringPiece(uint16_t resource_id, |
#error DataPack assumes little endian |
#endif |
- const DataPackEntry* target = reinterpret_cast<const DataPackEntry*>(bsearch( |
- &resource_id, data_source_->GetData() + kHeaderLength, resource_count_, |
- sizeof(DataPackEntry), DataPackEntry::CompareById)); |
- if (!target) { |
+ const Entry* target = LookupEntryById(resource_id); |
+ if (!target) |
return false; |
- } |
- const DataPackEntry* next_entry = target + 1; |
+ const Entry* next_entry = target + 1; |
// If the next entry points beyond the end of the file this data pack's entry |
// table is corrupt. Log an error and return false. See |
// http://crbug.com/371301. |
- if (next_entry->file_offset > data_source_->GetLength()) { |
- size_t entry_index = target - reinterpret_cast<const DataPackEntry*>( |
- data_source_->GetData() + kHeaderLength); |
+ size_t entry_offset = |
+ reinterpret_cast<const uint8_t*>(next_entry) - data_source_->GetData(); |
+ size_t pak_size = data_source_->GetLength(); |
+ if (entry_offset > pak_size || next_entry->file_offset > pak_size) { |
+ size_t entry_index = target - resource_table_; |
LOG(ERROR) << "Entry #" << entry_index << " in data pack points off end " |
<< "of file. This should have been caught when loading. Was the " |
<< "file modified?"; |
@@ -320,9 +368,7 @@ ui::ScaleFactor DataPack::GetScaleFactor() const { |
void DataPack::CheckForDuplicateResources( |
const std::vector<std::unique_ptr<ResourceHandle>>& packs) { |
for (size_t i = 0; i < resource_count_ + 1; ++i) { |
- const DataPackEntry* entry = reinterpret_cast<const DataPackEntry*>( |
- data_source_->GetData() + kHeaderLength + (i * sizeof(DataPackEntry))); |
- const uint16_t resource_id = entry->resource_id; |
+ const uint16_t resource_id = resource_table_[i].resource_id; |
const float resource_scale = GetScaleForScaleFactor(scale_factor_); |
for (const auto& handle : packs) { |
if (GetScaleForScaleFactor(handle->GetScaleFactor()) != resource_scale) |
@@ -339,56 +385,44 @@ void DataPack::CheckForDuplicateResources( |
bool DataPack::WritePack(const base::FilePath& path, |
const std::map<uint16_t, base::StringPiece>& resources, |
TextEncodingType textEncodingType) { |
- FILE* file = base::OpenFile(path, "wb"); |
- if (!file) |
- return false; |
- |
- if (fwrite(&kFileFormatVersion, sizeof(kFileFormatVersion), 1, file) != 1) { |
- LOG(ERROR) << "Failed to write file version"; |
- base::CloseFile(file); |
- return false; |
- } |
- |
- // Note: the python version of this function explicitly sorted keys, but |
- // std::map is a sorted associative container, we shouldn't have to do that. |
- uint32_t entry_count = resources.size(); |
- if (fwrite(&entry_count, sizeof(entry_count), 1, file) != 1) { |
- LOG(ERROR) << "Failed to write entry count"; |
- base::CloseFile(file); |
- return false; |
- } |
- |
if (textEncodingType != UTF8 && textEncodingType != UTF16 && |
textEncodingType != BINARY) { |
LOG(ERROR) << "Invalid text encoding type, got " << textEncodingType |
<< ", expected between " << BINARY << " and " << UTF16; |
- base::CloseFile(file); |
return false; |
} |
- uint8_t write_buffer = static_cast<uint8_t>(textEncodingType); |
- if (fwrite(&write_buffer, sizeof(uint8_t), 1, file) != 1) { |
- LOG(ERROR) << "Failed to write file text resources encoding"; |
+ FILE* file = base::OpenFile(path, "wb"); |
+ if (!file) |
+ return false; |
+ |
+ uint32_t encoding = static_cast<uint32_t>(textEncodingType); |
+ // Note: the python version of this function explicitly sorted keys, but |
+ // std::map is a sorted associative container, we shouldn't have to do that. |
+ uint16_t entry_count = resources.size(); |
+ // Don't bother computing aliases (revisit if it becomes worth it). |
+ uint16_t alias_count = 0; |
+ |
+ if (fwrite(&kFileFormatV5, sizeof(kFileFormatV5), 1, file) != 1 || |
+ fwrite(&encoding, sizeof(uint32_t), 1, file) != 1 || |
+ fwrite(&entry_count, sizeof(entry_count), 1, file) != 1 || |
+ fwrite(&alias_count, sizeof(alias_count), 1, file) != 1) { |
+ LOG(ERROR) << "Failed to write header"; |
base::CloseFile(file); |
return false; |
} |
// Each entry is a uint16_t + a uint32_t. We have an extra entry after the |
// last item so we can compute the size of the list item. |
- uint32_t index_length = (entry_count + 1) * sizeof(DataPackEntry); |
- uint32_t data_offset = kHeaderLength + index_length; |
+ uint32_t index_length = (entry_count + 1) * sizeof(Entry); |
+ uint32_t data_offset = kHeaderLengthV5 + index_length; |
for (std::map<uint16_t, base::StringPiece>::const_iterator it = |
resources.begin(); |
it != resources.end(); ++it) { |
uint16_t resource_id = it->first; |
- if (fwrite(&resource_id, sizeof(resource_id), 1, file) != 1) { |
- LOG(ERROR) << "Failed to write id for " << resource_id; |
- base::CloseFile(file); |
- return false; |
- } |
- |
- if (fwrite(&data_offset, sizeof(data_offset), 1, file) != 1) { |
- LOG(ERROR) << "Failed to write offset for " << resource_id; |
+ if (fwrite(&resource_id, sizeof(resource_id), 1, file) != 1 || |
+ fwrite(&data_offset, sizeof(data_offset), 1, file) != 1) { |
+ LOG(ERROR) << "Failed to write entry for " << resource_id; |
base::CloseFile(file); |
return false; |
} |