Home | History | Annotate | Download | only in base
      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