Home | History | Annotate | Download | only in base
      1 /*
      2  *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #ifndef WEBRTC_BASE_BUFFER_H_
     12 #define WEBRTC_BASE_BUFFER_H_
     13 
     14 #include <algorithm>  // std::swap (pre-C++11)
     15 #include <cassert>
     16 #include <cstring>
     17 #include <utility>  // std::swap (C++11 and later)
     18 
     19 #include "webrtc/base/deprecation.h"
     20 #include "webrtc/base/scoped_ptr.h"
     21 
     22 namespace rtc {
     23 
     24 namespace internal {
     25 
     26 // (Internal; please don't use outside this file.) ByteType<T>::t is int if T
     27 // is uint8_t, int8_t, or char; otherwise, it's a compilation error. Use like
     28 // this:
     29 //
     30 //   template <typename T, typename ByteType<T>::t = 0>
     31 //   void foo(T* x);
     32 //
     33 // to let foo<T> be defined only for byte-sized integers.
     34 template <typename T>
     35 struct ByteType {
     36  private:
     37   static int F(uint8_t*);
     38   static int F(int8_t*);
     39   static int F(char*);
     40 
     41  public:
     42   using t = decltype(F(static_cast<T*>(nullptr)));
     43 };
     44 
     45 }  // namespace internal
     46 
     47 // Basic buffer class, can be grown and shrunk dynamically.
     48 // Unlike std::string/vector, does not initialize data when expanding capacity.
     49 class Buffer {
     50  public:
     51   Buffer();                   // An empty buffer.
     52   Buffer(const Buffer& buf);  // Copy size and contents of an existing buffer.
     53   Buffer(Buffer&& buf);       // Move contents from an existing buffer.
     54 
     55   // Construct a buffer with the specified number of uninitialized bytes.
     56   explicit Buffer(size_t size);
     57   Buffer(size_t size, size_t capacity);
     58 
     59   // Construct a buffer and copy the specified number of bytes into it. The
     60   // source array may be (const) uint8_t*, int8_t*, or char*.
     61   template <typename T, typename internal::ByteType<T>::t = 0>
     62   Buffer(const T* data, size_t size)
     63       : Buffer(data, size, size) {}
     64   template <typename T, typename internal::ByteType<T>::t = 0>
     65   Buffer(const T* data, size_t size, size_t capacity)
     66       : Buffer(size, capacity) {
     67     std::memcpy(data_.get(), data, size);
     68   }
     69 
     70   // Construct a buffer from the contents of an array.
     71   template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
     72   Buffer(const T(&array)[N])
     73       : Buffer(array, N) {}
     74 
     75   ~Buffer();
     76 
     77   // Get a pointer to the data. Just .data() will give you a (const) uint8_t*,
     78   // but you may also use .data<int8_t>() and .data<char>().
     79   template <typename T = uint8_t, typename internal::ByteType<T>::t = 0>
     80   const T* data() const {
     81     assert(IsConsistent());
     82     return reinterpret_cast<T*>(data_.get());
     83   }
     84   template <typename T = uint8_t, typename internal::ByteType<T>::t = 0>
     85   T* data() {
     86     assert(IsConsistent());
     87     return reinterpret_cast<T*>(data_.get());
     88   }
     89 
     90   size_t size() const {
     91     assert(IsConsistent());
     92     return size_;
     93   }
     94   size_t capacity() const {
     95     assert(IsConsistent());
     96     return capacity_;
     97   }
     98 
     99   Buffer& operator=(const Buffer& buf) {
    100     if (&buf != this)
    101       SetData(buf.data(), buf.size());
    102     return *this;
    103   }
    104   Buffer& operator=(Buffer&& buf) {
    105     assert(IsConsistent());
    106     assert(buf.IsConsistent());
    107     size_ = buf.size_;
    108     capacity_ = buf.capacity_;
    109     data_ = std::move(buf.data_);
    110     buf.OnMovedFrom();
    111     return *this;
    112   }
    113 
    114   bool operator==(const Buffer& buf) const {
    115     assert(IsConsistent());
    116     return size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0;
    117   }
    118 
    119   bool operator!=(const Buffer& buf) const { return !(*this == buf); }
    120 
    121   // Replace the contents of the buffer. Accepts the same types as the
    122   // constructors.
    123   template <typename T, typename internal::ByteType<T>::t = 0>
    124   void SetData(const T* data, size_t size) {
    125     assert(IsConsistent());
    126     size_ = 0;
    127     AppendData(data, size);
    128   }
    129   template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
    130   void SetData(const T(&array)[N]) {
    131     SetData(array, N);
    132   }
    133   void SetData(const Buffer& buf) { SetData(buf.data(), buf.size()); }
    134 
    135   // Append data to the buffer. Accepts the same types as the constructors.
    136   template <typename T, typename internal::ByteType<T>::t = 0>
    137   void AppendData(const T* data, size_t size) {
    138     assert(IsConsistent());
    139     const size_t new_size = size_ + size;
    140     EnsureCapacity(new_size);
    141     std::memcpy(data_.get() + size_, data, size);
    142     size_ = new_size;
    143     assert(IsConsistent());
    144   }
    145   template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
    146   void AppendData(const T(&array)[N]) {
    147     AppendData(array, N);
    148   }
    149   void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); }
    150 
    151   // Sets the size of the buffer. If the new size is smaller than the old, the
    152   // buffer contents will be kept but truncated; if the new size is greater,
    153   // the existing contents will be kept and the new space will be
    154   // uninitialized.
    155   void SetSize(size_t size) {
    156     EnsureCapacity(size);
    157     size_ = size;
    158   }
    159 
    160   // Ensure that the buffer size can be increased to at least capacity without
    161   // further reallocation. (Of course, this operation might need to reallocate
    162   // the buffer.)
    163   void EnsureCapacity(size_t capacity) {
    164     assert(IsConsistent());
    165     if (capacity <= capacity_)
    166       return;
    167     scoped_ptr<uint8_t[]> new_data(new uint8_t[capacity]);
    168     std::memcpy(new_data.get(), data_.get(), size_);
    169     data_ = std::move(new_data);
    170     capacity_ = capacity;
    171     assert(IsConsistent());
    172   }
    173 
    174   // b.Pass() does the same thing as std::move(b).
    175   // Deprecated; remove in March 2016 (bug 5373).
    176   RTC_DEPRECATED Buffer&& Pass() { return DEPRECATED_Pass(); }
    177   Buffer&& DEPRECATED_Pass() {
    178     assert(IsConsistent());
    179     return std::move(*this);
    180   }
    181 
    182   // Resets the buffer to zero size and capacity. Works even if the buffer has
    183   // been moved from.
    184   void Clear() {
    185     data_.reset();
    186     size_ = 0;
    187     capacity_ = 0;
    188     assert(IsConsistent());
    189   }
    190 
    191   // Swaps two buffers. Also works for buffers that have been moved from.
    192   friend void swap(Buffer& a, Buffer& b) {
    193     using std::swap;
    194     swap(a.size_, b.size_);
    195     swap(a.capacity_, b.capacity_);
    196     swap(a.data_, b.data_);
    197   }
    198 
    199  private:
    200   // Precondition for all methods except Clear and the destructor.
    201   // Postcondition for all methods except move construction and move
    202   // assignment, which leave the moved-from object in a possibly inconsistent
    203   // state.
    204   bool IsConsistent() const {
    205     return (data_ || capacity_ == 0) && capacity_ >= size_;
    206   }
    207 
    208   // Called when *this has been moved from. Conceptually it's a no-op, but we
    209   // can mutate the state slightly to help subsequent sanity checks catch bugs.
    210   void OnMovedFrom() {
    211 #ifdef NDEBUG
    212     // Make *this consistent and empty. Shouldn't be necessary, but better safe
    213     // than sorry.
    214     size_ = 0;
    215     capacity_ = 0;
    216 #else
    217     // Ensure that *this is always inconsistent, to provoke bugs.
    218     size_ = 1;
    219     capacity_ = 0;
    220 #endif
    221   }
    222 
    223   size_t size_;
    224   size_t capacity_;
    225   scoped_ptr<uint8_t[]> data_;
    226 };
    227 
    228 }  // namespace rtc
    229 
    230 #endif  // WEBRTC_BASE_BUFFER_H_
    231