// 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::DataChunk::~DataChunk() {} 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.get(), 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.get(), 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::CropFront(int bytes) { CHECK(!locked_); if (total_bytes_ <= bytes) { Clear(); return; } total_bytes_ -= bytes; while (!chunks_.empty() && chunks_.front().size <= bytes) { bytes -= chunks_.front().size; chunks_.pop_front(); } if (!chunks_.empty() && bytes > 0) { chunks_.front().start += bytes; chunks_.front().size -= bytes; DCHECK_GT(chunks_.front().size, 0); bytes = 0; } DCHECK_EQ(bytes, 0); } void CompoundBuffer::CropBack(int bytes) { CHECK(!locked_); if (total_bytes_ <= bytes) { Clear(); return; } total_bytes_ -= bytes; while (!chunks_.empty() && chunks_.back().size <= bytes) { bytes -= chunks_.back().size; chunks_.pop_back(); } if (!chunks_.empty() && bytes > 0) { chunks_.back().size -= bytes; DCHECK_GT(chunks_.back().size, 0); bytes = 0; } DCHECK_EQ(bytes, 0); } 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