• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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