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..9e8b8c32a2ae77d957ebe33cd68976786dc53f24 |
--- /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 |