summaryrefslogtreecommitdiffstats
path: root/webkit
diff options
context:
space:
mode:
authortommycli@chromium.org <tommycli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-11 01:58:25 +0000
committertommycli@chromium.org <tommycli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-11 01:58:25 +0000
commitf386bb5b7433903e3e105c7725906366f4a85559 (patch)
treeae63efdb0fb245791df7fa33e4a474a007685f78 /webkit
parent677ad365c17980f74f03c1992e6e1795723f9cfb (diff)
downloadchromium_src-f386bb5b7433903e3e105c7725906366f4a85559.zip
chromium_src-f386bb5b7433903e3e105c7725906366f4a85559.tar.gz
chromium_src-f386bb5b7433903e3e105c7725906366f4a85559.tar.bz2
Simple PMP reader to parse Picasa's metadata
For Media Galleries project. BUG=151701 Review URL: https://chromiumcodereview.appspot.com/12704024 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@193537 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
-rw-r--r--webkit/fileapi/media/picasa/pmp_column_reader.cc186
-rw-r--r--webkit/fileapi/media/picasa/pmp_column_reader.h67
-rw-r--r--webkit/fileapi/media/picasa/pmp_column_reader_unittest.cc179
-rw-r--r--webkit/fileapi/media/picasa/pmp_constants.h46
-rw-r--r--webkit/fileapi/media/picasa/pmp_table_reader.cc88
-rw-r--r--webkit/fileapi/media/picasa/pmp_table_reader.h48
-rw-r--r--webkit/fileapi/media/picasa/pmp_table_reader_unittest.cc72
-rw-r--r--webkit/fileapi/media/picasa/pmp_test_helper.cc172
-rw-r--r--webkit/fileapi/media/picasa/pmp_test_helper.h55
-rw-r--r--webkit/fileapi/webkit_fileapi.gypi5
10 files changed, 918 insertions, 0 deletions
diff --git a/webkit/fileapi/media/picasa/pmp_column_reader.cc b/webkit/fileapi/media/picasa/pmp_column_reader.cc
new file mode 100644
index 0000000..3f6bcc4
--- /dev/null
+++ b/webkit/fileapi/media/picasa/pmp_column_reader.cc
@@ -0,0 +1,186 @@
+// Copyright 2013 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 "webkit/fileapi/media/picasa/pmp_column_reader.h"
+
+#include <cstring>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace picasaimport {
+
+namespace {
+
+const size_t kPmpMaxFilesize = 50*1024*1024; // Maximum of 50 MB.
+
+} // namespace
+
+PmpColumnReader::PmpColumnReader()
+ : length_(0),
+ field_type_(PMP_TYPE_INVALID),
+ rows_(0) { }
+
+PmpColumnReader::~PmpColumnReader() { }
+
+bool PmpColumnReader::Init(const base::FilePath& filepath, uint32* rows_read) {
+ DCHECK(!data_.get());
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ int64 length = 0; // Signed temporary.
+ if (!file_util::GetFileSize(filepath, &length))
+ return false;
+
+ length_ = length;
+
+ if (length_ < kPmpHeaderSize || length_ > kPmpMaxFilesize)
+ return false;
+
+ data_.reset(new uint8[length_]);
+
+ char* data_begin = reinterpret_cast<char*>(data_.get());
+
+ return file_util::ReadFile(filepath, data_begin, length_) &&
+ ParseData(rows_read);
+}
+
+bool PmpColumnReader::ReadString(const uint32 row, std::string* result) const {
+ DCHECK(data_.get() != NULL);
+ DCHECK_GT(length_, kPmpHeaderSize + row);
+
+ if (field_type_ != PMP_TYPE_STRING || row >= rows_)
+ return false;
+
+ *result = strings_[row];
+ return true;
+}
+
+bool PmpColumnReader::ReadUInt32(const uint32 row, uint32* result) const {
+ DCHECK(data_.get() != NULL);
+ DCHECK_GT(length_, kPmpHeaderSize + row * sizeof(uint32));
+
+ if (field_type_ != PMP_TYPE_UINT32 || row >= rows_)
+ return false;
+
+ *result = reinterpret_cast<uint32*>(data_.get() + kPmpHeaderSize)[row];
+ return true;
+}
+
+bool PmpColumnReader::ReadDouble64(const uint32 row, double* result) const {
+ DCHECK(data_.get() != NULL);
+ DCHECK_GT(length_, kPmpHeaderSize + row * sizeof(double));
+
+ if (field_type_ != PMP_TYPE_DOUBLE64 || row >= rows_)
+ return false;
+
+ *result = reinterpret_cast<double*>(data_.get() + kPmpHeaderSize)[row];
+ return true;
+}
+
+bool PmpColumnReader::ReadUInt8(const uint32 row, uint8* result) const {
+ DCHECK(data_.get() != NULL);
+ DCHECK_GT(length_, kPmpHeaderSize + row * sizeof(uint8));
+
+ if (field_type_ != PMP_TYPE_UINT8 || row >= rows_)
+ return false;
+
+ *result = reinterpret_cast<uint8*>(data_.get() + kPmpHeaderSize)[row];
+ return true;
+}
+
+bool PmpColumnReader::ReadUInt64(const uint32 row, uint64* result) const {
+ DCHECK(data_.get() != NULL);
+ DCHECK_GT(length_, kPmpHeaderSize + row * sizeof(uint64));
+
+ if (field_type_ != PMP_TYPE_UINT64 || row >= rows_)
+ return false;
+
+ *result = reinterpret_cast<uint64*>(data_.get() + kPmpHeaderSize)[row];
+ return true;
+}
+
+bool PmpColumnReader::ParseData(uint32* rows_read) {
+ DCHECK(data_.get() != NULL);
+ DCHECK_GE(length_, kPmpHeaderSize);
+
+ // Check all magic bytes.
+ if (memcmp(&kPmpMagic1, &data_[kPmpMagic1Offset], sizeof(kPmpMagic1)) != 0 ||
+ memcmp(&kPmpMagic2, &data_[kPmpMagic2Offset], sizeof(kPmpMagic2)) != 0 ||
+ memcmp(&kPmpMagic3, &data_[kPmpMagic3Offset], sizeof(kPmpMagic3)) != 0 ||
+ memcmp(&kPmpMagic4, &data_[kPmpMagic4Offset], sizeof(kPmpMagic4)) != 0) {
+ return false;
+ }
+
+ uint16 field_type_data =
+ *(reinterpret_cast<uint16*>(&data_[kPmpFieldType1Offset]));
+
+ // Verify if field type matches second declaration
+ if (field_type_data !=
+ *(reinterpret_cast<uint16*>(&data_[kPmpFieldType2Offset]))) {
+ return false;
+ }
+
+ field_type_ = static_cast<PmpFieldType>(field_type_data);
+
+ rows_ = *(reinterpret_cast<uint32*>(&data_[kPmpRowCountOffset]));
+
+ size_t body_length = length_ - kPmpHeaderSize;
+ size_t expected_body_length = 0;
+ switch (field_type_) {
+ case PMP_TYPE_STRING:
+ expected_body_length = IndexStrings();
+ break;
+ case PMP_TYPE_UINT32:
+ expected_body_length = rows_ * sizeof(uint32);
+ break;
+ case PMP_TYPE_DOUBLE64:
+ expected_body_length = rows_ * sizeof(double);
+ break;
+ case PMP_TYPE_UINT8:
+ expected_body_length = rows_ * sizeof(uint8);
+ break;
+ case PMP_TYPE_UINT64:
+ expected_body_length = rows_ * sizeof(uint64);
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ if (body_length == expected_body_length && rows_read)
+ *rows_read = rows_;
+ return body_length == expected_body_length;
+}
+
+long PmpColumnReader::IndexStrings() {
+ DCHECK(data_.get() != NULL);
+ DCHECK_GE(length_, kPmpHeaderSize);
+
+ strings_.reserve(rows_);
+
+ size_t bytes_parsed = kPmpHeaderSize;
+ const uint8* data_cursor = data_.get() + kPmpHeaderSize;
+
+ while (strings_.size() < rows_) {
+ const uint8* string_end = static_cast<const uint8*>(
+ memchr(data_cursor, '\0', length_ - bytes_parsed));
+
+ // Fail if cannot find null termination. String runs on past file end.
+ if (string_end == NULL)
+ return -1;
+
+ // Length of string. (+1 to include the termination character).
+ ptrdiff_t length_in_bytes = string_end - data_cursor + 1;
+
+ strings_.push_back(reinterpret_cast<const char*>(data_cursor));
+ data_cursor += length_in_bytes;
+ bytes_parsed += length_in_bytes;
+ }
+
+ return bytes_parsed - kPmpHeaderSize;
+}
+
+} // namespace picasaimport
diff --git a/webkit/fileapi/media/picasa/pmp_column_reader.h b/webkit/fileapi/media/picasa/pmp_column_reader.h
new file mode 100644
index 0000000..1266574
--- /dev/null
+++ b/webkit/fileapi/media/picasa/pmp_column_reader.h
@@ -0,0 +1,67 @@
+// Copyright 2013 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.
+
+#ifndef WEBKIT_FILEAPI_MEDIA_PICASA_PMP_COLUMN_READER_H_
+#define WEBKIT_FILEAPI_MEDIA_PICASA_PMP_COLUMN_READER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "webkit/fileapi/media/picasa/pmp_constants.h"
+#include "webkit/storage/webkit_storage_export.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace picasaimport {
+
+// Reads a single PMP column from a file.
+class WEBKIT_STORAGE_EXPORT_PRIVATE PmpColumnReader {
+ public:
+ PmpColumnReader();
+ virtual ~PmpColumnReader();
+
+ // Returns true if read successfully.
+ // |rows_read| is undefined if returns false.
+ bool Init(const base::FilePath& filepath, uint32* rows_read);
+
+ // These functions read the value of that |row| into |result|.
+ // Functions return false if the column is of the wrong type or the row
+ // is out of range.
+ bool ReadString(const uint32 row, std::string* result) const;
+ bool ReadUInt32(const uint32 row, uint32* result) const;
+ bool ReadDouble64(const uint32 row, double* result) const;
+ bool ReadUInt8(const uint32 row, uint8* result) const;
+ bool ReadUInt64(const uint32 row, uint64* result) const;
+
+ // Returns the native encoding of field_type.
+ PmpFieldType field_type() const {
+ return field_type_;
+ }
+
+ private:
+ bool ParseData(uint32* rows_read);
+ // Returns the number of bytes parsed in the body, or, -1 on failure.
+ long IndexStrings();
+
+ // Source data
+ scoped_array<uint8> data_;
+ size_t length_;
+
+ // Header data
+ PmpFieldType field_type_;
+ uint32 rows_;
+
+ // Index of string start locations if fields are strings. Empty otherwise.
+ std::vector<const char*> strings_;
+
+ DISALLOW_COPY_AND_ASSIGN(PmpColumnReader);
+};
+
+} // namespace picasaimport
+
+#endif // WEBKIT_FILEAPI_MEDIA_PICASA_PMP_COLUMN_READER_H_
diff --git a/webkit/fileapi/media/picasa/pmp_column_reader_unittest.cc b/webkit/fileapi/media/picasa/pmp_column_reader_unittest.cc
new file mode 100644
index 0000000..556cc6b
--- /dev/null
+++ b/webkit/fileapi/media/picasa/pmp_column_reader_unittest.cc
@@ -0,0 +1,179 @@
+// Copyright 2013 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 <algorithm>
+#include <vector>
+
+#include "base/file_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/fileapi/media/picasa/pmp_column_reader.h"
+#include "webkit/fileapi/media/picasa/pmp_constants.h"
+#include "webkit/fileapi/media/picasa/pmp_test_helper.h"
+
+namespace {
+
+using picasaimport::PmpColumnReader;
+using picasaimport::PmpTestHelper;
+
+// Overridden version of Read method to make test code templatable.
+bool DoRead(const PmpColumnReader* reader, uint32 row, std::string* target) {
+ return reader->ReadString(row, target);
+}
+
+bool DoRead(const PmpColumnReader* reader, uint32 row, uint32* target) {
+ return reader->ReadUInt32(row, target);
+}
+
+bool DoRead(const PmpColumnReader* reader, uint32 row, double* target) {
+ return reader->ReadDouble64(row, target);
+}
+
+bool DoRead(const PmpColumnReader* reader, uint32 row, uint8* target) {
+ return reader->ReadUInt8(row, target);
+}
+
+bool DoRead(const PmpColumnReader* reader, uint32 row, uint64* target) {
+ return reader->ReadUInt64(row, target);
+}
+
+// TestValid
+template<class T>
+void TestValid(const picasaimport::PmpFieldType field_type,
+ const std::vector<T>& elems) {
+ PmpTestHelper test_helper;
+ ASSERT_TRUE(test_helper.Init());
+
+ PmpColumnReader reader;
+
+ uint32 rows_read = 0xFF;
+
+ std::vector<uint8> data =
+ PmpTestHelper::MakeHeaderAndBody(field_type, elems.size(), elems);
+ ASSERT_TRUE(test_helper.InitColumnReaderFromBytes(&reader, data, &rows_read));
+ EXPECT_EQ(elems.size(), rows_read);
+
+ for (uint32 i = 0; i < elems.size() && i < rows_read; i++) {
+ T target;
+ EXPECT_TRUE(DoRead(&reader, i, &target));
+ EXPECT_EQ(elems[i], target);
+ }
+}
+
+template<class T>
+void TestMalformed(const picasaimport::PmpFieldType field_type,
+ const std::vector<T>& elems) {
+ PmpTestHelper test_helper;
+ ASSERT_TRUE(test_helper.Init());
+
+ PmpColumnReader reader1, reader2, reader3, reader4;
+
+ std::vector<uint8> data_too_few_declared_rows =
+ PmpTestHelper::MakeHeaderAndBody(field_type, elems.size()-1, elems);
+ EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
+ &reader1, data_too_few_declared_rows, NULL));
+
+ std::vector<uint8> data_too_many_declared_rows =
+ PmpTestHelper::MakeHeaderAndBody(field_type, elems.size()+1, elems);
+ EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
+ &reader2, data_too_many_declared_rows, NULL));
+
+ std::vector<uint8> data_truncated =
+ PmpTestHelper::MakeHeaderAndBody(field_type, elems.size(), elems);
+ data_truncated.resize(data_truncated.size()-10);
+ EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
+ &reader3, data_truncated, NULL));
+
+ std::vector<uint8> data_padded =
+ PmpTestHelper::MakeHeaderAndBody(field_type, elems.size(), elems);
+ data_padded.resize(data_padded.size()+10);
+ EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
+ &reader4, data_padded, NULL));
+}
+
+template<class T>
+void TestPrimitive(const picasaimport::PmpFieldType field_type) {
+ // Make an ascending vector of the primitive.
+ uint32 n = 100;
+ std::vector<T> data(n, 0);
+ for (uint32 i = 0; i < n; i++) {
+ data[i] = i*3;
+ }
+
+ TestValid<T>(field_type, data);
+ TestMalformed<T>(field_type, data);
+}
+
+
+TEST(PmpColumnReaderTest, HeaderParsingAndValidation) {
+ PmpTestHelper test_helper;
+ ASSERT_TRUE(test_helper.Init());
+
+ PmpColumnReader reader1, reader2, reader3, reader4, reader5;
+
+ // Good header.
+ uint32 rows_read = 0xFF;
+ std::vector<uint8> good_header = PmpTestHelper::MakeHeader(
+ picasaimport::PMP_TYPE_STRING, 0);
+ ASSERT_TRUE(test_helper.InitColumnReaderFromBytes(
+ &reader1, good_header, &rows_read));
+ EXPECT_EQ(0U, rows_read) << "Read non-zero rows from header-only data.";
+
+ // Botch up elements of the header.
+ std::vector<uint8> bad_magic_byte = PmpTestHelper::MakeHeader(
+ picasaimport::PMP_TYPE_STRING, 0);
+ bad_magic_byte[0] = 0xff;
+ EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
+ &reader2, bad_magic_byte, NULL));
+
+ // Corrupt means the type fields don't agree.
+ std::vector<uint8> corrupt_type = PmpTestHelper::MakeHeader(
+ picasaimport::PMP_TYPE_STRING, 0);
+ corrupt_type[picasaimport::kPmpFieldType1Offset] = 0xff;
+ EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
+ &reader3, corrupt_type, NULL));
+
+ std::vector<uint8> invalid_type = PmpTestHelper::MakeHeader(
+ picasaimport::PMP_TYPE_STRING, 0);
+ invalid_type[picasaimport::kPmpFieldType1Offset] = 0xff;
+ invalid_type[picasaimport::kPmpFieldType2Offset] = 0xff;
+ EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
+ &reader4, invalid_type, NULL));
+
+ std::vector<uint8> incomplete_header = PmpTestHelper::MakeHeader(
+ picasaimport::PMP_TYPE_STRING, 0);
+ incomplete_header.resize(10);
+ EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
+ &reader5, incomplete_header, NULL));
+}
+
+TEST(PmpColumnReaderTest, StringParsing) {
+ std::vector<std::string> empty_strings(100, "");
+
+ // Test empty strings read okay.
+ TestValid(picasaimport::PMP_TYPE_STRING, empty_strings);
+
+ std::vector<std::string> mixed_strings;
+ mixed_strings.push_back("");
+ mixed_strings.push_back("Hello");
+ mixed_strings.push_back("World");
+ mixed_strings.push_back("");
+ mixed_strings.push_back("123123");
+ mixed_strings.push_back("Q");
+ mixed_strings.push_back("");
+
+ // Test that a mixed set of strings read correctly.
+ TestValid(picasaimport::PMP_TYPE_STRING, mixed_strings);
+
+ // Test with the data messed up in a variety of ways.
+ TestMalformed(picasaimport::PMP_TYPE_STRING, mixed_strings);
+}
+
+TEST(PmpColumnReaderTest, PrimitiveParsing) {
+ TestPrimitive<uint32>(picasaimport::PMP_TYPE_UINT32);
+ TestPrimitive<double>(picasaimport::PMP_TYPE_DOUBLE64);
+ TestPrimitive<uint8>(picasaimport::PMP_TYPE_UINT8);
+ TestPrimitive<uint64>(picasaimport::PMP_TYPE_UINT64);
+}
+
+} // namespace
diff --git a/webkit/fileapi/media/picasa/pmp_constants.h b/webkit/fileapi/media/picasa/pmp_constants.h
new file mode 100644
index 0000000..52013b3
--- /dev/null
+++ b/webkit/fileapi/media/picasa/pmp_constants.h
@@ -0,0 +1,46 @@
+// Copyright 2013 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.
+
+#ifndef WEBKIT_FILEAPI_MEDIA_PICASA_PMP_CONSTANTS_H_
+#define WEBKIT_FILEAPI_MEDIA_PICASA_PMP_CONSTANTS_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace picasaimport {
+
+// PMP file format.
+// Info derived from: http://sbktech.blogspot.com/2011/12/picasa-pmp-format.html
+
+const char* const kPmpExtension = "pmp";
+
+const size_t kPmpHeaderSize = 20;
+
+const int kPmpMagic1Offset = 0;
+const int kPmpMagic2Offset = 6;
+const int kPmpMagic3Offset = 8;
+const int kPmpMagic4Offset = 14;
+
+const uint32 kPmpMagic1 = 0x3fcccccd;
+const uint16 kPmpMagic2 = 0x1332;
+const uint32 kPmpMagic3 = 0x00000002;
+const uint16 kPmpMagic4 = 0x1332;
+
+const int kPmpFieldType1Offset = 4;
+const int kPmpFieldType2Offset = 12;
+const int kPmpRowCountOffset = 16;
+
+enum PmpFieldType {
+ PMP_TYPE_STRING = 0x00,
+ PMP_TYPE_UINT32 = 0x01,
+ PMP_TYPE_DOUBLE64 = 0x02,
+ PMP_TYPE_UINT8 = 0x03,
+ PMP_TYPE_UINT64 = 0x04,
+ PMP_TYPE_INVALID = 0xff
+};
+
+} // namespace picasaimport
+
+#endif // WEBKIT_FILEAPI_MEDIA_PICASA_PMP_CONSTANTS_H_
diff --git a/webkit/fileapi/media/picasa/pmp_table_reader.cc b/webkit/fileapi/media/picasa/pmp_table_reader.cc
new file mode 100644
index 0000000..c8f76d4
--- /dev/null
+++ b/webkit/fileapi/media/picasa/pmp_table_reader.cc
@@ -0,0 +1,88 @@
+// Copyright 2013 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 "webkit/fileapi/media/picasa/pmp_table_reader.h"
+
+#include <algorithm>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "webkit/fileapi/media/picasa/pmp_column_reader.h"
+#include "webkit/fileapi/media/picasa/pmp_constants.h"
+
+namespace picasaimport {
+
+namespace {
+
+COMPILE_ASSERT(sizeof(double) == 8, double_must_be_8_bytes_long);
+
+} // namespace
+
+PmpTableReader::PmpTableReader() : column_readers_(), max_row_count_(0) { }
+
+PmpTableReader::~PmpTableReader() { }
+
+bool PmpTableReader::Init(const std::string& table_name,
+ const base::FilePath& directory_path,
+ const std::vector<std::string>& columns) {
+ DCHECK(!columns.empty());
+
+ if (!column_readers_.empty())
+ return false;
+
+ if (!file_util::DirectoryExists(directory_path))
+ return false;
+
+ std::string table_prefix = table_name + "_";
+ std::string indicator_file_name = table_prefix + "0";
+
+ base::FilePath indicator_file = directory_path.Append(
+ base::FilePath::FromUTF8Unsafe(indicator_file_name));
+
+ // Look for the indicator_file file, indicating table existence.
+ if (!file_util::PathExists(indicator_file) ||
+ file_util::DirectoryExists(indicator_file)) {
+ return false;
+ }
+
+ ScopedVector<PmpColumnReader> column_readers;
+ uint32 max_row_count = 0;
+
+ for (std::vector<std::string>::const_iterator it = columns.begin();
+ it != columns.end(); ++it) {
+ std::string filename = table_prefix + *it + "." + kPmpExtension;
+
+ base::FilePath column_file_path = directory_path.Append(
+ base::FilePath::FromUTF8Unsafe(filename));
+
+ PmpColumnReader* column_reader = new PmpColumnReader();
+ column_readers.push_back(column_reader);
+
+ uint32 row_cnt;
+
+ if (!column_reader->Init(column_file_path, &row_cnt))
+ return false;
+
+ max_row_count = std::max(max_row_count, row_cnt);
+ }
+
+ column_readers_ = column_readers.Pass();
+ max_row_count_ = max_row_count;
+
+ return true;
+}
+
+uint32 PmpTableReader::RowCount() const {
+ return max_row_count_;
+}
+
+std::vector<const PmpColumnReader*> PmpTableReader::GetColumns() const {
+ std::vector<const PmpColumnReader*> readers(
+ column_readers_.begin(), column_readers_.end());
+ return readers;
+}
+
+} // namespace picasaimport
diff --git a/webkit/fileapi/media/picasa/pmp_table_reader.h b/webkit/fileapi/media/picasa/pmp_table_reader.h
new file mode 100644
index 0000000..de2627f
--- /dev/null
+++ b/webkit/fileapi/media/picasa/pmp_table_reader.h
@@ -0,0 +1,48 @@
+// Copyright 2013 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.
+
+#ifndef WEBKIT_FILEAPI_MEDIA_PICASA_PMP_TABLE_READER_H_
+#define WEBKIT_FILEAPI_MEDIA_PICASA_PMP_TABLE_READER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_vector.h"
+#include "webkit/storage/webkit_storage_export.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace picasaimport {
+
+class PmpColumnReader;
+
+class WEBKIT_STORAGE_EXPORT_PRIVATE PmpTableReader {
+ public:
+ PmpTableReader();
+
+ virtual ~PmpTableReader();
+
+ // |columns| parameter will define, in-order, the columns returned by
+ // subsequent columns to GetColumns() if Init() succeeds.
+ bool Init(const std::string& table_name,
+ const base::FilePath& directory_path,
+ const std::vector<std::string>& columns);
+
+ // Returns a const "view" of the current contents of |column_readers_|.
+ std::vector<const PmpColumnReader*> GetColumns() const;
+
+ uint32 RowCount() const;
+
+ private:
+ ScopedVector<PmpColumnReader> column_readers_;
+ uint32 max_row_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(PmpTableReader);
+};
+
+} // namespace picasaimport
+
+#endif // WEBKIT_FILEAPI_MEDIA_PICASA_PMP_TABLE_READER_H_
diff --git a/webkit/fileapi/media/picasa/pmp_table_reader_unittest.cc b/webkit/fileapi/media/picasa/pmp_table_reader_unittest.cc
new file mode 100644
index 0000000..66d3fa1
--- /dev/null
+++ b/webkit/fileapi/media/picasa/pmp_table_reader_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright 2013 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 <algorithm>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/fileapi/media/picasa/pmp_column_reader.h"
+#include "webkit/fileapi/media/picasa/pmp_constants.h"
+#include "webkit/fileapi/media/picasa/pmp_table_reader.h"
+#include "webkit/fileapi/media/picasa/pmp_test_helper.h"
+
+namespace {
+
+using picasaimport::PmpTestHelper;
+
+TEST(PmpTableReaderTest, RowCountAndFieldType) {
+ PmpTestHelper test_helper;
+ ASSERT_TRUE(test_helper.Init());
+
+ std::string table_name = "testtable";
+
+ std::vector<std::string> column_names;
+ column_names.push_back("strings");
+ column_names.push_back("uint32s");
+ column_names.push_back("doubles");
+
+ const std::vector<std::string> strings_vector(10, "Hello");
+ const std::vector<uint32> uint32s_vector(30, 42);
+ const std::vector<double> doubles_vector(20, 0.5);
+
+ picasaimport::PmpFieldType column_field_types[] = {
+ picasaimport::PMP_TYPE_STRING,
+ picasaimport::PMP_TYPE_UINT32,
+ picasaimport::PMP_TYPE_DOUBLE64
+ };
+
+ const uint32 max_rows = uint32s_vector.size();
+
+ base::FilePath indicator_path = test_helper.GetTempDirPath().Append(
+ base::FilePath::FromUTF8Unsafe(table_name + "_0"));
+
+ ASSERT_EQ(0, file_util::WriteFile(indicator_path, NULL, 0));
+ // Write three column files, one each for strings, uint32s, and doubles.
+
+ ASSERT_TRUE(test_helper.WriteColumnFileFromVector(
+ table_name, column_names[0], column_field_types[0], strings_vector));
+
+ ASSERT_TRUE(test_helper.WriteColumnFileFromVector(
+ table_name, column_names[1], column_field_types[1], uint32s_vector));
+
+ ASSERT_TRUE(test_helper.WriteColumnFileFromVector(
+ table_name, column_names[2], column_field_types[2], doubles_vector));
+
+ picasaimport::PmpTableReader table_reader;
+ ASSERT_TRUE(table_reader.Init(
+ table_name, test_helper.GetTempDirPath(), column_names));
+
+ EXPECT_EQ(max_rows, table_reader.RowCount());
+
+ const std::vector<const picasaimport::PmpColumnReader*> column_readers =
+ table_reader.GetColumns();
+
+ for (int i = 0; i < 3; i++) {
+ EXPECT_EQ(column_field_types[i], column_readers[i]->field_type());
+ }
+}
+
+} // namespace
diff --git a/webkit/fileapi/media/picasa/pmp_test_helper.cc b/webkit/fileapi/media/picasa/pmp_test_helper.cc
new file mode 100644
index 0000000..69f92ee
--- /dev/null
+++ b/webkit/fileapi/media/picasa/pmp_test_helper.cc
@@ -0,0 +1,172 @@
+// Copyright 2013 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 "webkit/fileapi/media/picasa/pmp_test_helper.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "webkit/fileapi/media/picasa/pmp_column_reader.h"
+
+namespace picasaimport {
+
+namespace {
+
+bool WriteToFile(const base::FilePath& path, std::vector<uint8> data) {
+ // Cast for usage in WriteFile function
+ const char* data_char = reinterpret_cast<const char*>(&data[0]);
+ size_t bytes_written = file_util::WriteFile(path, data_char, data.size());
+ return (bytes_written == data.size());
+}
+
+// Flatten a vector of elements into an array of bytes.
+template<class T>
+std::vector<uint8> Flatten(const std::vector<T>& elems) {
+ const uint8* elems0 = reinterpret_cast<const uint8*>(&elems[0]);
+ std::vector<uint8> data_body(elems0, elems0 + sizeof(T)*elems.size());
+
+ return data_body;
+}
+
+// Custom specialization for std::string.
+template<>
+std::vector<uint8> Flatten(const std::vector<std::string>& strings) {
+ std::vector<uint8> totalchars;
+
+ for(std::vector<std::string>::const_iterator it = strings.begin();
+ it != strings.end(); ++it) {
+ std::copy(it->begin(), it->end(), std::back_inserter(totalchars));
+ totalchars.push_back('\0'); // Add the null termination too.
+ }
+
+ return totalchars;
+}
+
+// Returns a new vector with the concatenated contents of |a| and |b|.
+std::vector<uint8> CombinedVectors(const std::vector<uint8>& a,
+ const std::vector<uint8>& b) {
+ std::vector<uint8> total;
+
+ std::copy(a.begin(), a.end(), std::back_inserter(total));
+ std::copy(b.begin(), b.end(), std::back_inserter(total));
+
+ return total;
+}
+
+} // namespace
+
+PmpTestHelper::PmpTestHelper() { }
+
+bool PmpTestHelper::Init() {
+ return temp_dir_.CreateUniqueTempDir();
+}
+
+base::FilePath PmpTestHelper::GetTempDirPath() {
+ DCHECK(temp_dir_.IsValid());
+ return temp_dir_.path();
+}
+
+template<class T>
+bool PmpTestHelper::WriteColumnFileFromVector(
+ const std::string& table_name, const std::string& column_name,
+ const PmpFieldType field_type, const std::vector<T>& elements_vector) {
+ DCHECK(temp_dir_.IsValid());
+
+ std::string file_name = table_name + "_" + column_name + "." + kPmpExtension;
+
+ base::FilePath path = temp_dir_.path().Append(
+ base::FilePath::FromUTF8Unsafe(file_name));
+
+ std::vector<uint8> data = PmpTestHelper::MakeHeaderAndBody(
+ field_type, elements_vector.size(), elements_vector);
+
+ return WriteToFile(path, data);
+}
+
+// Explicit Instantiation for all the valid types.
+template bool PmpTestHelper::WriteColumnFileFromVector<std::string>(
+ const std::string&, const std::string&, const PmpFieldType,
+ const std::vector<std::string>&);
+template bool PmpTestHelper::WriteColumnFileFromVector<uint32>(
+ const std::string&, const std::string&, const PmpFieldType,
+ const std::vector<uint32>&);
+template bool PmpTestHelper::WriteColumnFileFromVector<double>(
+ const std::string&, const std::string&, const PmpFieldType,
+ const std::vector<double>&);
+template bool PmpTestHelper::WriteColumnFileFromVector<uint8>(
+ const std::string&, const std::string&, const PmpFieldType,
+ const std::vector<uint8>&);
+template bool PmpTestHelper::WriteColumnFileFromVector<uint64>(
+ const std::string&, const std::string&, const PmpFieldType,
+ const std::vector<uint64>&);
+
+bool PmpTestHelper::InitColumnReaderFromBytes(
+ PmpColumnReader* const reader, const std::vector<uint8>& data,
+ uint32* rows_read) {
+ DCHECK(temp_dir_.IsValid());
+
+ base::FilePath temp_path;
+
+ if (!file_util::CreateTemporaryFileInDir(temp_dir_.path(), &temp_path) ||
+ !WriteToFile(temp_path, data)) {
+ return false;
+ }
+
+ bool success = reader->Init(temp_path, rows_read);
+
+ file_util::Delete(temp_path, true);
+
+ return success;
+
+}
+
+// Return a vector so we don't have to worry about memory management.
+std::vector<uint8> PmpTestHelper::MakeHeader(const PmpFieldType field_type,
+ const uint32 row_count) {
+ std::vector<uint8> header(picasaimport::kPmpHeaderSize);
+
+ // Copy in magic bytes.
+ memcpy(&header[picasaimport::kPmpMagic1Offset], &picasaimport::kPmpMagic1,
+ sizeof(picasaimport::kPmpMagic1));
+ memcpy(&header[picasaimport::kPmpMagic2Offset], &picasaimport::kPmpMagic2,
+ sizeof(picasaimport::kPmpMagic2));
+ memcpy(&header[picasaimport::kPmpMagic3Offset], &picasaimport::kPmpMagic3,
+ sizeof(picasaimport::kPmpMagic3));
+ memcpy(&header[picasaimport::kPmpMagic4Offset], &picasaimport::kPmpMagic4,
+ sizeof(picasaimport::kPmpMagic4));
+
+ // Copy in field type.
+ memcpy(&header[picasaimport::kPmpFieldType1Offset], &field_type, 2);
+ memcpy(&header[picasaimport::kPmpFieldType2Offset], &field_type, 2);
+
+ // Copy in row count.
+ memcpy(&header[picasaimport::kPmpRowCountOffset], &row_count, 4);
+
+ return header;
+}
+
+template<class T>
+std::vector<uint8> PmpTestHelper::MakeHeaderAndBody(
+ const PmpFieldType field_type, const uint32 row_count,
+ const std::vector<T>& elems) {
+ return CombinedVectors(PmpTestHelper::MakeHeader(field_type, row_count),
+ Flatten(elems));
+}
+
+// Explicit Instantiation for all the valid types.
+template std::vector<uint8> PmpTestHelper::MakeHeaderAndBody<std::string>(
+ const PmpFieldType, const uint32, const std::vector<std::string>&);
+template std::vector<uint8> PmpTestHelper::MakeHeaderAndBody<uint32>(
+ const PmpFieldType, const uint32, const std::vector<uint32>&);
+template std::vector<uint8> PmpTestHelper::MakeHeaderAndBody<double>(
+ const PmpFieldType, const uint32, const std::vector<double>&);
+template std::vector<uint8> PmpTestHelper::MakeHeaderAndBody<uint8>(
+ const PmpFieldType, const uint32, const std::vector<uint8>&);
+template std::vector<uint8> PmpTestHelper::MakeHeaderAndBody<uint64>(
+ const PmpFieldType, const uint32, const std::vector<uint64>&);
+
+} // namespace picasaimport
diff --git a/webkit/fileapi/media/picasa/pmp_test_helper.h b/webkit/fileapi/media/picasa/pmp_test_helper.h
new file mode 100644
index 0000000..2da2118
--- /dev/null
+++ b/webkit/fileapi/media/picasa/pmp_test_helper.h
@@ -0,0 +1,55 @@
+// Copyright 2013 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.
+
+#ifndef WEBKIT_FILEAPI_MEDIA_PICASA_PMP_TEST_HELPER_H_
+#define WEBKIT_FILEAPI_MEDIA_PICASA_PMP_TEST_HELPER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/scoped_temp_dir.h"
+#include "webkit/fileapi/media/picasa/pmp_constants.h"
+
+namespace base {
+class FilePath;
+} // namespace base
+
+namespace picasaimport {
+
+class PmpColumnReader;
+
+// A helper class used for unit tests only
+class PmpTestHelper {
+ public:
+ PmpTestHelper();
+
+ bool Init();
+
+ base::FilePath GetTempDirPath();
+
+ template<class T>
+ bool WriteColumnFileFromVector(const std::string& table_name,
+ const std::string& column_name,
+ const PmpFieldType field_type,
+ const std::vector<T>& elements_vector);
+
+ bool InitColumnReaderFromBytes(PmpColumnReader* const reader,
+ const std::vector<uint8>& data,
+ uint32* rows_read);
+
+ static std::vector<uint8> MakeHeader(const PmpFieldType field_type,
+ const uint32 row_count);
+
+ template<class T>
+ static std::vector<uint8> MakeHeaderAndBody(const PmpFieldType field_type,
+ const uint32 row_count,
+ const std::vector<T>& elems);
+
+ private:
+ base::ScopedTempDir temp_dir_;
+};
+
+} // namespace picasaimport
+
+#endif // WEBKIT_FILEAPI_MEDIA_PICASA_PMP_TEST_HELPER_H_
diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi
index d0a36a1..cbcffaf 100644
--- a/webkit/fileapi/webkit_fileapi.gypi
+++ b/webkit/fileapi/webkit_fileapi.gypi
@@ -74,6 +74,11 @@
'../fileapi/media/mtp_device_file_system_config.h',
'../fileapi/media/native_media_file_util.cc',
'../fileapi/media/native_media_file_util.h',
+ '../fileapi/media/picasa/pmp_column_reader.cc',
+ '../fileapi/media/picasa/pmp_column_reader.h',
+ '../fileapi/media/picasa/pmp_constants.h',
+ '../fileapi/media/picasa/pmp_table_reader.cc',
+ '../fileapi/media/picasa/pmp_table_reader.h',
'../fileapi/mount_points.cc',
'../fileapi/mount_points.h',
'../fileapi/native_file_util.cc',