 Chromium Code Reviews
 Chromium Code Reviews Issue 4779001:
  Added CompoundBuffer that will be used to store data in the encoding/decoding  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src
    
  
    Issue 4779001:
  Added CompoundBuffer that will be used to store data in the encoding/decoding  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src| Index: remoting/base/compound_buffer.cc | 
| diff --git a/remoting/base/compound_buffer.cc b/remoting/base/compound_buffer.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..8c536e46b39a41a11e7fdd9e89d942d055bba2cf | 
| --- /dev/null | 
| +++ b/remoting/base/compound_buffer.cc | 
| @@ -0,0 +1,209 @@ | 
| +// 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, const char* data_start, int data_size) | 
| + : buffer(buffer), | 
| + data_start(data_start), | 
| + data_size(data_size) { | 
| +} | 
| + | 
| +CompoundBuffer::CompoundBuffer() | 
| + : total_bytes_(0), | 
| + current_buffer_(0), | 
| + current_buffer_position_(0), | 
| + position_(0), | 
| + last_returned_size_(0) { | 
| +} | 
| + | 
| +CompoundBuffer::~CompoundBuffer() { | 
| +} | 
| + | 
| +void CompoundBuffer::Clear() { | 
| + buffers_.clear(); | 
| + total_bytes_ = 0; | 
| + position_ = 0; | 
| + last_returned_size_ = 0; | 
| + current_buffer_ = 0; | 
| + current_buffer_position_ = 0; | 
| +} | 
| + | 
| + | 
| 
awong
2010/11/12 02:40:06
remove extra newline
 
Sergey Ulanov
2010/11/13 04:43:39
Done.
 | 
