1*d9f75844SAndroid Build Coastguard Worker /* 2*d9f75844SAndroid Build Coastguard Worker * Copyright 2004 The WebRTC Project Authors. All rights reserved. 3*d9f75844SAndroid Build Coastguard Worker * 4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license 5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source 6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found 7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may 8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree. 9*d9f75844SAndroid Build Coastguard Worker */ 10*d9f75844SAndroid Build Coastguard Worker 11*d9f75844SAndroid Build Coastguard Worker #ifndef RTC_BASE_BUFFER_H_ 12*d9f75844SAndroid Build Coastguard Worker #define RTC_BASE_BUFFER_H_ 13*d9f75844SAndroid Build Coastguard Worker 14*d9f75844SAndroid Build Coastguard Worker #include <stdint.h> 15*d9f75844SAndroid Build Coastguard Worker 16*d9f75844SAndroid Build Coastguard Worker #include <algorithm> 17*d9f75844SAndroid Build Coastguard Worker #include <cstring> 18*d9f75844SAndroid Build Coastguard Worker #include <memory> 19*d9f75844SAndroid Build Coastguard Worker #include <type_traits> 20*d9f75844SAndroid Build Coastguard Worker #include <utility> 21*d9f75844SAndroid Build Coastguard Worker 22*d9f75844SAndroid Build Coastguard Worker #include "absl/strings/string_view.h" 23*d9f75844SAndroid Build Coastguard Worker #include "api/array_view.h" 24*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h" 25*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/type_traits.h" 26*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/zero_memory.h" 27*d9f75844SAndroid Build Coastguard Worker 28*d9f75844SAndroid Build Coastguard Worker namespace rtc { 29*d9f75844SAndroid Build Coastguard Worker 30*d9f75844SAndroid Build Coastguard Worker namespace internal { 31*d9f75844SAndroid Build Coastguard Worker 32*d9f75844SAndroid Build Coastguard Worker // (Internal; please don't use outside this file.) Determines if elements of 33*d9f75844SAndroid Build Coastguard Worker // type U are compatible with a BufferT<T>. For most types, we just ignore 34*d9f75844SAndroid Build Coastguard Worker // top-level const and forbid top-level volatile and require T and U to be 35*d9f75844SAndroid Build Coastguard Worker // otherwise equal, but all byte-sized integers (notably char, int8_t, and 36*d9f75844SAndroid Build Coastguard Worker // uint8_t) are compatible with each other. (Note: We aim to get rid of this 37*d9f75844SAndroid Build Coastguard Worker // behavior, and treat all types the same.) 38*d9f75844SAndroid Build Coastguard Worker template <typename T, typename U> 39*d9f75844SAndroid Build Coastguard Worker struct BufferCompat { 40*d9f75844SAndroid Build Coastguard Worker static constexpr bool value = 41*d9f75844SAndroid Build Coastguard Worker !std::is_volatile<U>::value && 42*d9f75844SAndroid Build Coastguard Worker ((std::is_integral<T>::value && sizeof(T) == 1) 43*d9f75844SAndroid Build Coastguard Worker ? (std::is_integral<U>::value && sizeof(U) == 1) 44*d9f75844SAndroid Build Coastguard Worker : (std::is_same<T, typename std::remove_const<U>::type>::value)); 45*d9f75844SAndroid Build Coastguard Worker }; 46*d9f75844SAndroid Build Coastguard Worker 47*d9f75844SAndroid Build Coastguard Worker } // namespace internal 48*d9f75844SAndroid Build Coastguard Worker 49*d9f75844SAndroid Build Coastguard Worker // Basic buffer class, can be grown and shrunk dynamically. 50*d9f75844SAndroid Build Coastguard Worker // Unlike std::string/vector, does not initialize data when increasing size. 51*d9f75844SAndroid Build Coastguard Worker // If "ZeroOnFree" is true, any memory is explicitly cleared before releasing. 52*d9f75844SAndroid Build Coastguard Worker // The type alias "ZeroOnFreeBuffer" below should be used instead of setting 53*d9f75844SAndroid Build Coastguard Worker // "ZeroOnFree" in the template manually to "true". 54*d9f75844SAndroid Build Coastguard Worker template <typename T, bool ZeroOnFree = false> 55*d9f75844SAndroid Build Coastguard Worker class BufferT { 56*d9f75844SAndroid Build Coastguard Worker // We want T's destructor and default constructor to be trivial, i.e. perform 57*d9f75844SAndroid Build Coastguard Worker // no action, so that we don't have to touch the memory we allocate and 58*d9f75844SAndroid Build Coastguard Worker // deallocate. And we want T to be trivially copyable, so that we can copy T 59*d9f75844SAndroid Build Coastguard Worker // instances with std::memcpy. This is precisely the definition of a trivial 60*d9f75844SAndroid Build Coastguard Worker // type. 61*d9f75844SAndroid Build Coastguard Worker static_assert(std::is_trivial<T>::value, "T must be a trivial type."); 62*d9f75844SAndroid Build Coastguard Worker 63*d9f75844SAndroid Build Coastguard Worker // This class relies heavily on being able to mutate its data. 64*d9f75844SAndroid Build Coastguard Worker static_assert(!std::is_const<T>::value, "T may not be const"); 65*d9f75844SAndroid Build Coastguard Worker 66*d9f75844SAndroid Build Coastguard Worker public: 67*d9f75844SAndroid Build Coastguard Worker using value_type = T; 68*d9f75844SAndroid Build Coastguard Worker using const_iterator = const T*; 69*d9f75844SAndroid Build Coastguard Worker 70*d9f75844SAndroid Build Coastguard Worker // An empty BufferT. BufferT()71*d9f75844SAndroid Build Coastguard Worker BufferT() : size_(0), capacity_(0), data_(nullptr) { 72*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 73*d9f75844SAndroid Build Coastguard Worker } 74*d9f75844SAndroid Build Coastguard Worker 75*d9f75844SAndroid Build Coastguard Worker // Disable copy construction and copy assignment, since copying a buffer is 76*d9f75844SAndroid Build Coastguard Worker // expensive enough that we want to force the user to be explicit about it. 77*d9f75844SAndroid Build Coastguard Worker BufferT(const BufferT&) = delete; 78*d9f75844SAndroid Build Coastguard Worker BufferT& operator=(const BufferT&) = delete; 79*d9f75844SAndroid Build Coastguard Worker BufferT(BufferT && buf)80*d9f75844SAndroid Build Coastguard Worker BufferT(BufferT&& buf) 81*d9f75844SAndroid Build Coastguard Worker : size_(buf.size()), 82*d9f75844SAndroid Build Coastguard Worker capacity_(buf.capacity()), 83*d9f75844SAndroid Build Coastguard Worker data_(std::move(buf.data_)) { 84*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 85*d9f75844SAndroid Build Coastguard Worker buf.OnMovedFrom(); 86*d9f75844SAndroid Build Coastguard Worker } 87*d9f75844SAndroid Build Coastguard Worker 88*d9f75844SAndroid Build Coastguard Worker // Construct a buffer with the specified number of uninitialized elements. BufferT(size_t size)89*d9f75844SAndroid Build Coastguard Worker explicit BufferT(size_t size) : BufferT(size, size) {} 90*d9f75844SAndroid Build Coastguard Worker BufferT(size_t size,size_t capacity)91*d9f75844SAndroid Build Coastguard Worker BufferT(size_t size, size_t capacity) 92*d9f75844SAndroid Build Coastguard Worker : size_(size), 93*d9f75844SAndroid Build Coastguard Worker capacity_(std::max(size, capacity)), 94*d9f75844SAndroid Build Coastguard Worker data_(capacity_ > 0 ? new T[capacity_] : nullptr) { 95*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 96*d9f75844SAndroid Build Coastguard Worker } 97*d9f75844SAndroid Build Coastguard Worker 98*d9f75844SAndroid Build Coastguard Worker // Construct a buffer and copy the specified number of elements into it. 99*d9f75844SAndroid Build Coastguard Worker template <typename U, 100*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 101*d9f75844SAndroid Build Coastguard Worker internal::BufferCompat<T, U>::value>::type* = nullptr> BufferT(const U * data,size_t size)102*d9f75844SAndroid Build Coastguard Worker BufferT(const U* data, size_t size) : BufferT(data, size, size) {} 103*d9f75844SAndroid Build Coastguard Worker 104*d9f75844SAndroid Build Coastguard Worker template <typename U, 105*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 106*d9f75844SAndroid Build Coastguard Worker internal::BufferCompat<T, U>::value>::type* = nullptr> BufferT(U * data,size_t size,size_t capacity)107*d9f75844SAndroid Build Coastguard Worker BufferT(U* data, size_t size, size_t capacity) : BufferT(size, capacity) { 108*d9f75844SAndroid Build Coastguard Worker static_assert(sizeof(T) == sizeof(U), ""); 109*d9f75844SAndroid Build Coastguard Worker if (size > 0) { 110*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(data); 111*d9f75844SAndroid Build Coastguard Worker std::memcpy(data_.get(), data, size * sizeof(U)); 112*d9f75844SAndroid Build Coastguard Worker } 113*d9f75844SAndroid Build Coastguard Worker } 114*d9f75844SAndroid Build Coastguard Worker 115*d9f75844SAndroid Build Coastguard Worker // Construct a buffer from the contents of an array. 116*d9f75844SAndroid Build Coastguard Worker template <typename U, 117*d9f75844SAndroid Build Coastguard Worker size_t N, 118*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 119*d9f75844SAndroid Build Coastguard Worker internal::BufferCompat<T, U>::value>::type* = nullptr> BufferT(U (& array)[N])120*d9f75844SAndroid Build Coastguard Worker BufferT(U (&array)[N]) : BufferT(array, N) {} 121*d9f75844SAndroid Build Coastguard Worker ~BufferT()122*d9f75844SAndroid Build Coastguard Worker ~BufferT() { MaybeZeroCompleteBuffer(); } 123*d9f75844SAndroid Build Coastguard Worker 124*d9f75844SAndroid Build Coastguard Worker // Implicit conversion to absl::string_view if T is compatible with char. 125*d9f75844SAndroid Build Coastguard Worker template <typename U = T> 126*d9f75844SAndroid Build Coastguard Worker operator typename std::enable_if<internal::BufferCompat<U, char>::value, type()127*d9f75844SAndroid Build Coastguard Worker absl::string_view>::type() const { 128*d9f75844SAndroid Build Coastguard Worker return absl::string_view(data<char>(), size()); 129*d9f75844SAndroid Build Coastguard Worker } 130*d9f75844SAndroid Build Coastguard Worker 131*d9f75844SAndroid Build Coastguard Worker // Get a pointer to the data. Just .data() will give you a (const) T*, but if 132*d9f75844SAndroid Build Coastguard Worker // T is a byte-sized integer, you may also use .data<U>() for any other 133*d9f75844SAndroid Build Coastguard Worker // byte-sized integer U. 134*d9f75844SAndroid Build Coastguard Worker template <typename U = T, 135*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 136*d9f75844SAndroid Build Coastguard Worker internal::BufferCompat<T, U>::value>::type* = nullptr> data()137*d9f75844SAndroid Build Coastguard Worker const U* data() const { 138*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 139*d9f75844SAndroid Build Coastguard Worker return reinterpret_cast<U*>(data_.get()); 140*d9f75844SAndroid Build Coastguard Worker } 141*d9f75844SAndroid Build Coastguard Worker 142*d9f75844SAndroid Build Coastguard Worker template <typename U = T, 143*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 144*d9f75844SAndroid Build Coastguard Worker internal::BufferCompat<T, U>::value>::type* = nullptr> data()145*d9f75844SAndroid Build Coastguard Worker U* data() { 146*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 147*d9f75844SAndroid Build Coastguard Worker return reinterpret_cast<U*>(data_.get()); 148*d9f75844SAndroid Build Coastguard Worker } 149*d9f75844SAndroid Build Coastguard Worker empty()150*d9f75844SAndroid Build Coastguard Worker bool empty() const { 151*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 152*d9f75844SAndroid Build Coastguard Worker return size_ == 0; 153*d9f75844SAndroid Build Coastguard Worker } 154*d9f75844SAndroid Build Coastguard Worker size()155*d9f75844SAndroid Build Coastguard Worker size_t size() const { 156*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 157*d9f75844SAndroid Build Coastguard Worker return size_; 158*d9f75844SAndroid Build Coastguard Worker } 159*d9f75844SAndroid Build Coastguard Worker capacity()160*d9f75844SAndroid Build Coastguard Worker size_t capacity() const { 161*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 162*d9f75844SAndroid Build Coastguard Worker return capacity_; 163*d9f75844SAndroid Build Coastguard Worker } 164*d9f75844SAndroid Build Coastguard Worker 165*d9f75844SAndroid Build Coastguard Worker BufferT& operator=(BufferT&& buf) { 166*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(buf.IsConsistent()); 167*d9f75844SAndroid Build Coastguard Worker MaybeZeroCompleteBuffer(); 168*d9f75844SAndroid Build Coastguard Worker size_ = buf.size_; 169*d9f75844SAndroid Build Coastguard Worker capacity_ = buf.capacity_; 170*d9f75844SAndroid Build Coastguard Worker using std::swap; 171*d9f75844SAndroid Build Coastguard Worker swap(data_, buf.data_); 172*d9f75844SAndroid Build Coastguard Worker buf.data_.reset(); 173*d9f75844SAndroid Build Coastguard Worker buf.OnMovedFrom(); 174*d9f75844SAndroid Build Coastguard Worker return *this; 175*d9f75844SAndroid Build Coastguard Worker } 176*d9f75844SAndroid Build Coastguard Worker 177*d9f75844SAndroid Build Coastguard Worker bool operator==(const BufferT& buf) const { 178*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 179*d9f75844SAndroid Build Coastguard Worker if (size_ != buf.size_) { 180*d9f75844SAndroid Build Coastguard Worker return false; 181*d9f75844SAndroid Build Coastguard Worker } 182*d9f75844SAndroid Build Coastguard Worker if (std::is_integral<T>::value) { 183*d9f75844SAndroid Build Coastguard Worker // Optimization. 184*d9f75844SAndroid Build Coastguard Worker return std::memcmp(data_.get(), buf.data_.get(), size_ * sizeof(T)) == 0; 185*d9f75844SAndroid Build Coastguard Worker } 186*d9f75844SAndroid Build Coastguard Worker for (size_t i = 0; i < size_; ++i) { 187*d9f75844SAndroid Build Coastguard Worker if (data_[i] != buf.data_[i]) { 188*d9f75844SAndroid Build Coastguard Worker return false; 189*d9f75844SAndroid Build Coastguard Worker } 190*d9f75844SAndroid Build Coastguard Worker } 191*d9f75844SAndroid Build Coastguard Worker return true; 192*d9f75844SAndroid Build Coastguard Worker } 193*d9f75844SAndroid Build Coastguard Worker 194*d9f75844SAndroid Build Coastguard Worker bool operator!=(const BufferT& buf) const { return !(*this == buf); } 195*d9f75844SAndroid Build Coastguard Worker 196*d9f75844SAndroid Build Coastguard Worker T& operator[](size_t index) { 197*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_LT(index, size_); 198*d9f75844SAndroid Build Coastguard Worker return data()[index]; 199*d9f75844SAndroid Build Coastguard Worker } 200*d9f75844SAndroid Build Coastguard Worker 201*d9f75844SAndroid Build Coastguard Worker T operator[](size_t index) const { 202*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_LT(index, size_); 203*d9f75844SAndroid Build Coastguard Worker return data()[index]; 204*d9f75844SAndroid Build Coastguard Worker } 205*d9f75844SAndroid Build Coastguard Worker begin()206*d9f75844SAndroid Build Coastguard Worker T* begin() { return data(); } end()207*d9f75844SAndroid Build Coastguard Worker T* end() { return data() + size(); } begin()208*d9f75844SAndroid Build Coastguard Worker const T* begin() const { return data(); } end()209*d9f75844SAndroid Build Coastguard Worker const T* end() const { return data() + size(); } cbegin()210*d9f75844SAndroid Build Coastguard Worker const T* cbegin() const { return data(); } cend()211*d9f75844SAndroid Build Coastguard Worker const T* cend() const { return data() + size(); } 212*d9f75844SAndroid Build Coastguard Worker 213*d9f75844SAndroid Build Coastguard Worker // The SetData functions replace the contents of the buffer. They accept the 214*d9f75844SAndroid Build Coastguard Worker // same input types as the constructors. 215*d9f75844SAndroid Build Coastguard Worker template <typename U, 216*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 217*d9f75844SAndroid Build Coastguard Worker internal::BufferCompat<T, U>::value>::type* = nullptr> SetData(const U * data,size_t size)218*d9f75844SAndroid Build Coastguard Worker void SetData(const U* data, size_t size) { 219*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 220*d9f75844SAndroid Build Coastguard Worker const size_t old_size = size_; 221*d9f75844SAndroid Build Coastguard Worker size_ = 0; 222*d9f75844SAndroid Build Coastguard Worker AppendData(data, size); 223*d9f75844SAndroid Build Coastguard Worker if (ZeroOnFree && size_ < old_size) { 224*d9f75844SAndroid Build Coastguard Worker ZeroTrailingData(old_size - size_); 225*d9f75844SAndroid Build Coastguard Worker } 226*d9f75844SAndroid Build Coastguard Worker } 227*d9f75844SAndroid Build Coastguard Worker 228*d9f75844SAndroid Build Coastguard Worker template <typename U, 229*d9f75844SAndroid Build Coastguard Worker size_t N, 230*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 231*d9f75844SAndroid Build Coastguard Worker internal::BufferCompat<T, U>::value>::type* = nullptr> SetData(const U (& array)[N])232*d9f75844SAndroid Build Coastguard Worker void SetData(const U (&array)[N]) { 233*d9f75844SAndroid Build Coastguard Worker SetData(array, N); 234*d9f75844SAndroid Build Coastguard Worker } 235*d9f75844SAndroid Build Coastguard Worker 236*d9f75844SAndroid Build Coastguard Worker template <typename W, 237*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 238*d9f75844SAndroid Build Coastguard Worker HasDataAndSize<const W, const T>::value>::type* = nullptr> SetData(const W & w)239*d9f75844SAndroid Build Coastguard Worker void SetData(const W& w) { 240*d9f75844SAndroid Build Coastguard Worker SetData(w.data(), w.size()); 241*d9f75844SAndroid Build Coastguard Worker } 242*d9f75844SAndroid Build Coastguard Worker 243*d9f75844SAndroid Build Coastguard Worker // Replaces the data in the buffer with at most `max_elements` of data, using 244*d9f75844SAndroid Build Coastguard Worker // the function `setter`, which should have the following signature: 245*d9f75844SAndroid Build Coastguard Worker // 246*d9f75844SAndroid Build Coastguard Worker // size_t setter(ArrayView<U> view) 247*d9f75844SAndroid Build Coastguard Worker // 248*d9f75844SAndroid Build Coastguard Worker // `setter` is given an appropriately typed ArrayView of length exactly 249*d9f75844SAndroid Build Coastguard Worker // `max_elements` that describes the area where it should write the data; it 250*d9f75844SAndroid Build Coastguard Worker // should return the number of elements actually written. (If it doesn't fill 251*d9f75844SAndroid Build Coastguard Worker // the whole ArrayView, it should leave the unused space at the end.) 252*d9f75844SAndroid Build Coastguard Worker template <typename U = T, 253*d9f75844SAndroid Build Coastguard Worker typename F, 254*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 255*d9f75844SAndroid Build Coastguard Worker internal::BufferCompat<T, U>::value>::type* = nullptr> SetData(size_t max_elements,F && setter)256*d9f75844SAndroid Build Coastguard Worker size_t SetData(size_t max_elements, F&& setter) { 257*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 258*d9f75844SAndroid Build Coastguard Worker const size_t old_size = size_; 259*d9f75844SAndroid Build Coastguard Worker size_ = 0; 260*d9f75844SAndroid Build Coastguard Worker const size_t written = AppendData<U>(max_elements, std::forward<F>(setter)); 261*d9f75844SAndroid Build Coastguard Worker if (ZeroOnFree && size_ < old_size) { 262*d9f75844SAndroid Build Coastguard Worker ZeroTrailingData(old_size - size_); 263*d9f75844SAndroid Build Coastguard Worker } 264*d9f75844SAndroid Build Coastguard Worker return written; 265*d9f75844SAndroid Build Coastguard Worker } 266*d9f75844SAndroid Build Coastguard Worker 267*d9f75844SAndroid Build Coastguard Worker // The AppendData functions add data to the end of the buffer. They accept 268*d9f75844SAndroid Build Coastguard Worker // the same input types as the constructors. 269*d9f75844SAndroid Build Coastguard Worker template <typename U, 270*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 271*d9f75844SAndroid Build Coastguard Worker internal::BufferCompat<T, U>::value>::type* = nullptr> AppendData(const U * data,size_t size)272*d9f75844SAndroid Build Coastguard Worker void AppendData(const U* data, size_t size) { 273*d9f75844SAndroid Build Coastguard Worker if (size == 0) { 274*d9f75844SAndroid Build Coastguard Worker return; 275*d9f75844SAndroid Build Coastguard Worker } 276*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(data); 277*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 278*d9f75844SAndroid Build Coastguard Worker const size_t new_size = size_ + size; 279*d9f75844SAndroid Build Coastguard Worker EnsureCapacityWithHeadroom(new_size, true); 280*d9f75844SAndroid Build Coastguard Worker static_assert(sizeof(T) == sizeof(U), ""); 281*d9f75844SAndroid Build Coastguard Worker std::memcpy(data_.get() + size_, data, size * sizeof(U)); 282*d9f75844SAndroid Build Coastguard Worker size_ = new_size; 283*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 284*d9f75844SAndroid Build Coastguard Worker } 285*d9f75844SAndroid Build Coastguard Worker 286*d9f75844SAndroid Build Coastguard Worker template <typename U, 287*d9f75844SAndroid Build Coastguard Worker size_t N, 288*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 289*d9f75844SAndroid Build Coastguard Worker internal::BufferCompat<T, U>::value>::type* = nullptr> AppendData(const U (& array)[N])290*d9f75844SAndroid Build Coastguard Worker void AppendData(const U (&array)[N]) { 291*d9f75844SAndroid Build Coastguard Worker AppendData(array, N); 292*d9f75844SAndroid Build Coastguard Worker } 293*d9f75844SAndroid Build Coastguard Worker 294*d9f75844SAndroid Build Coastguard Worker template <typename W, 295*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 296*d9f75844SAndroid Build Coastguard Worker HasDataAndSize<const W, const T>::value>::type* = nullptr> AppendData(const W & w)297*d9f75844SAndroid Build Coastguard Worker void AppendData(const W& w) { 298*d9f75844SAndroid Build Coastguard Worker AppendData(w.data(), w.size()); 299*d9f75844SAndroid Build Coastguard Worker } 300*d9f75844SAndroid Build Coastguard Worker 301*d9f75844SAndroid Build Coastguard Worker template <typename U, 302*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 303*d9f75844SAndroid Build Coastguard Worker internal::BufferCompat<T, U>::value>::type* = nullptr> AppendData(const U & item)304*d9f75844SAndroid Build Coastguard Worker void AppendData(const U& item) { 305*d9f75844SAndroid Build Coastguard Worker AppendData(&item, 1); 306*d9f75844SAndroid Build Coastguard Worker } 307*d9f75844SAndroid Build Coastguard Worker 308*d9f75844SAndroid Build Coastguard Worker // Appends at most `max_elements` to the end of the buffer, using the function 309*d9f75844SAndroid Build Coastguard Worker // `setter`, which should have the following signature: 310*d9f75844SAndroid Build Coastguard Worker // 311*d9f75844SAndroid Build Coastguard Worker // size_t setter(ArrayView<U> view) 312*d9f75844SAndroid Build Coastguard Worker // 313*d9f75844SAndroid Build Coastguard Worker // `setter` is given an appropriately typed ArrayView of length exactly 314*d9f75844SAndroid Build Coastguard Worker // `max_elements` that describes the area where it should write the data; it 315*d9f75844SAndroid Build Coastguard Worker // should return the number of elements actually written. (If it doesn't fill 316*d9f75844SAndroid Build Coastguard Worker // the whole ArrayView, it should leave the unused space at the end.) 317*d9f75844SAndroid Build Coastguard Worker template <typename U = T, 318*d9f75844SAndroid Build Coastguard Worker typename F, 319*d9f75844SAndroid Build Coastguard Worker typename std::enable_if< 320*d9f75844SAndroid Build Coastguard Worker internal::BufferCompat<T, U>::value>::type* = nullptr> AppendData(size_t max_elements,F && setter)321*d9f75844SAndroid Build Coastguard Worker size_t AppendData(size_t max_elements, F&& setter) { 322*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 323*d9f75844SAndroid Build Coastguard Worker const size_t old_size = size_; 324*d9f75844SAndroid Build Coastguard Worker SetSize(old_size + max_elements); 325*d9f75844SAndroid Build Coastguard Worker U* base_ptr = data<U>() + old_size; 326*d9f75844SAndroid Build Coastguard Worker size_t written_elements = setter(rtc::ArrayView<U>(base_ptr, max_elements)); 327*d9f75844SAndroid Build Coastguard Worker 328*d9f75844SAndroid Build Coastguard Worker RTC_CHECK_LE(written_elements, max_elements); 329*d9f75844SAndroid Build Coastguard Worker size_ = old_size + written_elements; 330*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 331*d9f75844SAndroid Build Coastguard Worker return written_elements; 332*d9f75844SAndroid Build Coastguard Worker } 333*d9f75844SAndroid Build Coastguard Worker 334*d9f75844SAndroid Build Coastguard Worker // Sets the size of the buffer. If the new size is smaller than the old, the 335*d9f75844SAndroid Build Coastguard Worker // buffer contents will be kept but truncated; if the new size is greater, 336*d9f75844SAndroid Build Coastguard Worker // the existing contents will be kept and the new space will be 337*d9f75844SAndroid Build Coastguard Worker // uninitialized. SetSize(size_t size)338*d9f75844SAndroid Build Coastguard Worker void SetSize(size_t size) { 339*d9f75844SAndroid Build Coastguard Worker const size_t old_size = size_; 340*d9f75844SAndroid Build Coastguard Worker EnsureCapacityWithHeadroom(size, true); 341*d9f75844SAndroid Build Coastguard Worker size_ = size; 342*d9f75844SAndroid Build Coastguard Worker if (ZeroOnFree && size_ < old_size) { 343*d9f75844SAndroid Build Coastguard Worker ZeroTrailingData(old_size - size_); 344*d9f75844SAndroid Build Coastguard Worker } 345*d9f75844SAndroid Build Coastguard Worker } 346*d9f75844SAndroid Build Coastguard Worker 347*d9f75844SAndroid Build Coastguard Worker // Ensure that the buffer size can be increased to at least capacity without 348*d9f75844SAndroid Build Coastguard Worker // further reallocation. (Of course, this operation might need to reallocate 349*d9f75844SAndroid Build Coastguard Worker // the buffer.) EnsureCapacity(size_t capacity)350*d9f75844SAndroid Build Coastguard Worker void EnsureCapacity(size_t capacity) { 351*d9f75844SAndroid Build Coastguard Worker // Don't allocate extra headroom, since the user is asking for a specific 352*d9f75844SAndroid Build Coastguard Worker // capacity. 353*d9f75844SAndroid Build Coastguard Worker EnsureCapacityWithHeadroom(capacity, false); 354*d9f75844SAndroid Build Coastguard Worker } 355*d9f75844SAndroid Build Coastguard Worker 356*d9f75844SAndroid Build Coastguard Worker // Resets the buffer to zero size without altering capacity. Works even if the 357*d9f75844SAndroid Build Coastguard Worker // buffer has been moved from. Clear()358*d9f75844SAndroid Build Coastguard Worker void Clear() { 359*d9f75844SAndroid Build Coastguard Worker MaybeZeroCompleteBuffer(); 360*d9f75844SAndroid Build Coastguard Worker size_ = 0; 361*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 362*d9f75844SAndroid Build Coastguard Worker } 363*d9f75844SAndroid Build Coastguard Worker 364*d9f75844SAndroid Build Coastguard Worker // Swaps two buffers. Also works for buffers that have been moved from. swap(BufferT & a,BufferT & b)365*d9f75844SAndroid Build Coastguard Worker friend void swap(BufferT& a, BufferT& b) { 366*d9f75844SAndroid Build Coastguard Worker using std::swap; 367*d9f75844SAndroid Build Coastguard Worker swap(a.size_, b.size_); 368*d9f75844SAndroid Build Coastguard Worker swap(a.capacity_, b.capacity_); 369*d9f75844SAndroid Build Coastguard Worker swap(a.data_, b.data_); 370*d9f75844SAndroid Build Coastguard Worker } 371*d9f75844SAndroid Build Coastguard Worker 372*d9f75844SAndroid Build Coastguard Worker private: EnsureCapacityWithHeadroom(size_t capacity,bool extra_headroom)373*d9f75844SAndroid Build Coastguard Worker void EnsureCapacityWithHeadroom(size_t capacity, bool extra_headroom) { 374*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 375*d9f75844SAndroid Build Coastguard Worker if (capacity <= capacity_) 376*d9f75844SAndroid Build Coastguard Worker return; 377*d9f75844SAndroid Build Coastguard Worker 378*d9f75844SAndroid Build Coastguard Worker // If the caller asks for extra headroom, ensure that the new capacity is 379*d9f75844SAndroid Build Coastguard Worker // >= 1.5 times the old capacity. Any constant > 1 is sufficient to prevent 380*d9f75844SAndroid Build Coastguard Worker // quadratic behavior; as to why we pick 1.5 in particular, see 381*d9f75844SAndroid Build Coastguard Worker // https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md and 382*d9f75844SAndroid Build Coastguard Worker // http://www.gahcep.com/cpp-internals-stl-vector-part-1/. 383*d9f75844SAndroid Build Coastguard Worker const size_t new_capacity = 384*d9f75844SAndroid Build Coastguard Worker extra_headroom ? std::max(capacity, capacity_ + capacity_ / 2) 385*d9f75844SAndroid Build Coastguard Worker : capacity; 386*d9f75844SAndroid Build Coastguard Worker 387*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<T[]> new_data(new T[new_capacity]); 388*d9f75844SAndroid Build Coastguard Worker if (data_ != nullptr) { 389*d9f75844SAndroid Build Coastguard Worker std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T)); 390*d9f75844SAndroid Build Coastguard Worker } 391*d9f75844SAndroid Build Coastguard Worker MaybeZeroCompleteBuffer(); 392*d9f75844SAndroid Build Coastguard Worker data_ = std::move(new_data); 393*d9f75844SAndroid Build Coastguard Worker capacity_ = new_capacity; 394*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 395*d9f75844SAndroid Build Coastguard Worker } 396*d9f75844SAndroid Build Coastguard Worker 397*d9f75844SAndroid Build Coastguard Worker // Zero the complete buffer if template argument "ZeroOnFree" is true. MaybeZeroCompleteBuffer()398*d9f75844SAndroid Build Coastguard Worker void MaybeZeroCompleteBuffer() { 399*d9f75844SAndroid Build Coastguard Worker if (ZeroOnFree && capacity_ > 0) { 400*d9f75844SAndroid Build Coastguard Worker // It would be sufficient to only zero "size_" elements, as all other 401*d9f75844SAndroid Build Coastguard Worker // methods already ensure that the unused capacity contains no sensitive 402*d9f75844SAndroid Build Coastguard Worker // data---but better safe than sorry. 403*d9f75844SAndroid Build Coastguard Worker ExplicitZeroMemory(data_.get(), capacity_ * sizeof(T)); 404*d9f75844SAndroid Build Coastguard Worker } 405*d9f75844SAndroid Build Coastguard Worker } 406*d9f75844SAndroid Build Coastguard Worker 407*d9f75844SAndroid Build Coastguard Worker // Zero the first "count" elements of unused capacity. ZeroTrailingData(size_t count)408*d9f75844SAndroid Build Coastguard Worker void ZeroTrailingData(size_t count) { 409*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsConsistent()); 410*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_LE(count, capacity_ - size_); 411*d9f75844SAndroid Build Coastguard Worker ExplicitZeroMemory(data_.get() + size_, count * sizeof(T)); 412*d9f75844SAndroid Build Coastguard Worker } 413*d9f75844SAndroid Build Coastguard Worker 414*d9f75844SAndroid Build Coastguard Worker // Precondition for all methods except Clear, operator= and the destructor. 415*d9f75844SAndroid Build Coastguard Worker // Postcondition for all methods except move construction and move 416*d9f75844SAndroid Build Coastguard Worker // assignment, which leave the moved-from object in a possibly inconsistent 417*d9f75844SAndroid Build Coastguard Worker // state. IsConsistent()418*d9f75844SAndroid Build Coastguard Worker bool IsConsistent() const { 419*d9f75844SAndroid Build Coastguard Worker return (data_ || capacity_ == 0) && capacity_ >= size_; 420*d9f75844SAndroid Build Coastguard Worker } 421*d9f75844SAndroid Build Coastguard Worker 422*d9f75844SAndroid Build Coastguard Worker // Called when *this has been moved from. Conceptually it's a no-op, but we 423*d9f75844SAndroid Build Coastguard Worker // can mutate the state slightly to help subsequent sanity checks catch bugs. OnMovedFrom()424*d9f75844SAndroid Build Coastguard Worker void OnMovedFrom() { 425*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(!data_); // Our heap block should have been stolen. 426*d9f75844SAndroid Build Coastguard Worker #if RTC_DCHECK_IS_ON 427*d9f75844SAndroid Build Coastguard Worker // Ensure that *this is always inconsistent, to provoke bugs. 428*d9f75844SAndroid Build Coastguard Worker size_ = 1; 429*d9f75844SAndroid Build Coastguard Worker capacity_ = 0; 430*d9f75844SAndroid Build Coastguard Worker #else 431*d9f75844SAndroid Build Coastguard Worker // Make *this consistent and empty. Shouldn't be necessary, but better safe 432*d9f75844SAndroid Build Coastguard Worker // than sorry. 433*d9f75844SAndroid Build Coastguard Worker size_ = 0; 434*d9f75844SAndroid Build Coastguard Worker capacity_ = 0; 435*d9f75844SAndroid Build Coastguard Worker #endif 436*d9f75844SAndroid Build Coastguard Worker } 437*d9f75844SAndroid Build Coastguard Worker 438*d9f75844SAndroid Build Coastguard Worker size_t size_; 439*d9f75844SAndroid Build Coastguard Worker size_t capacity_; 440*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<T[]> data_; 441*d9f75844SAndroid Build Coastguard Worker }; 442*d9f75844SAndroid Build Coastguard Worker 443*d9f75844SAndroid Build Coastguard Worker // By far the most common sort of buffer. 444*d9f75844SAndroid Build Coastguard Worker using Buffer = BufferT<uint8_t>; 445*d9f75844SAndroid Build Coastguard Worker 446*d9f75844SAndroid Build Coastguard Worker // A buffer that zeros memory before releasing it. 447*d9f75844SAndroid Build Coastguard Worker template <typename T> 448*d9f75844SAndroid Build Coastguard Worker using ZeroOnFreeBuffer = BufferT<T, true>; 449*d9f75844SAndroid Build Coastguard Worker 450*d9f75844SAndroid Build Coastguard Worker } // namespace rtc 451*d9f75844SAndroid Build Coastguard Worker 452*d9f75844SAndroid Build Coastguard Worker #endif // RTC_BASE_BUFFER_H_ 453