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