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