buffer.h 14 KB

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