blob: ab5249444be47ce1786de3ba5c02a7d283ec7acc [file] [log] [blame]
Karl Wibergc1269372020-03-02 20:23:41 +01001/*
2 * Copyright 2020 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 RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_
12#define RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_
13
14#include <stdint.h>
15
16#include <cstring>
17#include <memory>
18#include <type_traits>
19#include <utility>
20
21namespace webrtc {
22namespace bounded_inline_vector_impl {
23
24template <bool...>
25struct BoolPack;
26
27// Tests if all its parameters (x0, x1, ..., xn) are true. The implementation
28// checks whether (x0, x1, ..., xn, true) == (true, x0, x1, ..., xn), which is
29// true iff true == x0 && x0 == x1 && x1 == x2 ... && xn-1 == xn && xn == true.
30template <bool... Bs>
31using AllTrue = std::is_same<BoolPack<Bs..., true>, BoolPack<true, Bs...>>;
32
33template <typename To, typename... Froms>
34using AllConvertible = AllTrue<std::is_convertible<Froms, To>::value...>;
35
36// Initializes part of an uninitialized array. Unlike normal array
37// initialization, does not zero the remaining array elements. Caller is
38// responsible for ensuring that there is enough space in `data`.
39template <typename T>
40void InitializeElements(T* data) {}
41template <typename T, typename U, typename... Us>
42void InitializeElements(T* data, U&& element, Us&&... elements) {
43 // Placement new, because we construct a new object in uninitialized memory.
44 ::new (data) T(std::forward<U>(element));
45 InitializeElements(data + 1, std::forward<Us>(elements)...);
46}
47
48// Copies from source to uninitialized destination. Caller is responsible for
49// ensuring that there is enough space in `dst_data`.
50template <typename T>
51void CopyElements(const T* src_data, int src_size, T* dst_data, int* dst_size) {
52 if /*constexpr*/ (std::is_trivially_copy_constructible<T>::value) {
53 std::memcpy(dst_data, src_data, src_size * sizeof(T));
54 } else {
55 std::uninitialized_copy_n(src_data, src_size, dst_data);
56 }
57 *dst_size = src_size;
58}
59
60// Moves from source to uninitialized destination. Caller is responsible for
61// ensuring that there is enough space in `dst_data`.
62template <typename T>
63void MoveElements(T* src_data, int src_size, T* dst_data, int* dst_size) {
64 if /*constexpr*/ (std::is_trivially_move_constructible<T>::value) {
65 std::memcpy(dst_data, src_data, src_size * sizeof(T));
66 } else {
67 // TODO(kwiberg): Use std::uninitialized_move_n() instead (C++17).
68 for (int i = 0; i < src_size; ++i) {
69 // Placement new, because we create a new object in uninitialized
70 // memory.
71 ::new (&dst_data[i]) T(std::move(src_data[i]));
72 }
73 }
74 *dst_size = src_size;
75}
76
77// Destroys elements, leaving them uninitialized.
78template <typename T>
79void DestroyElements(T* data, int size) {
80 if /*constexpr*/ (!std::is_trivially_destructible<T>::value) {
81 for (int i = 0; i < size; ++i) {
82 data[i].~T();
83 }
84 }
85}
86
87// If elements are trivial and the total capacity is at most this many bytes,
88// copy everything instead of just the elements that are in use; this is more
89// efficient, and makes BoundedInlineVector trivially copyable.
90static constexpr int kSmallSize = 64;
91
92// Storage implementations.
93//
94// There are diferent Storage structs for diferent kinds of element types. The
95// common contract is the following:
96//
97// * They have public `size` variables and `data` array members.
98//
99// * Their owner is responsible for enforcing the invariant that the first
100// `size` elements in `data` are initialized, and the remaining elements are
101// not initialized.
102//
103// * They implement default construction, construction with one or more
104// elements, copy/move construction, copy/move assignment, and destruction;
105// the owner must ensure that the invariant holds whenever these operations
106// occur.
107
108// Storage implementation for nontrivial element types.
109template <typename T,
110 int fixed_capacity,
111 bool is_trivial = std::is_trivial<T>::value,
112 bool is_small = (sizeof(T) * fixed_capacity <= kSmallSize)>
113struct Storage {
114 static_assert(!std::is_trivial<T>::value, "");
115
116 template <
117 typename... Ts,
118 typename std::enable_if_t<AllConvertible<T, Ts...>::value>* = nullptr>
119 explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) {
120 InitializeElements(data, std::forward<Ts>(elements)...);
121 }
122
123 Storage(const Storage& other) {
124 CopyElements(other.data, other.size, data, &size);
125 }
126
127 Storage(Storage&& other) {
128 MoveElements(other.data, other.size, data, &size);
129 }
130
131 Storage& operator=(const Storage& other) {
132 if (this != &other) {
133 DestroyElements(data, size);
134 CopyElements(other.data, other.size, data, &size);
135 }
136 return *this;
137 }
138
139 Storage& operator=(Storage&& other) {
140 DestroyElements(data, size);
141 size = 0; // Needed in case of self assignment.
142 MoveElements(other.data, other.size, data, &size);
143 return *this;
144 }
145
146 ~Storage() { DestroyElements(data, size); }
147
148 int size;
149 union {
150 // Since this array is in a union, we get to construct and destroy it
151 // manually.
152 T data[fixed_capacity]; // NOLINT(runtime/arrays)
153 };
154};
155
156// Storage implementation for trivial element types when the capacity is small
157// enough that we can cheaply copy everything.
158template <typename T, int fixed_capacity>
159struct Storage<T, fixed_capacity, /*is_trivial=*/true, /*is_small=*/true> {
160 static_assert(std::is_trivial<T>::value, "");
161 static_assert(sizeof(T) * fixed_capacity <= kSmallSize, "");
162
163 template <
164 typename... Ts,
165 typename std::enable_if_t<AllConvertible<T, Ts...>::value>* = nullptr>
166 explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) {
167 InitializeElements(data, std::forward<Ts>(elements)...);
168 }
169
170 Storage(const Storage&) = default;
171 Storage& operator=(const Storage&) = default;
172 ~Storage() = default;
173
174 int size;
175 T data[fixed_capacity]; // NOLINT(runtime/arrays)
176};
177
178// Storage implementation for trivial element types when the capacity is large
179// enough that we want to avoid copying uninitialized elements.
180template <typename T, int fixed_capacity>
181struct Storage<T, fixed_capacity, /*is_trivial=*/true, /*is_small=*/false> {
182 static_assert(std::is_trivial<T>::value, "");
183 static_assert(sizeof(T) * fixed_capacity > kSmallSize, "");
184
185 template <
186 typename... Ts,
187 typename std::enable_if_t<AllConvertible<T, Ts...>::value>* = nullptr>
188 explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) {
189 InitializeElements(data, std::forward<Ts>(elements)...);
190 }
191
192 Storage(const Storage& other) : size(other.size) {
193 std::memcpy(data, other.data, other.size * sizeof(T));
194 }
195
196 Storage& operator=(const Storage& other) {
197 if (this != &other) {
198 size = other.size;
199 std::memcpy(data, other.data, other.size * sizeof(T));
200 }
201 return *this;
202 }
203
204 ~Storage() = default;
205
206 int size;
207 union {
208 T data[fixed_capacity]; // NOLINT(runtime/arrays)
209 };
210};
211
212} // namespace bounded_inline_vector_impl
213} // namespace webrtc
214
215#endif // RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_