Home | History | Annotate | Download | only in base
      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 
     31   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.
     53 DataPack::DataPack() : resource_count_(0) {
     54 }
     55 DataPack::~DataPack() {
     56 }
     57 
     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 
    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 
    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
    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