• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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