summaryrefslogtreecommitdiffstats
path: root/base/data_pack.cc
blob: 051fb4e5184a864a933291af44708d8b5d3aebf2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/data_pack.h"

#include <errno.h>

#include "base/file_util.h"
#include "base/logging.h"
#include "base/string_piece.h"

// For details of the file layout, see
// http://dev.chromium.org/developers/design-documents/linuxresourcesandlocalizedstrings

namespace {
static const uint32_t kFileFormatVersion = 1;
// Length of file header: version and entry count.
static const size_t kHeaderLength = 2 * sizeof(uint32_t);

struct DataPackEntry {
  uint32_t resource_id;
  uint32_t file_offset;
  uint32_t length;

  static int CompareById(const void* void_key, const void* void_entry) {
    uint32_t key = *reinterpret_cast<const uint32_t*>(void_key);
    const DataPackEntry* entry =
        reinterpret_cast<const DataPackEntry*>(void_entry);
    if (key < entry->resource_id) {
      return -1;
    } else if (key > entry->resource_id) {
      return 1;
    } else {
      return 0;
    }
  }
}  __attribute((packed));

}  // anonymous namespace

namespace base {

// In .cc for MemoryMappedFile dtor.
DataPack::DataPack() : resource_count_(0) {
}
DataPack::~DataPack() {
}

bool DataPack::Load(const FilePath& path) {
  mmap_.reset(new file_util::MemoryMappedFile);
  if (!mmap_->Initialize(path)) {
    CHECK(false) << "Failed to mmap " << path.value() << " errno: " <<
        strerror(errno);
  }

  // Parse the header of the file.
  // First uint32_t: version; second: resource count.
  const uint32* ptr = reinterpret_cast<const uint32_t*>(mmap_->data());
  uint32 version = ptr[0];
  if (version != kFileFormatVersion) {
    LOG(ERROR) << "Bad data pack version: got " << version << ", expected "
               << kFileFormatVersion;
    mmap_.reset();
    return false;
  }
  resource_count_ = ptr[1];

  // Sanity check the file.
  // 1) Check we have enough entries.
  if (kHeaderLength + resource_count_ * sizeof(DataPackEntry) >
      mmap_->length()) {
    LOG(ERROR) << "Data pack file corruption: too short for number of "
                  "entries specified.";
    mmap_.reset();
    return false;
  }
  // 2) Verify the entries are within the appropriate bounds.
  for (size_t i = 0; i < resource_count_; ++i) {
    const DataPackEntry* entry = reinterpret_cast<const DataPackEntry*>(
        mmap_->data() + kHeaderLength + (i * sizeof(DataPackEntry)));
    if (entry->file_offset + entry->length > mmap_->length()) {
      LOG(ERROR) << "Entry #" << i << " in data pack points off end of file. "
                 << "Was the file corrupted?";
      mmap_.reset();
      return false;
    }
  }

  return true;
}

bool DataPack::Get(uint32_t resource_id, StringPiece* data) {
  // It won't be hard to make this endian-agnostic, but it's not worth
  // bothering to do right now.
#if defined(__BYTE_ORDER)
  // Linux check
  COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN,
                 datapack_assumes_little_endian);
#elif defined(__BIG_ENDIAN__)
  // Mac check
  #error DataPack assumes little endian
#endif

  DataPackEntry* target = reinterpret_cast<DataPackEntry*>(
      bsearch(&resource_id, mmap_->data() + kHeaderLength, resource_count_,
              sizeof(DataPackEntry), DataPackEntry::CompareById));
  if (!target) {
    LOG(ERROR) << "No resource found with id: " << resource_id;
    return false;
  }

  data->set(mmap_->data() + target->file_offset, target->length);
  return true;
}

}  // namespace base