// 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