Chromium Code Reviews| Index: webkit/fileapi/media/picasa/pmp_column_reader.cc |
| 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 0000000000000000000000000000000000000000..098a16df0a18dc3905be3769cecab73df63374ee |
| --- /dev/null |
| +++ b/webkit/fileapi/media/picasa/pmp_column_reader.cc |
| @@ -0,0 +1,189 @@ |
| +// 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" |
| +#include "webkit/fileapi/media/picasa/pmp_constants.h" |
| + |
| +namespace fileapi { |
| + |
| +namespace { |
| + |
| +const size_t kPmpMaxFilesize = 50*1024*1024; // Maximum of 50 MB. |
| + |
| +} // namespace |
| + |
| +PmpColumnReader::PmpColumnReader() |
| + : data_(NULL), |
| + length_(0), |
| + field_type_(kPmpFieldTypeInvalid), |
| + rows_(0), |
| + strings_() { } |
| + |
| +PmpColumnReader::~PmpColumnReader() { } |
| + |
| +bool PmpColumnReader::Init(const base::FilePath& filepath, uint32* rows_read) { |
| + DCHECK(!data_.get()); |
| + base::ThreadRestrictions::AssertIOAllowed(); |
| + |
| + long int 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_) && |
|
vandebo (ex-Chrome)
2013/04/02 18:10:55
nit: extra ()s
tommycli
2013/04/02 19:05:55
Done.
|
| + 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_ != kPmpFieldTypeString || 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_ != kPmpFieldTypeUInt32 || 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_ != kPmpFieldTypeDouble64 || 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_ != kPmpFieldTypeUInt8 || 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_ != kPmpFieldTypeUInt64 || 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; |
| + } |
| + |
| + field_type_ = *(reinterpret_cast<uint16*>(&data_[kPmpFieldType1Offset])); |
| + |
| + // Verify if field type matches second declaration |
| + if (field_type_ != *(reinterpret_cast<uint16*>(&data_[kPmpFieldType2Offset]))) |
| + return false; |
| + |
| + rows_ = *(reinterpret_cast<uint32*>(&data_[kPmpRowCountOffset])); |
| + |
| + bool parse_success = false; |
|
vandebo (ex-Chrome)
2013/04/02 18:10:55
If you have IndexStrings return bytes_parsed, you
tommycli
2013/04/02 19:05:55
Done.
|
| + size_t body_length = length_ - kPmpHeaderSize; |
| + size_t expected_body_length = 0; |
| + switch (field_type_) { |
| + case kPmpFieldTypeString: |
| + parse_success = IndexStrings(); |
| + break; |
| + case kPmpFieldTypeUInt32: |
| + expected_body_length = rows_ * sizeof(uint32); |
| + parse_success = body_length == expected_body_length; |
| + break; |
| + case kPmpFieldTypeDouble64: |
| + expected_body_length = rows_ * sizeof(double); |
| + parse_success = body_length == expected_body_length; |
| + break; |
| + case kPmpFieldTypeUInt8: |
| + expected_body_length = rows_ * sizeof(uint8); |
| + parse_success = body_length == expected_body_length; |
| + break; |
| + case kPmpFieldTypeUInt64: |
| + expected_body_length = rows_ * sizeof(uint64); |
| + parse_success = body_length == expected_body_length; |
| + break; |
| + default: |
| + return false; |
| + break; |
| + } |
| + |
| + if (parse_success && rows_read) |
| + *rows_read = rows_; |
| + return parse_success; |
| +} |
| + |
| +bool PmpColumnReader::IndexStrings() { |
| + DCHECK(data_.get() != NULL); |
| + DCHECK_GE(length_, kPmpHeaderSize); |
| + |
| + strings_.reserve(rows_); |
| + |
| + size_t bytes_parsed = kPmpHeaderSize; |
| + const uint8* body_cursor = data_.get() + kPmpHeaderSize; |
|
vandebo (ex-Chrome)
2013/04/02 18:10:55
nit: body? data_cursor or just cursor.
tommycli
2013/04/02 19:05:55
Done.
|
| + |
| + while (strings_.size() < rows_) { |
| + const uint8* string_end = static_cast<const uint8*>( |
| + memchr(body_cursor, '\0', length_ - bytes_parsed)); |
| + |
| + // Fail if cannot find null termination. String runs on past file end. |
| + if (string_end == NULL) |
| + return false; |
| + |
| + // Length of string. (+1 to include the termination character). |
| + ptrdiff_t length_in_bytes = string_end - body_cursor + 1; |
| + |
| + strings_.push_back(reinterpret_cast<const char*>(body_cursor)); |
| + body_cursor += length_in_bytes; |
| + bytes_parsed += length_in_bytes; |
| + } |
| + |
| + return bytes_parsed == length_; |
| +} |
| + |
| +} // namespace |