123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- /*
- * Copyright 2020 The WebRTC Project Authors. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
- #ifndef RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_
- #define RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_
- #include <stdint.h>
- #include <cstring>
- #include <memory>
- #include <type_traits>
- #include <utility>
- namespace webrtc {
- namespace bounded_inline_vector_impl {
- template <bool...>
- struct BoolPack;
- // Tests if all its parameters (x0, x1, ..., xn) are true. The implementation
- // checks whether (x0, x1, ..., xn, true) == (true, x0, x1, ..., xn), which is
- // true iff true == x0 && x0 == x1 && x1 == x2 ... && xn-1 == xn && xn == true.
- template <bool... Bs>
- using AllTrue = std::is_same<BoolPack<Bs..., true>, BoolPack<true, Bs...>>;
- template <typename To, typename... Froms>
- using AllConvertible = AllTrue<std::is_convertible<Froms, To>::value...>;
- // Initializes part of an uninitialized array. Unlike normal array
- // initialization, does not zero the remaining array elements. Caller is
- // responsible for ensuring that there is enough space in `data`.
- template <typename T>
- void InitializeElements(T* data) {}
- template <typename T, typename U, typename... Us>
- void InitializeElements(T* data, U&& element, Us&&... elements) {
- // Placement new, because we construct a new object in uninitialized memory.
- ::new (data) T(std::forward<U>(element));
- InitializeElements(data + 1, std::forward<Us>(elements)...);
- }
- // Default initializes uninitialized array elements.
- // TODO(kwiberg): Replace with std::uninitialized_default_construct_n() (C++17).
- template <typename T>
- void DefaultInitializeElements(T* data, int size) {
- for (int i = 0; i < size; ++i) {
- // Placement new, because we construct a new object in uninitialized memory.
- ::new (&data[i]) T;
- }
- }
- // Copies from source to uninitialized destination. Caller is responsible for
- // ensuring that there is enough space in `dst_data`.
- template <typename T>
- void CopyElements(const T* src_data, int src_size, T* dst_data, int* dst_size) {
- if /*constexpr*/ (std::is_trivially_copy_constructible<T>::value) {
- std::memcpy(dst_data, src_data, src_size * sizeof(T));
- } else {
- std::uninitialized_copy_n(src_data, src_size, dst_data);
- }
- *dst_size = src_size;
- }
- // Moves from source to uninitialized destination. Caller is responsible for
- // ensuring that there is enough space in `dst_data`.
- template <typename T>
- void MoveElements(T* src_data, int src_size, T* dst_data, int* dst_size) {
- if /*constexpr*/ (std::is_trivially_move_constructible<T>::value) {
- std::memcpy(dst_data, src_data, src_size * sizeof(T));
- } else {
- // TODO(kwiberg): Use std::uninitialized_move_n() instead (C++17).
- for (int i = 0; i < src_size; ++i) {
- // Placement new, because we create a new object in uninitialized
- // memory.
- ::new (&dst_data[i]) T(std::move(src_data[i]));
- }
- }
- *dst_size = src_size;
- }
- // Destroys elements, leaving them uninitialized.
- template <typename T>
- void DestroyElements(T* data, int size) {
- if /*constexpr*/ (!std::is_trivially_destructible<T>::value) {
- for (int i = 0; i < size; ++i) {
- data[i].~T();
- }
- }
- }
- // If elements are trivial and the total capacity is at most this many bytes,
- // copy everything instead of just the elements that are in use; this is more
- // efficient, and makes BoundedInlineVector trivially copyable.
- static constexpr int kSmallSize = 64;
- // Storage implementations.
- //
- // There are diferent Storage structs for diferent kinds of element types. The
- // common contract is the following:
- //
- // * They have public `size` variables and `data` array members.
- //
- // * Their owner is responsible for enforcing the invariant that the first
- // `size` elements in `data` are initialized, and the remaining elements are
- // not initialized.
- //
- // * They implement default construction, construction with one or more
- // elements, copy/move construction, copy/move assignment, and destruction;
- // the owner must ensure that the invariant holds whenever these operations
- // occur.
- // Storage implementation for nontrivial element types.
- template <typename T,
- int fixed_capacity,
- bool is_trivial = std::is_trivial<T>::value,
- bool is_small = (sizeof(T) * fixed_capacity <= kSmallSize)>
- struct Storage {
- static_assert(!std::is_trivial<T>::value, "");
- template <
- typename... Ts,
- typename std::enable_if_t<AllConvertible<T, Ts...>::value>* = nullptr>
- explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) {
- InitializeElements(data, std::forward<Ts>(elements)...);
- }
- Storage(const Storage& other) {
- CopyElements(other.data, other.size, data, &size);
- }
- Storage(Storage&& other) {
- MoveElements(other.data, other.size, data, &size);
- }
- Storage& operator=(const Storage& other) {
- if (this != &other) {
- DestroyElements(data, size);
- CopyElements(other.data, other.size, data, &size);
- }
- return *this;
- }
- Storage& operator=(Storage&& other) {
- DestroyElements(data, size);
- size = 0; // Needed in case of self assignment.
- MoveElements(other.data, other.size, data, &size);
- return *this;
- }
- ~Storage() { DestroyElements(data, size); }
- int size;
- union {
- // Since this array is in a union, we get to construct and destroy it
- // manually.
- T data[fixed_capacity]; // NOLINT(runtime/arrays)
- };
- };
- // Storage implementation for trivial element types when the capacity is small
- // enough that we can cheaply copy everything.
- template <typename T, int fixed_capacity>
- struct Storage<T, fixed_capacity, /*is_trivial=*/true, /*is_small=*/true> {
- static_assert(std::is_trivial<T>::value, "");
- static_assert(sizeof(T) * fixed_capacity <= kSmallSize, "");
- template <
- typename... Ts,
- typename std::enable_if_t<AllConvertible<T, Ts...>::value>* = nullptr>
- explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) {
- InitializeElements(data, std::forward<Ts>(elements)...);
- }
- Storage(const Storage&) = default;
- Storage& operator=(const Storage&) = default;
- ~Storage() = default;
- int size;
- T data[fixed_capacity]; // NOLINT(runtime/arrays)
- };
- // Storage implementation for trivial element types when the capacity is large
- // enough that we want to avoid copying uninitialized elements.
- template <typename T, int fixed_capacity>
- struct Storage<T, fixed_capacity, /*is_trivial=*/true, /*is_small=*/false> {
- static_assert(std::is_trivial<T>::value, "");
- static_assert(sizeof(T) * fixed_capacity > kSmallSize, "");
- template <
- typename... Ts,
- typename std::enable_if_t<AllConvertible<T, Ts...>::value>* = nullptr>
- explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) {
- InitializeElements(data, std::forward<Ts>(elements)...);
- }
- Storage(const Storage& other) : size(other.size) {
- std::memcpy(data, other.data, other.size * sizeof(T));
- }
- Storage& operator=(const Storage& other) {
- if (this != &other) {
- size = other.size;
- std::memcpy(data, other.data, other.size * sizeof(T));
- }
- return *this;
- }
- ~Storage() = default;
- int size;
- union {
- T data[fixed_capacity]; // NOLINT(runtime/arrays)
- };
- };
- } // namespace bounded_inline_vector_impl
- } // namespace webrtc
- #endif // RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_
|