diff --git a/media/base/buffer_writer.cc b/media/base/buffer_writer.cc new file mode 100644 index 0000000000..38a854f621 --- /dev/null +++ b/media/base/buffer_writer.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/buffer_writer.h" + +#include "base/sys_byteorder.h" +#include "media/file/file.h" + +namespace media { + +BufferWriter::BufferWriter() { + const size_t kDefaultReservedCapacity = 0x40000; // 256KB. + buf_.reserve(kDefaultReservedCapacity); +} +BufferWriter::BufferWriter(size_t reserved_size_in_bytes) { + buf_.reserve(reserved_size_in_bytes); +} +BufferWriter::~BufferWriter() {} + +void BufferWriter::AppendInt(uint8 v) { buf_.push_back(v); } +void BufferWriter::AppendInt(uint16 v) { AppendInternal(base::HostToNet16(v)); } +void BufferWriter::AppendInt(uint32 v) { AppendInternal(base::HostToNet32(v)); } +void BufferWriter::AppendInt(uint64 v) { AppendInternal(base::HostToNet64(v)); } +void BufferWriter::AppendInt(int16 v) { AppendInternal(base::HostToNet16(v)); } +void BufferWriter::AppendInt(int32 v) { AppendInternal(base::HostToNet32(v)); } +void BufferWriter::AppendInt(int64 v) { AppendInternal(base::HostToNet64(v)); } + +void BufferWriter::AppendNBytes(uint64 v, size_t num_bytes) { + DCHECK_GE(sizeof(v), num_bytes); + v = base::HostToNet64(v); + const uint8* data = reinterpret_cast(&v); + AppendArray(&data[sizeof(v) - num_bytes], num_bytes); +} + +void BufferWriter::AppendVector(const std::vector& v) { + buf_.insert(buf_.end(), v.begin(), v.end()); +} + +void BufferWriter::AppendArray(const uint8* buf, size_t size) { + buf_.insert(buf_.end(), buf, buf + size); +} + +void BufferWriter::AppendBuffer(const BufferWriter& buffer) { + buf_.insert(buf_.end(), buffer.buf_.begin(), buffer.buf_.end()); +} + +Status BufferWriter::WriteToFile(File* file) { + DCHECK(file); + + size_t remaining_size = buf_.size(); + const uint8* buf = &buf_[0]; + while (remaining_size > 0) { + int64 size_written = file->Write(buf, remaining_size); + if (size_written <= 0) { + return Status(error::FILE_FAILURE, + "Fail to write to file in BufferWriter"); + } + remaining_size -= size_written; + buf += size_written; + } + buf_.clear(); + return Status::OK; +} + +template +void BufferWriter::AppendInternal(T v) { + AppendArray(reinterpret_cast(&v), sizeof(T)); +} + +} // namespace media diff --git a/media/base/buffer_writer.h b/media/base/buffer_writer.h new file mode 100644 index 0000000000..d69aa6d3b6 --- /dev/null +++ b/media/base/buffer_writer.h @@ -0,0 +1,68 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A simple write buffer implementation, which appends various data types to +// buffer. + +#ifndef MEDIA_BASE_BUFFER_WRITER_H_ +#define MEDIA_BASE_BUFFER_WRITER_H_ + +#include + +#include "media/base/status.h" + +namespace media { + +class File; + +class BufferWriter { + public: + BufferWriter(); + // Construct the object with a reserved capacity of |reserved_size_in_bytes|. + // The value is intended for optimization and is not a hard limit. It does + // not affect the actual size of the buffer, which is still starting from 0. + explicit BufferWriter(size_t reserved_size_in_bytes); + ~BufferWriter(); + + // These convenient functions append the integers (in network byte order, + // i.e. big endian) of various size and signedness to the end of the buffer. + void AppendInt(uint8 v); + void AppendInt(uint16 v); + void AppendInt(uint32 v); + void AppendInt(uint64 v); + void AppendInt(int16 v); + void AppendInt(int32 v); + void AppendInt(int64 v); + + // Append the least significant |num_bytes| of |v| to buffer. |num_bytes| + // should not be larger than sizeof(v), i.e. 8. + void AppendNBytes(uint64 v, size_t num_bytes); + + void AppendVector(const std::vector& v); + void AppendArray(const uint8* buf, size_t size); + void AppendBuffer(const BufferWriter& buffer); + + void Swap(BufferWriter* buffer) { buf_.swap(buffer->buf_); } + void Clear() { buf_.clear(); } + size_t Size() const { return buf_.size(); } + // Return underlying buffer. Behavior is undefined if the buffer size is 0. + const uint8* Buffer() const { return &buf_[0]; } + + // Write the buffer to file. |file| should not be NULL. + // Returns OK on success. The internal buffer will be cleared after writing. + Status WriteToFile(File* file); + + private: + // Internal implementation of multi-byte write. + template + void AppendInternal(T v); + + std::vector buf_; + + DISALLOW_COPY_AND_ASSIGN(BufferWriter); +}; + +} // namespace media + +#endif // MEDIA_BASE_BUFFER_WRITER_H_ diff --git a/media/base/buffer_writer_unittest.cc b/media/base/buffer_writer_unittest.cc new file mode 100644 index 0000000000..cf2b54e14e --- /dev/null +++ b/media/base/buffer_writer_unittest.cc @@ -0,0 +1,177 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/buffer_writer.h" + +#include "base/file_util.h" +#include "base/memory/scoped_ptr.h" +#include "media/base/buffer_reader.h" +#include "media/base/status_test_util.h" +#include "media/file/file.h" + +namespace { +const int kReservedBufferCapacity = 1000; +// Min values for various integers of different size. Min values for signed +// integers are already defined in //base/basictypes.h. +const uint8 kuint8min = 0; +const uint16 kuint16min = 0; +const uint32 kuint32min = 0; +const uint64 kuint64min = 0; +// Max values for various integers are already defined in //base/basictypes.h. +// Other integer values. +const uint8 kuint8 = 10; +const uint16 kuint16 = 1000; +const int16 kint16 = -1000; +const uint32 kuint32 = 1000000; +const int32 kint32 = -1000000; +const uint64 kuint64 = 10000000000; +const int64 kint64 = -10000000000; +const uint8 kuint8Array[] = {10, 1, 100, 5, 3, 60}; +} // namespace + +namespace media { + +class BufferWriterTest : public testing::Test { + public: + BufferWriterTest() + : writer_(new BufferWriter(kReservedBufferCapacity)), + reader_(new BufferReader(writer_->Buffer(), kReservedBufferCapacity)) {} + + bool ReadInt(uint8* v) { return reader_->Read1(v); } + bool ReadInt(uint16* v) { return reader_->Read2(v); } + bool ReadInt(int16* v) { return reader_->Read2s(v); } + bool ReadInt(uint32* v) { return reader_->Read4(v); } + bool ReadInt(int32* v) { return reader_->Read4s(v); } + bool ReadInt(uint64* v) { return reader_->Read8(v); } + bool ReadInt(int64* v) { return reader_->Read8s(v); } + + template + void ReadAndExpect(T expectation) { + T data_read; + ASSERT_TRUE(ReadInt(&data_read)); + ASSERT_EQ(expectation, data_read); + } + + template + void Verify(T min, T max, T val) { + writer_->AppendInt(min); + writer_->AppendInt(max); + writer_->AppendInt(val); + ASSERT_EQ(sizeof(min) + sizeof(max) + sizeof(val), writer_->Size()); + ReadAndExpect(min); + ReadAndExpect(max); + ReadAndExpect(val); + } + + protected: + scoped_ptr writer_; + scoped_ptr reader_; + + private: + DISALLOW_COPY_AND_ASSIGN(BufferWriterTest); +}; + +TEST_F(BufferWriterTest, Append1) { Verify(kuint8min, kuint8max, kuint8); } +TEST_F(BufferWriterTest, Append2) { Verify(kuint16min, kuint16max, kuint16); } +TEST_F(BufferWriterTest, Append2s) { Verify(kint16min, kint16max, kint16); } +TEST_F(BufferWriterTest, Append4) { Verify(kuint32min, kuint32max, kuint32); } +TEST_F(BufferWriterTest, Append4s) { Verify(kint32min, kint32max, kint32); } +TEST_F(BufferWriterTest, Append8) { Verify(kuint64min, kuint64max, kuint64); } +TEST_F(BufferWriterTest, Append8s) { Verify(kint64min, kint64max, kint64); } + +TEST_F(BufferWriterTest, AppendNBytes) { + // Write the least significant four bytes and verify the result. + writer_->AppendNBytes(kuint64, sizeof(uint32)); + ASSERT_EQ(sizeof(uint32), writer_->Size()); + ReadAndExpect(static_cast(kuint64 & 0xFFFFFFFF)); +} + +TEST_F(BufferWriterTest, AppendEmptyVector) { + std::vector v; + writer_->AppendVector(v); + ASSERT_EQ(0, writer_->Size()); +} + +TEST_F(BufferWriterTest, AppendVector) { + std::vector v(kuint8Array, kuint8Array + sizeof(kuint8Array)); + writer_->AppendVector(v); + ASSERT_EQ(sizeof(kuint8Array), writer_->Size()); + + std::vector data_read; + ASSERT_TRUE(reader_->ReadToVector(&data_read, sizeof(kuint8Array))); + ASSERT_EQ(v, data_read); +} + +TEST_F(BufferWriterTest, AppendArray) { + writer_->AppendArray(kuint8Array, sizeof(kuint8Array)); + ASSERT_EQ(sizeof(kuint8Array), writer_->Size()); + + std::vector data_read; + ASSERT_TRUE(reader_->ReadToVector(&data_read, sizeof(kuint8Array))); + for (size_t i = 0; i < sizeof(kuint8Array); ++i) + EXPECT_EQ(kuint8Array[i], data_read[i]); +} + +TEST_F(BufferWriterTest, AppendBufferWriter) { + BufferWriter local_writer; + local_writer.AppendInt(kuint16); + local_writer.AppendInt(kint64); + local_writer.AppendInt(kuint32); + writer_->AppendBuffer(local_writer); + ASSERT_EQ(sizeof(kuint16) + sizeof(kint64) + sizeof(kuint32), + writer_->Size()); + + ASSERT_NO_FATAL_FAILURE(ReadAndExpect(kuint16)); + ASSERT_NO_FATAL_FAILURE(ReadAndExpect(kint64)); + ASSERT_NO_FATAL_FAILURE(ReadAndExpect(kuint32)); +} + +TEST_F(BufferWriterTest, Swap) { + BufferWriter local_writer; + local_writer.AppendInt(kuint16); + local_writer.AppendInt(kint64); + writer_->AppendInt(kuint32); + writer_->Swap(&local_writer); + + ASSERT_EQ(sizeof(kuint16) + sizeof(kint64), writer_->Size()); + ASSERT_EQ(sizeof(kuint32), local_writer.Size()); + + reader_.reset(new BufferReader(writer_->Buffer(), writer_->Size())); + ASSERT_NO_FATAL_FAILURE(ReadAndExpect(kuint16)); + ASSERT_NO_FATAL_FAILURE(ReadAndExpect(kint64)); +} + +TEST_F(BufferWriterTest, Clear) { + writer_->AppendInt(kuint32); + ASSERT_EQ(sizeof(kuint32), writer_->Size()); + writer_->Clear(); + ASSERT_EQ(0, writer_->Size()); +} + +TEST_F(BufferWriterTest, WriteToFile) { + base::FilePath path; + ASSERT_TRUE(file_util::CreateTemporaryFile(&path)); + LOG(INFO) << "Created temporary file: " << path.value(); + + // Append an array to buffer and then write to the temporary file. + File* const output_file = File::Open(path.value().c_str(), "w"); + writer_->AppendArray(kuint8Array, sizeof(kuint8Array)); + ASSERT_EQ(sizeof(kuint8Array), writer_->Size()); + ASSERT_OK(writer_->WriteToFile(output_file)); + ASSERT_EQ(0, writer_->Size()); + ASSERT_TRUE(output_file->Close()); + + // Read the file and verify. + File* const input_file = File::Open(path.value().c_str(), "r"); + ASSERT_TRUE(input_file != NULL); + std::vector data_read(sizeof(kuint8Array), 0); + EXPECT_EQ(sizeof(kuint8Array), + input_file->Read(&data_read[0], data_read.size())); + ASSERT_TRUE(input_file->Close()); + + for (size_t i = 0; i < sizeof(kuint8Array); ++i) + EXPECT_EQ(kuint8Array[i], data_read[i]); +} + +} // namespace media