1// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <functional> 6 7#include "base/logging.h" 8#include "net/base/io_buffer.h" 9#include "remoting/base/compound_buffer.h" 10 11namespace remoting { 12 13CompoundBuffer::DataChunk::DataChunk( 14 net::IOBuffer* buffer_value, const char* start_value, int size_value) 15 : buffer(buffer_value), 16 start(start_value), 17 size(size_value) { 18} 19 20CompoundBuffer::DataChunk::~DataChunk() {} 21 22CompoundBuffer::CompoundBuffer() 23 : total_bytes_(0), 24 locked_(false) { 25} 26 27CompoundBuffer::~CompoundBuffer() { 28} 29 30void CompoundBuffer::Clear() { 31 CHECK(!locked_); 32 chunks_.clear(); 33 total_bytes_ = 0; 34} 35 36void CompoundBuffer::Append(net::IOBuffer* buffer, 37 const char* start, int size) { 38 // A weak check that the |start| is within |buffer|. 39 DCHECK_GE(start, buffer->data()); 40 DCHECK_GT(size, 0); 41 42 CHECK(!locked_); 43 44 chunks_.push_back(DataChunk(buffer, start, size)); 45 total_bytes_ += size; 46} 47 48void CompoundBuffer::Append(net::IOBuffer* buffer, int size) { 49 Append(buffer, buffer->data(), size); 50} 51 52void CompoundBuffer::Append(const CompoundBuffer& buffer) { 53 for (DataChunkList::const_iterator it = buffer.chunks_.begin(); 54 it != buffer.chunks_.end(); ++it) { 55 Append(it->buffer.get(), it->start, it->size); 56 } 57} 58 59void CompoundBuffer::Prepend(net::IOBuffer* buffer, 60 const char* start, int size) { 61 // A weak check that the |start| is within |buffer|. 62 DCHECK_GE(start, buffer->data()); 63 DCHECK_GT(size, 0); 64 65 CHECK(!locked_); 66 67 chunks_.push_front(DataChunk(buffer, start, size)); 68 total_bytes_ += size; 69} 70 71void CompoundBuffer::Prepend(net::IOBuffer* buffer, int size) { 72 Prepend(buffer, buffer->data(), size); 73} 74 75void CompoundBuffer::Prepend(const CompoundBuffer& buffer) { 76 for (DataChunkList::const_iterator it = buffer.chunks_.begin(); 77 it != buffer.chunks_.end(); ++it) { 78 Prepend(it->buffer.get(), it->start, it->size); 79 } 80} 81void CompoundBuffer::AppendCopyOf(const char* data, int size) { 82 net::IOBuffer* buffer = new net::IOBuffer(size); 83 memcpy(buffer->data(), data, size); 84 Append(buffer, size); 85} 86 87void CompoundBuffer::PrependCopyOf(const char* data, int size) { 88 net::IOBuffer* buffer = new net::IOBuffer(size); 89 memcpy(buffer->data(), data, size); 90 Prepend(buffer, size); 91} 92 93void CompoundBuffer::CropFront(int bytes) { 94 CHECK(!locked_); 95 96 if (total_bytes_ <= bytes) { 97 Clear(); 98 return; 99 } 100 101 total_bytes_ -= bytes; 102 while (!chunks_.empty() && chunks_.front().size <= bytes) { 103 bytes -= chunks_.front().size; 104 chunks_.pop_front(); 105 } 106 if (!chunks_.empty() && bytes > 0) { 107 chunks_.front().start += bytes; 108 chunks_.front().size -= bytes; 109 DCHECK_GT(chunks_.front().size, 0); 110 bytes = 0; 111 } 112 DCHECK_EQ(bytes, 0); 113} 114 115void CompoundBuffer::CropBack(int bytes) { 116 CHECK(!locked_); 117 118 if (total_bytes_ <= bytes) { 119 Clear(); 120 return; 121 } 122 123 total_bytes_ -= bytes; 124 while (!chunks_.empty() && chunks_.back().size <= bytes) { 125 bytes -= chunks_.back().size; 126 chunks_.pop_back(); 127 } 128 if (!chunks_.empty() && bytes > 0) { 129 chunks_.back().size -= bytes; 130 DCHECK_GT(chunks_.back().size, 0); 131 bytes = 0; 132 } 133 DCHECK_EQ(bytes, 0); 134} 135 136void CompoundBuffer::Lock() { 137 locked_ = true; 138} 139 140net::IOBufferWithSize* CompoundBuffer::ToIOBufferWithSize() const { 141 net::IOBufferWithSize* result = new net::IOBufferWithSize(total_bytes_); 142 CopyTo(result->data(), total_bytes_); 143 return result; 144} 145 146void CompoundBuffer::CopyTo(char* data, int size) const { 147 char* pos = data; 148 for (DataChunkList::const_iterator it = chunks_.begin(); 149 it != chunks_.end(); ++it) { 150 CHECK_LE(pos + it->size, data + size); 151 memcpy(pos, it->start, it->size); 152 pos += it->size; 153 } 154} 155 156void CompoundBuffer::CopyFrom(const CompoundBuffer& source, 157 int start, int end) { 158 // Check that 0 <= |start| <= |end| <= |total_bytes_|. 159 DCHECK_LE(0, start); 160 DCHECK_LE(start, end); 161 DCHECK_LE(end, source.total_bytes()); 162 163 Clear(); 164 165 if (end == start) { 166 return; 167 } 168 169 // Iterate over chunks in the |source| and add those that we need. 170 int pos = 0; 171 for (DataChunkList::const_iterator it = source.chunks_.begin(); 172 it != source.chunks_.end(); ++it) { 173 174 // Add data from the current chunk only if it is in the specified interval. 175 if (pos + it->size > start && pos < end) { 176 int relative_start = std::max(0, start - pos); 177 int relative_end = std::min(it->size, end - pos); 178 DCHECK_LE(0, relative_start); 179 DCHECK_LT(relative_start, relative_end); 180 DCHECK_LE(relative_end, it->size); 181 Append(it->buffer.get(), it->start + relative_start, 182 relative_end - relative_start); 183 } 184 185 pos += it->size; 186 if (pos >= end) { 187 // We've got all the data we need. 188 break; 189 } 190 } 191 192 DCHECK_EQ(total_bytes_, end - start); 193} 194 195CompoundBufferInputStream::CompoundBufferInputStream( 196 const CompoundBuffer* buffer) 197 : buffer_(buffer), 198 current_chunk_(0), 199 current_chunk_position_(0), 200 position_(0), 201 last_returned_size_(0) { 202 DCHECK(buffer_->locked()); 203} 204 205CompoundBufferInputStream::~CompoundBufferInputStream() { 206} 207 208bool CompoundBufferInputStream::Next(const void** data, int* size) { 209 if (current_chunk_ < buffer_->chunks_.size()) { 210 // Reply with the number of bytes remaining in the current buffer. 211 const CompoundBuffer::DataChunk& chunk = buffer_->chunks_[current_chunk_]; 212 int read_size = chunk.size - current_chunk_position_; 213 *data = chunk.start + current_chunk_position_; 214 *size = read_size; 215 216 // Adjust position. 217 ++current_chunk_; 218 current_chunk_position_ = 0; 219 position_ += read_size; 220 221 last_returned_size_ = read_size; 222 return true; 223 } 224 225 DCHECK_EQ(position_, buffer_->total_bytes()); 226 227 // We've reached the end of the stream. So reset |last_returned_size_| 228 // to zero to prevent any backup request. 229 // This is the same as in ArrayInputStream. 230 // See google/protobuf/io/zero_copy_stream_impl_lite.cc. 231 last_returned_size_ = 0; 232 return false; 233} 234 235void CompoundBufferInputStream::BackUp(int count) { 236 DCHECK_LE(count, last_returned_size_); 237 DCHECK_GT(current_chunk_, 0u); 238 239 // Rewind one buffer and rewind data offset by |count| bytes. 240 --current_chunk_; 241 const CompoundBuffer::DataChunk& chunk = buffer_->chunks_[current_chunk_]; 242 current_chunk_position_ = chunk.size - count; 243 position_ -= count; 244 DCHECK_GE(position_, 0); 245 246 // Prevent additional backups. 247 last_returned_size_ = 0; 248} 249 250bool CompoundBufferInputStream::Skip(int count) { 251 DCHECK_GE(count, 0); 252 last_returned_size_ = 0; 253 254 while (count > 0 && current_chunk_ < buffer_->chunks_.size()) { 255 const CompoundBuffer::DataChunk& chunk = buffer_->chunks_[current_chunk_]; 256 int read = std::min(count, chunk.size - current_chunk_position_); 257 258 // Advance the current buffer offset and position. 259 current_chunk_position_ += read; 260 position_ += read; 261 count -= read; 262 263 // If the current buffer is fully read, then advance to the next buffer. 264 if (current_chunk_position_ == chunk.size) { 265 ++current_chunk_; 266 current_chunk_position_ = 0; 267 } 268 } 269 270 return count == 0; 271} 272 273int64 CompoundBufferInputStream::ByteCount() const { 274 return position_; 275} 276 277} // namespace remoting 278