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
This commit is contained in:
Kongqun Yang 2013-11-13 16:08:54 -08:00
parent 1fade8aa36
commit 4f1aab5e51
3 changed files with 316 additions and 0 deletions

View File

@ -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<uint8*>(&v);
AppendArray(&data[sizeof(v) - num_bytes], num_bytes);
}
void BufferWriter::AppendVector(const std::vector<uint8>& 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 <typename T>
void BufferWriter::AppendInternal(T v) {
AppendArray(reinterpret_cast<uint8*>(&v), sizeof(T));
}
} // namespace media

View File

@ -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 <vector>
#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<uint8>& 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 <typename T>
void AppendInternal(T v);
std::vector<uint8> buf_;
DISALLOW_COPY_AND_ASSIGN(BufferWriter);
};
} // namespace media
#endif // MEDIA_BASE_BUFFER_WRITER_H_

View File

@ -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 <typename T>
void ReadAndExpect(T expectation) {
T data_read;
ASSERT_TRUE(ReadInt(&data_read));
ASSERT_EQ(expectation, data_read);
}
template <typename T>
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<BufferWriter> writer_;
scoped_ptr<BufferReader> 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<uint32>(kuint64 & 0xFFFFFFFF));
}
TEST_F(BufferWriterTest, AppendEmptyVector) {
std::vector<uint8> v;
writer_->AppendVector(v);
ASSERT_EQ(0, writer_->Size());
}
TEST_F(BufferWriterTest, AppendVector) {
std::vector<uint8> v(kuint8Array, kuint8Array + sizeof(kuint8Array));
writer_->AppendVector(v);
ASSERT_EQ(sizeof(kuint8Array), writer_->Size());
std::vector<uint8> 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<uint8> 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<uint8> 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