| +void CompoundBuffer::Append(net::IOBuffer* buffer, | 
| + const char* data, int data_size) { | 
| + // A weak check that the |data| is within |buffer|. | 
| + DCHECK_GE(data, buffer->data()); | 
| + DCHECK_GT(data_size, 0); | 
| + | 
| + DCHECK_EQ(position_, 0); // Haven't started reading. | 
| 
awong
2010/11/12 02:40:06
So the API contract is that once you start reading
 
Sergey Ulanov
2010/11/13 04:43:39
Done.
 | 
| + | 
| + buffers_.push_back(DataChunk(buffer, data, data_size)); | 
| + total_bytes_ += data_size; | 
| +} | 
| + | 
| +void CompoundBuffer::Append(net::IOBuffer* buffer, int data_size) { | 
| + Append(buffer, buffer->data(), data_size); | 
| +} | 
| + | 
| +void CompoundBuffer::Prepend(net::IOBuffer* buffer, | 
| + const char* data, int data_size) { | 
| + // A weak check that the |data| is within |buffer|. | 
| + DCHECK_GE(data, buffer->data()); | 
| 
awong
2010/11/12 02:40:06
Same comments as earlier.
Also, should there be a
 
Sergey Ulanov
2010/11/13 04:43:39
Added locked() returns true if content is locked.
 | 
| + DCHECK_GT(data_size, 0); | 
| + | 
| + DCHECK_EQ(position_, 0); // Haven't started reading. | 
| + | 
| + buffers_.push_front(DataChunk(buffer, data, data_size)); | 
| + total_bytes_ += data_size; | 
| +} | 
| + | 
| +void CompoundBuffer::Prepend(net::IOBuffer* buffer, int data_size) { | 
| + Prepend(buffer, buffer->data(), data_size); | 
| +} | 
| + | 
| +void CompoundBuffer::CopyAndAppend(const char* data, int data_size) { | 
| + net::IOBuffer* buffer = new net::IOBuffer(data_size); | 
| + memcpy(buffer->data(), data, data_size); | 
| + Append(buffer, buffer->data(), data_size); | 
| +} | 
| + | 
| +void CompoundBuffer::CopyAndPrepend(const char* data, int data_size) { | 
| + net::IOBuffer* buffer = new net::IOBuffer(data_size); | 
| + memcpy(buffer->data(), data, data_size); | 
| + Prepend(buffer, buffer->data(), data_size); | 
| +} | 
| + | 
| +net::IOBufferWithSize* CompoundBuffer::Assemble() const { | 
| + net::IOBufferWithSize* result = new net::IOBufferWithSize(total_bytes_); | 
| 
awong
2010/11/12 02:40:06
Are these things refcounted?
 
Sergey Ulanov
2010/11/13 04:43:39
Yes, but we just create it, and then the caller wi
 | 
| + Assemble(result->data(), total_bytes_); | 
| + return result; | 
| +} | 
| + | 
| +void CompoundBuffer::Assemble(char* data, int data_size) const { | 
| + char* pos = data; | 
| + for (DataChunkList::const_iterator it = buffers_.begin(); | 
| + it != buffers_.end(); ++it) { | 
| + CHECK_LE(pos + it->data_size, data + data_size); | 
| + memcpy(pos, it->data_start, it->data_size); | 
| + pos += it->data_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.buffers_.begin(); | 
| + it != source.buffers_.end(); ++it) { | 
| + | 
| + // Add data from the current chunk only if it is in the specified interval. | 
| + if (pos + it->data_size > start && pos < end) { | 
| + int relative_start = std::max(0, start - pos); | 
| + int relative_end = std::min(it->data_size, end - pos); | 
| + DCHECK_LE(0, relative_start); | 
| + DCHECK_LT(relative_start, relative_end); | 
| + DCHECK_LE(relative_end, it->data_size); | 
| + Append(it->buffer.get(), it->data_start + relative_start, | 
| + relative_end - relative_start); | 
| + } | 
| + | 
| + pos += it->data_size; | 
| + if (pos >= end) { | 
| + // We've got all the data we need. | 
| + break; | 
| + } | 
| + } | 
| + | 
| + DCHECK_EQ(total_bytes_, end - start); | 
| +} | 
| + | 
| +bool CompoundBuffer::Next(const void** data, int* size) { | 
| + if (current_buffer_ < buffers_.size()) { | 
| + // Reply with the number of bytes remaining in the current buffer. | 
| + const DataChunk& buffer = buffers_[current_buffer_]; | 
| + int read_size = buffer.data_size - current_buffer_position_; | 
| + *data = buffer.data_start + current_buffer_position_; | 
| + *size = read_size; | 
| + | 
| + // Adjust position. | 
| + ++current_buffer_; | 
| + current_buffer_position_ = 0; | 
| + position_ += read_size; | 
| + | 
| + last_returned_size_ = read_size; | 
| + return true; | 
| + } | 
| + | 
| + DCHECK_EQ(position_, 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 CompoundBuffer::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_; | 
| + const DataChunk& buffer = buffers_[current_buffer_]; | 
| + current_buffer_position_ = buffer.data_size - count; | 
| + position_ -= count; | 
| + DCHECK_GE(position_, 0); | 
| + | 
| + // Prevent additional backups. | 
| + last_returned_size_ = 0; | 
| +} | 
| + | 
| +bool CompoundBuffer::Skip(int count) { | 
| + DCHECK_GE(count, 0); | 
| + last_returned_size_ = 0; | 
| + | 
| + while (count > 0 && current_buffer_ < buffers_.size()) { | 
| + const DataChunk& buffer = buffers_[current_buffer_]; | 
| + int read = std::min(count, buffer.data_size - current_buffer_position_); | 
| + | 
| + // Advance the current buffer offset and position. | 
| + current_buffer_position_ += read; | 
| + position_ += read; | 
| + count -= read; | 
| + | 
| + // If the current buffer is fully read, then advance to the next buffer. | 
| + if (current_buffer_position_ == buffer.data_size) { | 
| + ++current_buffer_; | 
| + current_buffer_position_ = 0; | 
| + } | 
| + } | 
| + | 
| + return count == 0; | 
| +} | 
| + | 
| +int64 CompoundBuffer::ByteCount() const { | 
| + return position_; | 
| +} | 
| + | 
| +} // namespace remoting |