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 "app/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 app { | |
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, | |
122 base::StringPiece* data) const { | |
123 // It won't be hard to make this endian-agnostic, but it's not worth | |
124 // bothering to do right now. | |
125 #if defined(__BYTE_ORDER) | |
126 // Linux check | |
127 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, | |
128 datapack_assumes_little_endian); | |
129 #elif defined(__BIG_ENDIAN__) | |
130 // Mac check | |
131 #error DataPack assumes little endian | |
132 #endif | |
133 | |
134 DataPackEntry* target = reinterpret_cast<DataPackEntry*>( | |
135 bsearch(&resource_id, mmap_->data() + kHeaderLength, resource_count_, | |
136 sizeof(DataPackEntry), DataPackEntry::CompareById)); | |
137 if (!target) { | |
138 return false; | |
139 } | |
140 | |
141 data->set(mmap_->data() + target->file_offset, target->length); | |
142 return true; | |
143 } | |
144 | |
145 RefCountedStaticMemory* DataPack::GetStaticMemory(uint32 resource_id) const { | |
146 base::StringPiece piece; | |
147 if (!GetStringPiece(resource_id, &piece)) | |
148 return NULL; | |
149 | |
150 return new RefCountedStaticMemory( | |
151 reinterpret_cast<const unsigned char*>(piece.data()), piece.length()); | |
152 } | |
153 | |
154 // static | |
155 bool DataPack::WritePack(const FilePath& path, | |
156 const std::map<uint32, base::StringPiece>& resources) { | |
157 FILE* file = file_util::OpenFile(path, "wb"); | |
158 if (!file) | |
159 return false; | |
160 | |
161 if (fwrite(&kFileFormatVersion, 1, kWord, file) != kWord) { | |
162 LOG(ERROR) << "Failed to write file version"; | |
163 file_util::CloseFile(file); | |
164 return false; | |
165 } | |
166 | |
167 // Note: the python version of this function explicitly sorted keys, but | |
168 // std::map is a sorted associative container, we shouldn't have to do that. | |
169 uint32 entry_count = resources.size(); | |
170 if (fwrite(&entry_count, 1, kWord, file) != kWord) { | |
171 LOG(ERROR) << "Failed to write entry count"; | |
172 file_util::CloseFile(file); | |
173 return false; | |
174 } | |
175 | |
176 // Each entry is 3 uint32s. | |
177 uint32 index_length = entry_count * 3 * kWord; | |
178 uint32 data_offset = kHeaderLength + index_length; | |
179 for (std::map<uint32, base::StringPiece>::const_iterator it = | |
180 resources.begin(); | |
181 it != resources.end(); ++it) { | |
182 if (fwrite(&it->first, 1, kWord, file) != kWord) { | |
183 LOG(ERROR) << "Failed to write id for " << it->first; | |
184 file_util::CloseFile(file); | |
185 return false; | |
186 } | |
187 | |
188 if (fwrite(&data_offset, 1, kWord, file) != kWord) { | |
189 LOG(ERROR) << "Failed to write offset for " << it->first; | |
190 file_util::CloseFile(file); | |
191 return false; | |
192 } | |
193 | |
194 uint32 len = it->second.length(); | |
195 if (fwrite(&len, 1, kWord, file) != kWord) { | |
196 LOG(ERROR) << "Failed to write length for " << it->first; | |
197 file_util::CloseFile(file); | |
198 return false; | |
199 } | |
200 | |
201 data_offset += len; | |
202 } | |
203 | |
204 for (std::map<uint32, base::StringPiece>::const_iterator it = | |
205 resources.begin(); | |
206 it != resources.end(); ++it) { | |
207 if (fwrite(it->second.data(), it->second.length(), 1, file) != 1) { | |
208 LOG(ERROR) << "Failed to write data for " << it->first; | |
209 file_util::CloseFile(file); | |
210 return false; | |
211 } | |
212 } | |
213 | |
214 file_util::CloseFile(file); | |
215 | |
216 return true; | |
217 } | |
218 | |
219 } // namespace app | |
OLD | NEW |