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
DataChunk(net::IOBuffer * buffer_value,const char * start_value,int size_value)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
~DataChunk()20 CompoundBuffer::DataChunk::~DataChunk() {}
21
CompoundBuffer()22 CompoundBuffer::CompoundBuffer()
23 : total_bytes_(0),
24 locked_(false) {
25 }
26
~CompoundBuffer()27 CompoundBuffer::~CompoundBuffer() {
28 }
29
Clear()30 void CompoundBuffer::Clear() {
31 CHECK(!locked_);
32 chunks_.clear();
33 total_bytes_ = 0;
34 }
35
Append(net::IOBuffer * buffer,const char * start,int size)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
Append(net::IOBuffer * buffer,int size)48 void CompoundBuffer::Append(net::IOBuffer* buffer, int size) {
49 Append(buffer, buffer->data(), size);
50 }
51
Append(const CompoundBuffer & buffer)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
Prepend(net::IOBuffer * buffer,const char * start,int size)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
Prepend(net::IOBuffer * buffer,int size)71 void CompoundBuffer::Prepend(net::IOBuffer* buffer, int size) {
72 Prepend(buffer, buffer->data(), size);
73 }
74
Prepend(const CompoundBuffer & buffer)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 }
AppendCopyOf(const char * data,int size)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
PrependCopyOf(const char * data,int size)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
CropFront(int bytes)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
CropBack(int bytes)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
Lock()136 void CompoundBuffer::Lock() {
137 locked_ = true;
138 }
139
ToIOBufferWithSize() const140 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
CopyTo(char * data,int size) const146 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
CopyFrom(const CompoundBuffer & source,int start,int end)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
CompoundBufferInputStream(const CompoundBuffer * buffer)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
~CompoundBufferInputStream()205 CompoundBufferInputStream::~CompoundBufferInputStream() {
206 }
207
Next(const void ** data,int * size)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
BackUp(int count)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
Skip(int count)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
ByteCount() const273 int64 CompoundBufferInputStream::ByteCount() const {
274 return position_;
275 }
276
277 } // namespace remoting
278