/* * 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 #include #include #include #include namespace webrtc { namespace bounded_inline_vector_impl { template 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 using AllTrue = std::is_same, BoolPack>; template using AllConvertible = AllTrue::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 void InitializeElements(T* data) {} template void InitializeElements(T* data, U&& element, Us&&... elements) { // Placement new, because we construct a new object in uninitialized memory. ::new (data) T(std::forward(element)); InitializeElements(data + 1, std::forward(elements)...); } // Default initializes uninitialized array elements. // TODO(kwiberg): Replace with std::uninitialized_default_construct_n() (C++17). template 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 void CopyElements(const T* src_data, int src_size, T* dst_data, int* dst_size) { if /*constexpr*/ (std::is_trivially_copy_constructible::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 void MoveElements(T* src_data, int src_size, T* dst_data, int* dst_size) { if /*constexpr*/ (std::is_trivially_move_constructible::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 void DestroyElements(T* data, int size) { if /*constexpr*/ (!std::is_trivially_destructible::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 ::value, bool is_small = (sizeof(T) * fixed_capacity <= kSmallSize)> struct Storage { static_assert(!std::is_trivial::value, ""); template < typename... Ts, typename std::enable_if_t::value>* = nullptr> explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) { InitializeElements(data, std::forward(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 struct Storage { static_assert(std::is_trivial::value, ""); static_assert(sizeof(T) * fixed_capacity <= kSmallSize, ""); template < typename... Ts, typename std::enable_if_t::value>* = nullptr> explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) { InitializeElements(data, std::forward(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 struct Storage { static_assert(std::is_trivial::value, ""); static_assert(sizeof(T) * fixed_capacity > kSmallSize, ""); template < typename... Ts, typename std::enable_if_t::value>* = nullptr> explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) { InitializeElements(data, std::forward(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_