Home | History | Annotate | Download | only in common
      1 // Copyright 2013 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 "chrome/common/partial_circular_buffer.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/logging.h"
     10 
     11 namespace {
     12 
     13 inline uint32 Min3(uint32 a, uint32 b, uint32 c) {
     14   return std::min(a, std::min(b, c));
     15 }
     16 
     17 }  // namespace
     18 
     19 PartialCircularBuffer::PartialCircularBuffer(void* buffer,
     20                                              uint32 buffer_size)
     21     : buffer_data_(reinterpret_cast<BufferData*>(buffer)),
     22       memory_buffer_size_(buffer_size),
     23       data_size_(0),
     24       position_(0),
     25       total_read_(0) {
     26   uint32 header_size =
     27       buffer_data_->data - reinterpret_cast<uint8*>(buffer_data_);
     28   data_size_ = memory_buffer_size_ - header_size;
     29 
     30   DCHECK(buffer_data_);
     31   DCHECK_GE(memory_buffer_size_, header_size);
     32   DCHECK_LE(buffer_data_->total_written, data_size_);
     33   DCHECK_LT(buffer_data_->wrap_position, data_size_);
     34   DCHECK_LT(buffer_data_->end_position, data_size_);
     35 }
     36 
     37 PartialCircularBuffer::PartialCircularBuffer(void* buffer,
     38                                              uint32 buffer_size,
     39                                              uint32 wrap_position,
     40                                              bool append)
     41     : buffer_data_(reinterpret_cast<BufferData*>(buffer)),
     42       memory_buffer_size_(buffer_size),
     43       data_size_(0),
     44       position_(0),
     45       total_read_(0) {
     46   uint32 header_size =
     47       buffer_data_->data - reinterpret_cast<uint8*>(buffer_data_);
     48   data_size_ = memory_buffer_size_ - header_size;
     49 
     50   DCHECK(buffer_data_);
     51   DCHECK_GE(memory_buffer_size_, header_size);
     52 
     53   if (append) {
     54     DCHECK_LT(buffer_data_->wrap_position, data_size_);
     55     position_ = buffer_data_->end_position;
     56   } else {
     57     DCHECK_LT(wrap_position, data_size_);
     58     buffer_data_->total_written = 0;
     59     buffer_data_->wrap_position = wrap_position;
     60     buffer_data_->end_position = 0;
     61   }
     62 }
     63 
     64 uint32 PartialCircularBuffer::Read(void* buffer, uint32 buffer_size) {
     65   DCHECK(buffer_data_);
     66   if (total_read_ >= buffer_data_->total_written)
     67     return 0;
     68 
     69   uint8* buffer_uint8 = reinterpret_cast<uint8*>(buffer);
     70   uint32 read = 0;
     71 
     72   // Read from beginning part.
     73   if (position_ < buffer_data_->wrap_position) {
     74     uint32 to_wrap_pos = buffer_data_->wrap_position - position_;
     75     uint32 to_eow = buffer_data_->total_written - total_read_;
     76     uint32 to_read = Min3(buffer_size, to_wrap_pos, to_eow);
     77     memcpy(buffer_uint8, buffer_data_->data + position_, to_read);
     78     position_ += to_read;
     79     total_read_ += to_read;
     80     read += to_read;
     81     if (position_ == buffer_data_->wrap_position &&
     82         buffer_data_->total_written == data_size_) {
     83       // We've read all the beginning part, set the position to the middle part.
     84       // (The second condition above checks if the wrapping part is filled, i.e.
     85       // writing has wrapped.)
     86       position_ = buffer_data_->end_position;
     87     }
     88     if (read >= buffer_size) {
     89       DCHECK_EQ(read, buffer_size);
     90       return read;
     91     }
     92     if (read >= to_eow) {
     93       DCHECK_EQ(read, to_eow);
     94       DCHECK_EQ(total_read_, buffer_data_->total_written);
     95       return read;
     96     }
     97   }
     98 
     99   // Read from middle part.
    100   DCHECK_GE(position_, buffer_data_->wrap_position);
    101   if (position_ >= buffer_data_->end_position) {
    102     uint32 remaining_buffer_size = buffer_size - read;
    103     uint32 to_eof = data_size_ - position_;
    104     uint32 to_eow = buffer_data_->total_written - total_read_;
    105     uint32 to_read = Min3(remaining_buffer_size, to_eof, to_eow);
    106     memcpy(buffer_uint8 + read, buffer_data_->data + position_, to_read);
    107     position_ += to_read;
    108     total_read_ += to_read;
    109     read += to_read;
    110     if (position_ == data_size_) {
    111       // We've read all the middle part, set position to the end part.
    112       position_ = buffer_data_->wrap_position;
    113     }
    114     if (read >= buffer_size) {
    115       DCHECK_EQ(read, buffer_size);
    116       return read;
    117     }
    118     if (total_read_ >= buffer_data_->total_written) {
    119       DCHECK_EQ(total_read_, buffer_data_->total_written);
    120       return read;
    121     }
    122   }
    123 
    124   // Read from end part.
    125   DCHECK_GE(position_, buffer_data_->wrap_position);
    126   DCHECK_LT(position_, buffer_data_->end_position);
    127   uint32 remaining_buffer_size = buffer_size - read;
    128   uint32 to_eob = buffer_data_->end_position - position_;
    129   uint32 to_eow = buffer_data_->total_written - total_read_;
    130   uint32 to_read = Min3(remaining_buffer_size, to_eob, to_eow);
    131   memcpy(buffer_uint8 + read, buffer_data_->data + position_, to_read);
    132   position_ += to_read;
    133   total_read_ += to_read;
    134   read += to_read;
    135   DCHECK_LE(read, buffer_size);
    136   DCHECK_LE(total_read_, buffer_data_->total_written);
    137   return read;
    138 }
    139 
    140 void PartialCircularBuffer::Write(const void* buffer, uint32 buffer_size) {
    141   DCHECK(buffer_data_);
    142   uint32 position_before_write = position_;
    143 
    144   uint32 to_eof = data_size_ - position_;
    145   uint32 to_write = std::min(buffer_size, to_eof);
    146   DoWrite(buffer_data_->data + position_, buffer, to_write);
    147   if (position_ >= data_size_) {
    148     DCHECK_EQ(position_, data_size_);
    149     position_ = buffer_data_->wrap_position;
    150   }
    151 
    152   if (to_write < buffer_size) {
    153     uint32 remainder_to_write = buffer_size - to_write;
    154     DCHECK_LT(position_, position_before_write);
    155     DCHECK_LE(position_ + remainder_to_write, position_before_write);
    156     DoWrite(buffer_data_->data + position_,
    157             reinterpret_cast<const uint8*>(buffer) + to_write,
    158             remainder_to_write);
    159   }
    160 }
    161 
    162 void PartialCircularBuffer::DoWrite(void* dest, const void* src, uint32 num) {
    163   memcpy(dest, src, num);
    164   position_ += num;
    165   buffer_data_->total_written =
    166       std::min(buffer_data_->total_written + num, data_size_);
    167   buffer_data_->end_position = position_;
    168 }
    169