- add sources.
[platform/framework/web/crosswalk.git] / src / ui / base / resource / data_pack.cc
1 // Copyright (c) 2012 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 "ui/base/resource/data_pack.h"
6
7 #include <errno.h>
8
9 #include "base/file_util.h"
10 #include "base/files/memory_mapped_file.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted_memory.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_piece.h"
15
16 // For details of the file layout, see
17 // http://dev.chromium.org/developers/design-documents/linuxresourcesandlocalizedstrings
18
19 namespace {
20
21 static const uint32 kFileFormatVersion = 4;
22 // Length of file header: version, entry count and text encoding type.
23 static const size_t kHeaderLength = 2 * sizeof(uint32) + sizeof(uint8);
24
25 #pragma pack(push,2)
26 struct DataPackEntry {
27   uint16 resource_id;
28   uint32 file_offset;
29
30   static int CompareById(const void* void_key, const void* void_entry) {
31     uint16 key = *reinterpret_cast<const uint16*>(void_key);
32     const DataPackEntry* entry =
33         reinterpret_cast<const DataPackEntry*>(void_entry);
34     if (key < entry->resource_id) {
35       return -1;
36     } else if (key > entry->resource_id) {
37       return 1;
38     } else {
39       return 0;
40     }
41   }
42 };
43 #pragma pack(pop)
44
45 COMPILE_ASSERT(sizeof(DataPackEntry) == 6, size_of_entry_must_be_six);
46
47 // We're crashing when trying to load a pak file on Windows.  Add some error
48 // codes for logging.
49 // http://crbug.com/58056
50 enum LoadErrors {
51   INIT_FAILED = 1,
52   BAD_VERSION,
53   INDEX_TRUNCATED,
54   ENTRY_NOT_FOUND,
55   HEADER_TRUNCATED,
56   WRONG_ENCODING,
57   INIT_FAILED_FROM_FILE,
58
59   LOAD_ERRORS_COUNT,
60 };
61
62 }  // namespace
63
64 namespace ui {
65
66 DataPack::DataPack(ui::ScaleFactor scale_factor)
67     : resource_count_(0),
68       text_encoding_type_(BINARY),
69       scale_factor_(scale_factor) {
70 }
71
72 DataPack::~DataPack() {
73 }
74
75 bool DataPack::LoadFromPath(const base::FilePath& path) {
76   mmap_.reset(new base::MemoryMappedFile);
77   if (!mmap_->Initialize(path)) {
78     DLOG(ERROR) << "Failed to mmap datapack";
79     UMA_HISTOGRAM_ENUMERATION("DataPack.Load", INIT_FAILED,
80                               LOAD_ERRORS_COUNT);
81     mmap_.reset();
82     return false;
83   }
84   return LoadImpl();
85 }
86
87 bool DataPack::LoadFromFile(base::PlatformFile file) {
88   mmap_.reset(new base::MemoryMappedFile);
89   if (!mmap_->Initialize(file)) {
90     DLOG(ERROR) << "Failed to mmap datapack";
91     UMA_HISTOGRAM_ENUMERATION("DataPack.Load", INIT_FAILED_FROM_FILE,
92                               LOAD_ERRORS_COUNT);
93     mmap_.reset();
94     return false;
95   }
96   return LoadImpl();
97 }
98
99 bool DataPack::LoadImpl() {
100   // Sanity check the header of the file.
101   if (kHeaderLength > mmap_->length()) {
102     DLOG(ERROR) << "Data pack file corruption: incomplete file header.";
103     UMA_HISTOGRAM_ENUMERATION("DataPack.Load", HEADER_TRUNCATED,
104                               LOAD_ERRORS_COUNT);
105     mmap_.reset();
106     return false;
107   }
108
109   // Parse the header of the file.
110   // First uint32: version; second: resource count;
111   const uint32* ptr = reinterpret_cast<const uint32*>(mmap_->data());
112   uint32 version = ptr[0];
113   if (version != kFileFormatVersion) {
114     LOG(ERROR) << "Bad data pack version: got " << version << ", expected "
115                << kFileFormatVersion;
116     UMA_HISTOGRAM_ENUMERATION("DataPack.Load", BAD_VERSION,
117                               LOAD_ERRORS_COUNT);
118     mmap_.reset();
119     return false;
120   }
121   resource_count_ = ptr[1];
122
123   // third: text encoding.
124   const uint8* ptr_encoding = reinterpret_cast<const uint8*>(ptr + 2);
125   text_encoding_type_ = static_cast<TextEncodingType>(*ptr_encoding);
126   if (text_encoding_type_ != UTF8 && text_encoding_type_ != UTF16 &&
127       text_encoding_type_ != BINARY) {
128     LOG(ERROR) << "Bad data pack text encoding: got " << text_encoding_type_
129                << ", expected between " << BINARY << " and " << UTF16;
130     UMA_HISTOGRAM_ENUMERATION("DataPack.Load", WRONG_ENCODING,
131                               LOAD_ERRORS_COUNT);
132     mmap_.reset();
133     return false;
134   }
135
136   // Sanity check the file.
137   // 1) Check we have enough entries.
138   if (kHeaderLength + resource_count_ * sizeof(DataPackEntry) >
139       mmap_->length()) {
140     LOG(ERROR) << "Data pack file corruption: too short for number of "
141                   "entries specified.";
142     UMA_HISTOGRAM_ENUMERATION("DataPack.Load", INDEX_TRUNCATED,
143                               LOAD_ERRORS_COUNT);
144     mmap_.reset();
145     return false;
146   }
147   // 2) Verify the entries are within the appropriate bounds. There's an extra
148   // entry after the last item which gives us the length of the last item.
149   for (size_t i = 0; i < resource_count_ + 1; ++i) {
150     const DataPackEntry* entry = reinterpret_cast<const DataPackEntry*>(
151         mmap_->data() + kHeaderLength + (i * sizeof(DataPackEntry)));
152     if (entry->file_offset > mmap_->length()) {
153       LOG(ERROR) << "Entry #" << i << " in data pack points off end of file. "
154                  << "Was the file corrupted?";
155       UMA_HISTOGRAM_ENUMERATION("DataPack.Load", ENTRY_NOT_FOUND,
156                                 LOAD_ERRORS_COUNT);
157       mmap_.reset();
158       return false;
159     }
160   }
161
162   return true;
163 }
164
165 bool DataPack::HasResource(uint16 resource_id) const {
166   return !!bsearch(&resource_id, mmap_->data() + kHeaderLength, resource_count_,
167                    sizeof(DataPackEntry), DataPackEntry::CompareById);
168 }
169
170 bool DataPack::GetStringPiece(uint16 resource_id,
171                               base::StringPiece* data) const {
172   // It won't be hard to make this endian-agnostic, but it's not worth
173   // bothering to do right now.
174 #if defined(__BYTE_ORDER)
175   // Linux check
176   COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN,
177                  datapack_assumes_little_endian);
178 #elif defined(__BIG_ENDIAN__)
179   // Mac check
180   #error DataPack assumes little endian
181 #endif
182
183   const DataPackEntry* target = reinterpret_cast<const DataPackEntry*>(
184       bsearch(&resource_id, mmap_->data() + kHeaderLength, resource_count_,
185               sizeof(DataPackEntry), DataPackEntry::CompareById));
186   if (!target) {
187     return false;
188   }
189
190   const DataPackEntry* next_entry = target + 1;
191   size_t length = next_entry->file_offset - target->file_offset;
192
193   data->set(mmap_->data() + target->file_offset, length);
194   return true;
195 }
196
197 base::RefCountedStaticMemory* DataPack::GetStaticMemory(
198     uint16 resource_id) const {
199   base::StringPiece piece;
200   if (!GetStringPiece(resource_id, &piece))
201     return NULL;
202
203   return new base::RefCountedStaticMemory(
204       reinterpret_cast<const unsigned char*>(piece.data()), piece.length());
205 }
206
207 ResourceHandle::TextEncodingType DataPack::GetTextEncodingType() const {
208   return text_encoding_type_;
209 }
210
211 ui::ScaleFactor DataPack::GetScaleFactor() const {
212   return scale_factor_;
213 }
214
215 // static
216 bool DataPack::WritePack(const base::FilePath& path,
217                          const std::map<uint16, base::StringPiece>& resources,
218                          TextEncodingType textEncodingType) {
219   FILE* file = file_util::OpenFile(path, "wb");
220   if (!file)
221     return false;
222
223   if (fwrite(&kFileFormatVersion, sizeof(kFileFormatVersion), 1, file) != 1) {
224     LOG(ERROR) << "Failed to write file version";
225     file_util::CloseFile(file);
226     return false;
227   }
228
229   // Note: the python version of this function explicitly sorted keys, but
230   // std::map is a sorted associative container, we shouldn't have to do that.
231   uint32 entry_count = resources.size();
232   if (fwrite(&entry_count, sizeof(entry_count), 1, file) != 1) {
233     LOG(ERROR) << "Failed to write entry count";
234     file_util::CloseFile(file);
235     return false;
236   }
237
238   if (textEncodingType != UTF8 && textEncodingType != UTF16 &&
239       textEncodingType != BINARY) {
240     LOG(ERROR) << "Invalid text encoding type, got " << textEncodingType
241                << ", expected between " << BINARY << " and " << UTF16;
242     file_util::CloseFile(file);
243     return false;
244   }
245
246   uint8 write_buffer = textEncodingType;
247   if (fwrite(&write_buffer, sizeof(uint8), 1, file) != 1) {
248     LOG(ERROR) << "Failed to write file text resources encoding";
249     file_util::CloseFile(file);
250     return false;
251   }
252
253   // Each entry is a uint16 + a uint32. We have an extra entry after the last
254   // item so we can compute the size of the list item.
255   uint32 index_length = (entry_count + 1) * sizeof(DataPackEntry);
256   uint32 data_offset = kHeaderLength + index_length;
257   for (std::map<uint16, base::StringPiece>::const_iterator it =
258            resources.begin();
259        it != resources.end(); ++it) {
260     uint16 resource_id = it->first;
261     if (fwrite(&resource_id, sizeof(resource_id), 1, file) != 1) {
262       LOG(ERROR) << "Failed to write id for " << resource_id;
263       file_util::CloseFile(file);
264       return false;
265     }
266
267     if (fwrite(&data_offset, sizeof(data_offset), 1, file) != 1) {
268       LOG(ERROR) << "Failed to write offset for " << resource_id;
269       file_util::CloseFile(file);
270       return false;
271     }
272
273     data_offset += it->second.length();
274   }
275
276   // We place an extra entry after the last item that allows us to read the
277   // size of the last item.
278   uint16 resource_id = 0;
279   if (fwrite(&resource_id, sizeof(resource_id), 1, file) != 1) {
280     LOG(ERROR) << "Failed to write extra resource id.";
281     file_util::CloseFile(file);
282     return false;
283   }
284
285   if (fwrite(&data_offset, sizeof(data_offset), 1, file) != 1) {
286     LOG(ERROR) << "Failed to write extra offset.";
287     file_util::CloseFile(file);
288     return false;
289   }
290
291   for (std::map<uint16, base::StringPiece>::const_iterator it =
292            resources.begin();
293        it != resources.end(); ++it) {
294     if (fwrite(it->second.data(), it->second.length(), 1, file) != 1) {
295       LOG(ERROR) << "Failed to write data for " << it->first;
296       file_util::CloseFile(file);
297       return false;
298     }
299   }
300
301   file_util::CloseFile(file);
302
303   return true;
304 }
305
306 }  // namespace ui