From 4f1aab5e51d5c2db29cf53b925673cb64621ac6a Mon Sep 17 00:00:00 2001 From: Kongqun Yang Date: Wed, 13 Nov 2013 16:08:54 -0800 Subject: [PATCH] A simple BufferWriter implementation. The BufferWriter is capable of appending various data types to buffer. No optimization is done yet. We may consider optimize it for appending (adding data to the tail) later. Change-Id: I3b8e749e410c7ea4d4f2fb855e27744131c63cb0 --- media/base/buffer_writer.cc | 71 +++++++++++ media/base/buffer_writer.h | 68 ++++++++++ media/base/buffer_writer_unittest.cc | 177 +++++++++++++++++++++++++++ 3 files changed, 316 insertions(+) create mode 100644 media/base/buffer_writer.cc create mode 100644 media/base/buffer_writer.h create mode 100644 media/base/buffer_writer_unittest.cc 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