diff options
-rw-r--r-- | base/base_lib.scons | 1 | ||||
-rw-r--r-- | base/base_unittests.scons | 5 | ||||
-rw-r--r-- | base/data/data_pack_unittest/sample.pak | bin | 0 -> 80 bytes | |||
-rw-r--r-- | base/data_pack.cc | 109 | ||||
-rw-r--r-- | base/data_pack.h | 46 | ||||
-rw-r--r-- | base/data_pack_unittest.cc | 42 |
6 files changed, 203 insertions, 0 deletions
diff --git a/base/base_lib.scons b/base/base_lib.scons index 976a899..0dd3673 100644 --- a/base/base_lib.scons +++ b/base/base_lib.scons @@ -361,6 +361,7 @@ if env.Bit('linux'): 'atomicops_internals_x86_gcc.cc', 'base_paths_linux.cc', 'clipboard_linux.cc', + 'data_pack.cc', 'file_util_linux.cc', 'file_version_info_linux.cc', 'hmac_nss.cc', diff --git a/base/base_unittests.scons b/base/base_unittests.scons index 66be1f3..4b0c2e8 100644 --- a/base/base_unittests.scons +++ b/base/base_unittests.scons @@ -131,6 +131,11 @@ if env.Bit('posix'): 'gfx/native_theme_unittest.cc', ) +if env.Bit('linux'): + input_files.Append( + 'data_pack_unittest.cc', + ) + if env.Bit('mac'): # Remove files that still need to be ported from the input_files list. # TODO(port): delete files from this list as they get ported. diff --git a/base/data/data_pack_unittest/sample.pak b/base/data/data_pack_unittest/sample.pak Binary files differnew file mode 100644 index 0000000..fdbe2b5 --- /dev/null +++ b/base/data/data_pack_unittest/sample.pak diff --git a/base/data_pack.cc b/base/data_pack.cc new file mode 100644 index 0000000..17db8c4 --- /dev/null +++ b/base/data_pack.cc @@ -0,0 +1,109 @@ +// 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 "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)) { + mmap_.reset(); + return false; + } + + // 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. + COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, + datapack_assumes_little_endian); + + 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 diff --git a/base/data_pack.h b/base/data_pack.h new file mode 100644 index 0000000..1836552 --- /dev/null +++ b/base/data_pack.h @@ -0,0 +1,46 @@ +// 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. + +// DataPack represents a read-only view onto an on-disk file that contains +// (key, value) pairs of data. It's used to store static resources like +// translation strings and images. + +#ifndef BASE_DATA_PACK_H_ +#define BASE_DATA_PACK_H_ + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" + +namespace file_util { + class MemoryMappedFile; +} +class FilePath; +class StringPiece; + +namespace base { + +class DataPack { + public: + DataPack(); + ~DataPack(); + + // Load a pack file from |path|, returning false on error. + bool Load(const FilePath& path); + + // Get resource by id |resource_id|, filling in |data|. + // The data is owned by the DataPack object and should not be modified. + // Returns false if the resource id isn't found. + bool Get(uint32_t resource_id, StringPiece* data); + + private: + // The memory-mapped data. + scoped_ptr<file_util::MemoryMappedFile> mmap_; + + // Number of resources in the data. + size_t resource_count_; +}; + +} // namespace base + +#endif // BASE_DATA_PACK_H_ diff --git a/base/data_pack_unittest.cc b/base/data_pack_unittest.cc new file mode 100644 index 0000000..1f6b264 --- /dev/null +++ b/base/data_pack_unittest.cc @@ -0,0 +1,42 @@ +// 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 "base/file_path.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/string_piece.h" +#include "testing/gtest/include/gtest/gtest.h" + +class DataPackTest : public testing::Test { + public: + DataPackTest() { + PathService::Get(base::DIR_SOURCE_ROOT, &data_path_); + data_path_ = data_path_.Append( + FILE_PATH_LITERAL("base/data/data_pack_unittest/sample.pak")); + } + + FilePath data_path_; +}; + +TEST_F(DataPackTest, Load) { + base::DataPack pack; + ASSERT_TRUE(pack.Load(data_path_)); + + StringPiece data; + ASSERT_TRUE(pack.Get(4, &data)); + EXPECT_EQ("this is id 4", data); + ASSERT_TRUE(pack.Get(6, &data)); + EXPECT_EQ("this is id 6", data); + + // Try reading zero-length data blobs, just in case. + ASSERT_TRUE(pack.Get(1, &data)); + EXPECT_EQ(0U, data.length()); + ASSERT_TRUE(pack.Get(10, &data)); + EXPECT_EQ(0U, data.length()); + + // Try looking up an invalid key. + ASSERT_FALSE(pack.Get(140, &data)); +} |