diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-16 01:27:46 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-16 01:27:46 +0000 |
commit | b3c03409a5538a9aa4577b10f340e14fd71d99dc (patch) | |
tree | 679974a34a0d1414ea8699f555c52b9fc6ad408f /remoting/base | |
parent | aa32bccb292a8e9e153bd44544558e36f440f21e (diff) | |
download | chromium_src-b3c03409a5538a9aa4577b10f340e14fd71d99dc.zip chromium_src-b3c03409a5538a9aa4577b10f340e14fd71d99dc.tar.gz chromium_src-b3c03409a5538a9aa4577b10f340e14fd71d99dc.tar.bz2 |
Added CompoundBuffer that will be used to store data in the encoding/decoding
pipeline.
BUG=None
TEST=Unittests
Review URL: http://codereview.chromium.org/4779001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@66209 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/base')
-rw-r--r-- | remoting/base/compound_buffer.cc | 232 | ||||
-rw-r--r-- | remoting/base/compound_buffer.h | 120 | ||||
-rw-r--r-- | remoting/base/compound_buffer_unittest.cc | 249 | ||||
-rw-r--r-- | remoting/base/multiple_array_input_stream.cc | 87 | ||||
-rw-r--r-- | remoting/base/multiple_array_input_stream.h | 57 | ||||
-rw-r--r-- | remoting/base/multiple_array_input_stream_unittest.cc | 98 |
6 files changed, 601 insertions, 242 deletions
diff --git a/remoting/base/compound_buffer.cc b/remoting/base/compound_buffer.cc new file mode 100644 index 0000000..9e8b8c3 --- /dev/null +++ b/remoting/base/compound_buffer.cc @@ -0,0 +1,232 @@ +// Copyright (c) 2010 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 <functional> + +#include "base/logging.h" +#include "net/base/io_buffer.h" +#include "remoting/base/compound_buffer.h" + +namespace remoting { + +CompoundBuffer::DataChunk::DataChunk( + net::IOBuffer* buffer_value, const char* start_value, int size_value) + : buffer(buffer_value), + start(start_value), + size(size_value) { +} + +CompoundBuffer::CompoundBuffer() + : total_bytes_(0), + locked_(false) { +} + +CompoundBuffer::~CompoundBuffer() { +} + +void CompoundBuffer::Clear() { + CHECK(!locked_); + chunks_.clear(); + total_bytes_ = 0; +} + +void CompoundBuffer::Append(net::IOBuffer* buffer, + const char* start, int size) { + // A weak check that the |start| is within |buffer|. + DCHECK_GE(start, buffer->data()); + DCHECK_GT(size, 0); + + CHECK(!locked_); + + chunks_.push_back(DataChunk(buffer, start, size)); + total_bytes_ += size; +} + +void CompoundBuffer::Append(net::IOBuffer* buffer, int size) { + Append(buffer, buffer->data(), size); +} + +void CompoundBuffer::Append(const CompoundBuffer& buffer) { + for (DataChunkList::const_iterator it = buffer.chunks_.begin(); + it != buffer.chunks_.end(); ++it) { + Append(it->buffer, it->start, it->size); + } +} + +void CompoundBuffer::Prepend(net::IOBuffer* buffer, + const char* start, int size) { + // A weak check that the |start| is within |buffer|. + DCHECK_GE(start, buffer->data()); + DCHECK_GT(size, 0); + + CHECK(!locked_); + + chunks_.push_front(DataChunk(buffer, start, size)); + total_bytes_ += size; +} + +void CompoundBuffer::Prepend(net::IOBuffer* buffer, int size) { + Prepend(buffer, buffer->data(), size); +} + +void CompoundBuffer::Prepend(const CompoundBuffer& buffer) { + for (DataChunkList::const_iterator it = buffer.chunks_.begin(); + it != buffer.chunks_.end(); ++it) { + Prepend(it->buffer, it->start, it->size); + } +} +void CompoundBuffer::AppendCopyOf(const char* data, int size) { + net::IOBuffer* buffer = new net::IOBuffer(size); + memcpy(buffer->data(), data, size); + Append(buffer, size); +} + +void CompoundBuffer::PrependCopyOf(const char* data, int size) { + net::IOBuffer* buffer = new net::IOBuffer(size); + memcpy(buffer->data(), data, size); + Prepend(buffer, size); +} + +void CompoundBuffer::Lock() { + locked_ = true; +} + +net::IOBufferWithSize* CompoundBuffer::ToIOBufferWithSize() const { + net::IOBufferWithSize* result = new net::IOBufferWithSize(total_bytes_); + CopyTo(result->data(), total_bytes_); + return result; +} + +void CompoundBuffer::CopyTo(char* data, int size) const { + char* pos = data; + for (DataChunkList::const_iterator it = chunks_.begin(); + it != chunks_.end(); ++it) { + CHECK_LE(pos + it->size, data + size); + memcpy(pos, it->start, it->size); + pos += it->size; + } +} + +void CompoundBuffer::CopyFrom(const CompoundBuffer& source, + int start, int end) { + // Check that 0 <= |start| <= |end| <= |total_bytes_|. + DCHECK_LE(0, start); + DCHECK_LE(start, end); + DCHECK_LE(end, source.total_bytes()); + + Clear(); + + if (end == start) { + return; + } + + // Iterate over chunks in the |source| and add those that we need. + int pos = 0; + for (DataChunkList::const_iterator it = source.chunks_.begin(); + it != source.chunks_.end(); ++it) { + + // Add data from the current chunk only if it is in the specified interval. + if (pos + it->size > start && pos < end) { + int relative_start = std::max(0, start - pos); + int relative_end = std::min(it->size, end - pos); + DCHECK_LE(0, relative_start); + DCHECK_LT(relative_start, relative_end); + DCHECK_LE(relative_end, it->size); + Append(it->buffer.get(), it->start + relative_start, + relative_end - relative_start); + } + + pos += it->size; + if (pos >= end) { + // We've got all the data we need. + break; + } + } + + DCHECK_EQ(total_bytes_, end - start); +} + +CompoundBufferInputStream::CompoundBufferInputStream( + const CompoundBuffer* buffer) + : buffer_(buffer), + current_chunk_(0), + current_chunk_position_(0), + position_(0), + last_returned_size_(0) { + DCHECK(buffer_->locked()); +} + +CompoundBufferInputStream::~CompoundBufferInputStream() { +} + +bool CompoundBufferInputStream::Next(const void** data, int* size) { + if (current_chunk_ < buffer_->chunks_.size()) { + // Reply with the number of bytes remaining in the current buffer. + const CompoundBuffer::DataChunk& chunk = buffer_->chunks_[current_chunk_]; + int read_size = chunk.size - current_chunk_position_; + *data = chunk.start + current_chunk_position_; + *size = read_size; + + // Adjust position. + ++current_chunk_; + current_chunk_position_ = 0; + position_ += read_size; + + last_returned_size_ = read_size; + return true; + } + + DCHECK_EQ(position_, buffer_->total_bytes()); + + // We've reached the end of the stream. So reset |last_returned_size_| + // to zero to prevent any backup request. + // This is the same as in ArrayInputStream. + // See google/protobuf/io/zero_copy_stream_impl_lite.cc. + last_returned_size_ = 0; + return false; +} + +void CompoundBufferInputStream::BackUp(int count) { + DCHECK_LE(count, last_returned_size_); + DCHECK_GT(current_chunk_, 0u); + + // Rewind one buffer and rewind data offset by |count| bytes. + --current_chunk_; + const CompoundBuffer::DataChunk& chunk = buffer_->chunks_[current_chunk_]; + current_chunk_position_ = chunk.size - count; + position_ -= count; + DCHECK_GE(position_, 0); + + // Prevent additional backups. + last_returned_size_ = 0; +} + +bool CompoundBufferInputStream::Skip(int count) { + DCHECK_GE(count, 0); + last_returned_size_ = 0; + + while (count > 0 && current_chunk_ < buffer_->chunks_.size()) { + const CompoundBuffer::DataChunk& chunk = buffer_->chunks_[current_chunk_]; + int read = std::min(count, chunk.size - current_chunk_position_); + + // Advance the current buffer offset and position. + current_chunk_position_ += read; + position_ += read; + count -= read; + + // If the current buffer is fully read, then advance to the next buffer. + if (current_chunk_position_ == chunk.size) { + ++current_chunk_; + current_chunk_position_ = 0; + } + } + + return count == 0; +} + +int64 CompoundBufferInputStream::ByteCount() const { + return position_; +} + +} // namespace remoting diff --git a/remoting/base/compound_buffer.h b/remoting/base/compound_buffer.h new file mode 100644 index 0000000..050182a --- /dev/null +++ b/remoting/base/compound_buffer.h @@ -0,0 +1,120 @@ +// Copyright (c) 2010 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. + +// CompoundBuffer implements a data buffer that is composed of several pieces, +// each stored in a refcounted IOBuffer. It is needed for encoding/decoding +// video pipeline to represent data packet and minimize data copying. +// It is particularly useful for splitting data between multiple RTP packets +// and assembling them into one buffer on the receiving side. +// +// CompoundBufferInputStream implements ZeroCopyInputStream interface +// to be used by protobuf to decode data stored in CompoundBuffer into +// a protocol buffer message. +// +// Mutations to the buffer are not thread-safe. Immutability can be ensured +// with the Lock() method. + +#ifndef REMOTING_BASE_COMPOUND_BUFFER_H_ +#define REMOTING_BASE_COMPOUND_BUFFER_H_ + +#include <deque> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "google/protobuf/io/zero_copy_stream.h" + +namespace net { +class IOBuffer; +class IOBufferWithSize; +} // namespace net + +namespace remoting { + +class CompoundBuffer { + public: + CompoundBuffer(); + ~CompoundBuffer(); + + void Clear(); + + // Adds new chunk to the buffer. |start| defines position of the chunk + // within the |buffer|. |size| is the size of the chunk that is being + // added, not size of the |buffer|. + void Append(net::IOBuffer* buffer, int size); + void Append(net::IOBuffer* buffer, const char* start, int size); + void Append(const CompoundBuffer& buffer); + void Prepend(net::IOBuffer* buffer, int size); + void Prepend(net::IOBuffer* buffer, const char* start, int size); + void Prepend(const CompoundBuffer& buffer); + + // Same as above, but creates new IOBuffer and copies the data. + void AppendCopyOf(const char* data, int data_size); + void PrependCopyOf(const char* data, int data_size); + + // Current size of the buffer. + int total_bytes() const { return total_bytes_; } + + // Locks the buffer. After the buffer is locked, no data can be + // added or removed (content can still be changed if some other + // object holds reference to the IOBuffer objects). + void Lock(); + + // Returns true if content is locked. + bool locked() const { return locked_; } + + // Creates new IOBufferWithSize object and copies all data into it. + // Ownership of the result is given to the caller. + net::IOBufferWithSize* ToIOBufferWithSize() const; + + // Copies all data into given location. + void CopyTo(char* data, int data_size) const; + + // Clears the buffer, and initializes it with the interval from |buffer| + // starting at |start| and ending at |end|. The data itself isn't copied. + void CopyFrom(const CompoundBuffer& source, int start, int end); + + private: + friend class CompoundBufferInputStream; + + struct DataChunk { + DataChunk(net::IOBuffer* buffer, const char* start, int size); + + scoped_refptr<net::IOBuffer> buffer; + const char* start; + int size; + }; + typedef std::deque<DataChunk> DataChunkList; + + DataChunkList chunks_; + int total_bytes_; + bool locked_; + + DISALLOW_COPY_AND_ASSIGN(CompoundBuffer); +}; + +class CompoundBufferInputStream + : public google::protobuf::io::ZeroCopyInputStream { + public: + // Caller keeps ownership of |buffer|. |buffer| must be locked. + explicit CompoundBufferInputStream(const CompoundBuffer* buffer); + virtual ~CompoundBufferInputStream(); + + // google::protobuf::io::ZeroCopyInputStream interface. + virtual bool Next(const void** data, int* size); + virtual void BackUp(int count); + virtual bool Skip(int count); + virtual int64 ByteCount() const; + + private: + const CompoundBuffer* buffer_; + + size_t current_chunk_; + int current_chunk_position_; + int position_; + int last_returned_size_; +}; + +} // namespace remoting + +#endif // REMOTING_BASE_COMPOUND_BUFFER_H_ diff --git a/remoting/base/compound_buffer_unittest.cc b/remoting/base/compound_buffer_unittest.cc new file mode 100644 index 0000000..63ede5a --- /dev/null +++ b/remoting/base/compound_buffer_unittest.cc @@ -0,0 +1,249 @@ +// Copyright (c) 2010 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 <string> + +#include "base/callback.h" +#include "base/scoped_ptr.h" +#include "net/base/io_buffer.h" +#include "remoting/base/compound_buffer.h" +#include "testing/gtest/include/gtest/gtest.h" + +using net::IOBuffer; + +namespace remoting { + +namespace { +const int kDataSize = 1024; + +// Chunk sizes used to append and prepend data to the buffer. +const int kChunkSizes0[] = {kDataSize, -1}; +const int kChunkSizes1[] = {1, 10, 20, -1}; + +// Chunk sizes used to test CopyFrom(). +const int kCopySizes0[] = {10, 3, -1}; +const int kCopySizes1[] = {20, -1}; +} // namespace + +class CompoundBufferTest : public testing::Test { + public: + + // Following 5 methods are used with IterateOverPieces(). + void Append(int pos, int size) { + target_.Append(data_, data_->data() + pos, size); + } + + void AppendCopyOf(int pos, int size) { + target_.AppendCopyOf(data_->data() + pos, size); + } + + void Prepend(int pos, int size) { + target_.Prepend(data_, data_->data() + kDataSize - pos - size, size); + } + + void PrependCopyOf(int pos, int size) { + target_.PrependCopyOf(data_->data() + (kDataSize - pos - size), size); + } + + void TestCopyFrom(int pos, int size) { + CompoundBuffer copy; + copy.CopyFrom(target_, pos, pos + size); + EXPECT_TRUE(CompareData(copy, data_->data() + pos, size)); + } + + protected: + virtual void SetUp() { + data_ = new IOBuffer(kDataSize); + for (int i = 0; i < kDataSize; ++i) { + data_->data()[i] = i; + } + } + + // Iterate over chunks of data with sizes specified in |sizes| in the + // interval [0..kDataSize]. |function| is called for each chunk. + void IterateOverPieces(const int sizes[], + Callback2<int, int>::Type* function) { + DCHECK_GT(sizes[0], 0); + + int pos = 0; + int index = 0; + while (pos < kDataSize) { + int size = std::min(sizes[index], kDataSize - pos); + ++index; + if (sizes[index] <= 0) + index = 0; + + function->Run(pos, size); + + pos += size; + } + delete function; + } + + bool CompareData(const CompoundBuffer& buffer, char* data, int size) { + scoped_refptr<IOBuffer> buffer_data = buffer.ToIOBufferWithSize(); + return buffer.total_bytes() == size && + memcmp(buffer_data->data(), data, size) == 0; + } + + static size_t ReadFromInput(CompoundBufferInputStream* input, + void* data, size_t size) { + uint8* out = reinterpret_cast<uint8*>(data); + int out_size = size; + + const void* in; + int in_size = 0; + + while (true) { + if (!input->Next(&in, &in_size)) { + return size - out_size; + } + EXPECT_GT(in_size, -1); + + if (out_size <= in_size) { + memcpy(out, in, out_size); + if (in_size > out_size) { + input->BackUp(in_size - out_size); + } + return size; // Copied all of it. + } + + memcpy(out, in, in_size); + out += in_size; + out_size -= in_size; + } + } + + static void ReadString(CompoundBufferInputStream* input, + const std::string& str) { + SCOPED_TRACE(str); + scoped_array<char> buffer(new char[str.size() + 1]); + buffer[str.size()] = '\0'; + EXPECT_EQ(ReadFromInput(input, buffer.get(), str.size()), str.size()); + EXPECT_STREQ(str.data(), buffer.get()); + } + + // Construct and prepare data in the |buffer|. + static void PrepareData(scoped_ptr<CompoundBuffer>* buffer) { + static const std::string kTestData = + "Hello world!" + "This is testing" + "MultipleArrayInputStream" + "for Chromoting"; + + // Determine how many segments to split kTestData. We split the data in + // 1 character, 2 characters, 1 character, 2 characters ... + int segments = (kTestData.length() / 3) * 2; + int remaining_chars = kTestData.length() % 3; + if (remaining_chars) { + if (remaining_chars == 1) + ++segments; + else + segments += 2; + } + + CompoundBuffer* result = new CompoundBuffer(); + const char* data = kTestData.data(); + for (int i = 0; i < segments; ++i) { + int size = i % 2 == 0 ? 1 : 2; + result->Append(new net::WrappedIOBuffer(data), size); + data += size; + } + result->Lock(); + buffer->reset(result); + } + + CompoundBuffer target_; + scoped_refptr<IOBuffer> data_; +}; + +TEST_F(CompoundBufferTest, Append) { + target_.Clear(); + IterateOverPieces(kChunkSizes0, NewCallback( + static_cast<CompoundBufferTest*>(this), &CompoundBufferTest::Append)); + EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize)); + + target_.Clear(); + IterateOverPieces(kChunkSizes1, NewCallback( + static_cast<CompoundBufferTest*>(this), &CompoundBufferTest::Append)); + EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize)); +} + +TEST_F(CompoundBufferTest, AppendCopyOf) { + target_.Clear(); + IterateOverPieces(kChunkSizes0, NewCallback( + static_cast<CompoundBufferTest*>(this), + &CompoundBufferTest::AppendCopyOf)); + EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize)); + + target_.Clear(); + IterateOverPieces(kChunkSizes1, NewCallback( + static_cast<CompoundBufferTest*>(this), + &CompoundBufferTest::AppendCopyOf)); + EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize)); +} + +TEST_F(CompoundBufferTest, Prepend) { + target_.Clear(); + IterateOverPieces(kChunkSizes0, NewCallback( + static_cast<CompoundBufferTest*>(this), &CompoundBufferTest::Prepend)); + EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize)); + + target_.Clear(); + IterateOverPieces(kChunkSizes1, NewCallback( + static_cast<CompoundBufferTest*>(this), &CompoundBufferTest::Prepend)); + EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize)); +} + +TEST_F(CompoundBufferTest, PrependCopyOf) { + target_.Clear(); + IterateOverPieces(kChunkSizes0, NewCallback( + static_cast<CompoundBufferTest*>(this), + &CompoundBufferTest::PrependCopyOf)); + EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize)); + + target_.Clear(); + IterateOverPieces(kChunkSizes1, NewCallback( + static_cast<CompoundBufferTest*>(this), + &CompoundBufferTest::PrependCopyOf)); + EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize)); +} + +TEST_F(CompoundBufferTest, CopyFrom) { + target_.Clear(); + IterateOverPieces(kChunkSizes1, NewCallback( + static_cast<CompoundBufferTest*>(this), &CompoundBufferTest::Append)); + { + SCOPED_TRACE("CopyFrom.kCopySizes0"); + IterateOverPieces(kCopySizes0, NewCallback( + static_cast<CompoundBufferTest*>(this), + &CompoundBufferTest::TestCopyFrom)); + } + { + SCOPED_TRACE("CopyFrom.kCopySizes1"); + IterateOverPieces(kCopySizes1, NewCallback( + static_cast<CompoundBufferTest*>(this), + &CompoundBufferTest::TestCopyFrom)); + } +} + +TEST_F(CompoundBufferTest, InputStream) { + scoped_ptr<CompoundBuffer> buffer; + PrepareData(&buffer); + CompoundBufferInputStream stream(buffer.get()); + + ReadString(&stream, "Hello world!"); + ReadString(&stream, "This "); + ReadString(&stream, "is test"); + EXPECT_TRUE(stream.Skip(3)); + ReadString(&stream, "MultipleArrayInput"); + EXPECT_TRUE(stream.Skip(6)); + ReadString(&stream, "f"); + ReadString(&stream, "o"); + ReadString(&stream, "r"); + ReadString(&stream, " "); + ReadString(&stream, "Chromoting"); +} + +} // namespace remoting diff --git a/remoting/base/multiple_array_input_stream.cc b/remoting/base/multiple_array_input_stream.cc deleted file mode 100644 index 67aa2ff..0000000 --- a/remoting/base/multiple_array_input_stream.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2010 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 <functional> - -#include "base/logging.h" -#include "net/base/io_buffer.h" -#include "remoting/base/multiple_array_input_stream.h" - -namespace remoting { - -MultipleArrayInputStream::MultipleArrayInputStream() - : current_buffer_(0), - position_(0), - last_returned_size_(0) { -} - -MultipleArrayInputStream::~MultipleArrayInputStream() { -} - -void MultipleArrayInputStream::AddBuffer(net::IOBuffer* buffer, int size) { - DCHECK_EQ(position_, 0); // Haven't started reading. - buffers_.push_back(make_scoped_refptr( - new net::DrainableIOBuffer(buffer, size))); -} - -bool MultipleArrayInputStream::Next(const void** data, int* size) { - if (current_buffer_ < buffers_.size()) { - // Reply with the number of bytes remaining in the current buffer. - scoped_refptr<net::DrainableIOBuffer> buffer = buffers_[current_buffer_]; - last_returned_size_ = buffer->BytesRemaining(); - *data = buffer->data(); - *size = last_returned_size_; - - // After reading the current buffer then advance to the next buffer. - buffer->DidConsume(last_returned_size_); - ++current_buffer_; - position_ += last_returned_size_; - return true; - } - - // We've reached the end of the stream. So reset |last_returned_size_| - // to zero to prevent any backup request. - // This is the same as in ArrayInputStream. - // See google/protobuf/io/zero_copy_stream_impl_lite.cc. - last_returned_size_ = 0; - return false; -} - -void MultipleArrayInputStream::BackUp(int count) { - DCHECK_LE(count, last_returned_size_); - DCHECK_GT(current_buffer_, 0u); - - // Rewind one buffer and rewind data offset by |count| bytes. - --current_buffer_; - scoped_refptr<net::DrainableIOBuffer> buffer = buffers_[current_buffer_]; - buffer->SetOffset(buffer->size() - count); - position_ -= count; - DCHECK_GE(position_, 0); -} - -bool MultipleArrayInputStream::Skip(int count) { - DCHECK_GE(count, 0); - last_returned_size_ = 0; - - while (count && current_buffer_ < buffers_.size()) { - scoped_refptr<net::DrainableIOBuffer> buffer = buffers_[current_buffer_]; - int read = std::min(count, buffer->BytesRemaining()); - - // Advance the current buffer offset and position. - buffer->DidConsume(read); - position_ += read; - count -= read; - - // If the current buffer is fully read, then advance to the next buffer. - if (!buffer->BytesRemaining()) - ++current_buffer_; - } - return count == 0; -} - -int64 MultipleArrayInputStream::ByteCount() const { - return position_; -} - -} // namespace remoting diff --git a/remoting/base/multiple_array_input_stream.h b/remoting/base/multiple_array_input_stream.h deleted file mode 100644 index a848248..0000000 --- a/remoting/base/multiple_array_input_stream.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2010 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. - -// MultipleArrayInputStream implements ZeroCopyInputStream to be used by -// protobuf to decode bytes into a protocol buffer message. -// -// This input stream is made of multiple IOBuffers received from the network. -// This object retains the IOBuffers added to it. -// -// Internally, we wrap each added IOBuffer in a DrainableIOBuffer. This allows -// us to track how much data has been consumed from each IOBuffer. - -#ifndef REMOTING_BASE_MULTIPLE_ARRAY_INPUT_STREAM_H_ -#define REMOTING_BASE_MULTIPLE_ARRAY_INPUT_STREAM_H_ - -#include <vector> - -#include "base/basictypes.h" -#include "base/ref_counted.h" -#include "google/protobuf/io/zero_copy_stream.h" - -namespace net { -class DrainableIOBuffer; -class IOBuffer; -} // namespace net - -namespace remoting { - -class MultipleArrayInputStream : - public google::protobuf::io::ZeroCopyInputStream { - public: - MultipleArrayInputStream(); - virtual ~MultipleArrayInputStream(); - - // Add a buffer to the list. |buffer| is retained by this object. - void AddBuffer(net::IOBuffer* buffer, int size); - - // google::protobuf::io::ZeroCopyInputStream interface. - virtual bool Next(const void** data, int* size); - virtual void BackUp(int count); - virtual bool Skip(int count); - virtual int64 ByteCount() const; - - private: - std::vector<scoped_refptr<net::DrainableIOBuffer> > buffers_; - - size_t current_buffer_; - int position_; - int last_returned_size_; - - DISALLOW_COPY_AND_ASSIGN(MultipleArrayInputStream); -}; - -} // namespace remoting - -#endif // REMOTING_BASE_MULTIPLE_ARRAY_INPUT_STREAM_H_ diff --git a/remoting/base/multiple_array_input_stream_unittest.cc b/remoting/base/multiple_array_input_stream_unittest.cc deleted file mode 100644 index 1a21add..0000000 --- a/remoting/base/multiple_array_input_stream_unittest.cc +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2010 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 <string> - -#include "base/scoped_ptr.h" -#include "net/base/io_buffer.h" -#include "remoting/base/multiple_array_input_stream.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace remoting { - -// TODO(sergeyu): Add SCOPED_TRACE() for ReadFromInput() and ReadString(). - -static size_t ReadFromInput(MultipleArrayInputStream* input, - void* data, size_t size) { - uint8* out = reinterpret_cast<uint8*>(data); - int out_size = size; - - const void* in; - int in_size = 0; - - while (true) { - if (!input->Next(&in, &in_size)) { - return size - out_size; - } - EXPECT_GT(in_size, -1); - - if (out_size <= in_size) { - memcpy(out, in, out_size); - if (in_size > out_size) { - input->BackUp(in_size - out_size); - } - return size; // Copied all of it. - } - - memcpy(out, in, in_size); - out += in_size; - out_size -= in_size; - } -} - -static void ReadString(MultipleArrayInputStream* input, - const std::string& str) { - scoped_array<char> buffer(new char[str.size() + 1]); - buffer[str.size()] = '\0'; - EXPECT_EQ(ReadFromInput(input, buffer.get(), str.size()), str.size()); - EXPECT_STREQ(str.data(), buffer.get()); -} - -// Construct and prepare data in the |output_stream|. -static void PrepareData(scoped_ptr<MultipleArrayInputStream>* stream) { - static const std::string kTestData = - "Hello world!" - "This is testing" - "MultipleArrayInputStream" - "for Chromoting"; - - // Determine how many segments to split kTestData. We split the data in - // 1 character, 2 characters, 1 character, 2 characters ... - int segments = (kTestData.length() / 3) * 2; - int remaining_chars = kTestData.length() % 3; - if (remaining_chars) { - if (remaining_chars == 1) - ++segments; - else - segments += 2; - } - - MultipleArrayInputStream* mstream = new MultipleArrayInputStream(); - const char* data = kTestData.data(); - for (int i = 0; i < segments; ++i) { - int size = i % 2 == 0 ? 1 : 2; - mstream->AddBuffer(new net::StringIOBuffer(std::string(data, size)), size); - data += size; - } - stream->reset(mstream); -} - -TEST(MultipleArrayInputStreamTest, BasicOperations) { - scoped_ptr<MultipleArrayInputStream> stream; - PrepareData(&stream); - - ReadString(stream.get(), "Hello world!"); - ReadString(stream.get(), "This "); - ReadString(stream.get(), "is test"); - EXPECT_TRUE(stream->Skip(3)); - ReadString(stream.get(), "MultipleArrayInput"); - EXPECT_TRUE(stream->Skip(6)); - ReadString(stream.get(), "f"); - ReadString(stream.get(), "o"); - ReadString(stream.get(), "r"); - ReadString(stream.get(), " "); - ReadString(stream.get(), "Chromoting"); -} - -} // namespace remoting |