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 11 namespace remoting { 12 13 CompoundBuffer::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 20 CompoundBuffer::DataChunk::~DataChunk() {} 21 22 CompoundBuffer::CompoundBuffer() 23 : total_bytes_(0), 24 locked_(false) { 25 } 26 27 CompoundBuffer::~CompoundBuffer() { 28 } 29 30 void CompoundBuffer::Clear() { 31 CHECK(!locked_); 32 chunks_.clear(); 33 total_bytes_ = 0; 34 } 35 36 void 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 48 void CompoundBuffer::Append(net::IOBuffer* buffer, int size) { 49 Append(buffer, buffer->data(), size); 50 } 51 52 void 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 59 void 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 71 void CompoundBuffer::Prepend(net::IOBuffer* buffer, int size) { 72 Prepend(buffer, buffer->data(), size); 73 } 74 75 void 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 } 81 void 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 87 void 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 93 void 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 115 void 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 136 void CompoundBuffer::Lock() { 137 locked_ = true; 138 } 139 140 net::IOBufferWithSize* CompoundBuffer::ToIOBufferWithSize() const { 141 net::IOBufferWithSize* result = new net::IOBufferWithSize(total_bytes_); 142 CopyTo(result->data(), total_bytes_); 143 return result; 144 } 145 146 void 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 156 void 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 195 CompoundBufferInputStream::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 205 CompoundBufferInputStream::~CompoundBufferInputStream() { 206 } 207 208 bool 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 235 void 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 250 bool 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 273 int64 CompoundBufferInputStream::ByteCount() const { 274 return position_; 275 } 276 277 } // namespace remoting 278