2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_UTIL_TINYVECTOR_H_
18 #define incl_HPHP_UTIL_TINYVECTOR_H_
22 #include <type_traits>
24 #include <boost/iterator/iterator_facade.hpp>
25 #include <folly/portability/Malloc.h>
27 #include "hphp/util/alloc.h"
28 #include "hphp/util/assertions.h"
29 #include "hphp/util/compact-tagged-ptrs.h"
34 //////////////////////////////////////////////////////////////////////
37 * Simple, small buffer-optimized sequence of T.
41 * - Unlike std::vector, storage is not guaranteed to be contiguous.
43 * - Insertion and removal can only happen at the end of the
44 * sequence (although you can modify elements anywhere in the
47 * - There is no non-const iterator support, and we have forward
50 * - The elements must be trivially copyable/assignable and have
51 * trivial destructors.
53 * Currently, does not invalidate pointers or references to elements
54 * at indexes less than InternalSize, but we don't want to depend on
55 * this without documenting it here. Iterators should be considered
56 * invalidated after any mutation.
58 * Also: if you have a T*, and the list almost always contains at most
59 * one element, PointerList is a smaller object so may be preferable
60 * in that case. (Unless you want the first element to remain
61 * accessible inline instead of moved to the heap.)
64 // Allocator interface with an additional `usable_size()` API.
66 struct TinyVectorMallocAllocator
{
67 using size_type
= std::size_t;
69 template <typename U
> struct rebind
{
70 using other
= TinyVectorMallocAllocator
<U
>;
73 void* allocate(std::size_t size
) const { return malloc(size
); }
74 void deallocate(void* ptr
, size_t) const { free(ptr
); }
75 std::size_t usable_size(void* ptr
, std::size_t /*size*/) const {
76 return malloc_usable_size(ptr
);
81 size_t InternalSize
= 1,
82 size_t MinHeapCapacity
= 0,
83 typename OrigAllocator
= std::allocator
<char>>
85 static_assert(std::is_trivially_destructible
<T
>::value
,
86 "TinyVector only supports elements with trivial destructors");
87 static_assert(std::is_trivially_copy_constructible
<T
>::value
&&
88 std::is_trivially_copy_assignable
<T
>::value
,
89 "TinyVector only supports elements with trivial copy "
90 "constructors and trivial assignment operators");
91 static_assert(InternalSize
>= 1,
92 "TinyVector assumes that the internal size is at least 1");
95 * We need to see if the allocator implements a member named `usable_size`.
96 * If it does, we assume it is a method like
97 * size_type Alloc::usable_size(pointer, size_type);
99 template <typename Alloc
> struct alloc_traits
: std::allocator_traits
<Alloc
> {
100 using typename
std::allocator_traits
<Alloc
>::size_type
;
103 template <typename AT
, decltype(&AT::usable_size
)* = nullptr>
105 template <typename AT
= Alloc
>
106 static std::true_type
helper(valid_type
<AT
>*);
107 template <typename AT
= Alloc
> static std::false_type
helper(...);
108 static constexpr bool has_usable_size
=
109 std::is_same
<std::true_type
, decltype(helper(nullptr))>::value
;
111 template<typename AT
= Alloc
> static size_type
usable_size_impl(
112 typename
std::enable_if
<alloc_traits
<AT
>::has_usable_size
, AT
>::type
& a
,
113 void* ptr
, size_type size
) {
114 return a
.usable_size(ptr
, size
);
116 template<typename AT
= Alloc
> static size_type
usable_size_impl(
117 typename
std::enable_if
<!alloc_traits
<AT
>::has_usable_size
, AT
>::type
&,
118 void*, size_type size
) {
122 static size_type
usable_size(Alloc
& a
, void* ptr
, size_type size
) {
123 return usable_size_impl(a
, ptr
, size
);
127 using Allocator
= typename
OrigAllocator::template rebind
<uint8_t>::other
;
128 using AT
= alloc_traits
<Allocator
>;
129 using AllocPtr
= typename
AT::pointer
;
131 struct const_iterator
;
134 ~TinyVector() { clear(); }
136 TinyVector(const TinyVector
&) = delete;
137 TinyVector
& operator=(const TinyVector
&) = delete;
139 size_t size() const { return m_impl
.m_data
.size(); }
140 bool empty() const { return !size(); }
142 const_iterator
begin() const { return const_iterator(this, 0); }
143 const_iterator
end() const { return const_iterator(this); }
145 T
& operator[](size_t index
) {
146 assert(index
< size());
147 return *location(index
);
150 const T
& operator[](size_t index
) const {
151 return const_cast<TinyVector
*>(this)->operator[](index
);
155 if (HeapData
* p
= m_impl
.m_data
.ptr()) {
156 alloc_traits
<Impl
>::deallocate(m_impl
, reinterpret_cast<AllocPtr
>(p
),
157 allocSize(p
->capacity
));
159 m_impl
.m_data
.set(0, 0);
162 void push_back(const T
& t
) {
167 * Increase the size of this TinyVector by 1 and return a reference to the
168 * new object, which will be uninitialized.
171 size_t current
= size();
172 reserve(current
+ 1);
173 m_impl
.m_data
.set(current
+ 1, m_impl
.m_data
.ptr());
179 m_impl
.m_data
.set(size() - 1, m_impl
.m_data
.ptr());
184 return (*this)[size() - 1];
187 const T
& back() const {
189 return (*this)[size() - 1];
194 return m_impl
.m_vals
[0];
197 const T
& front() const {
199 return m_impl
.m_vals
[0];
202 void reserve(size_t sz
) {
203 if (sz
< InternalSize
) return;
205 const size_t currentHeap
=
206 m_impl
.m_data
.ptr() ? m_impl
.m_data
.ptr()->capacity
: 0;
207 const size_t neededHeap
= sz
- InternalSize
;
208 if (neededHeap
<= currentHeap
) {
212 const size_t newCapacity
= std::max(
213 currentHeap
? currentHeap
* 4 / 3
214 : std::max(neededHeap
, MinHeapCapacity
),
216 const size_t requested
= allocSize(newCapacity
);
217 auto newHeap
= reinterpret_cast<HeapData
*>(AT::allocate(m_impl
, requested
));
218 const size_t usableSize
= AT::usable_size(m_impl
, newHeap
, requested
);
219 newHeap
->capacity
= (usableSize
- offsetof(HeapData
, vals
)) / sizeof(T
);
221 if (HeapData
* p
= m_impl
.m_data
.ptr()) {
222 std::copy(&m_impl
.m_data
.ptr()->vals
[0],
223 &m_impl
.m_data
.ptr()->vals
[size() - InternalSize
],
225 AT::deallocate(m_impl
, reinterpret_cast<AllocPtr
>(p
),
226 allocSize(p
->capacity
));
228 m_impl
.m_data
.set(size(), newHeap
);
231 template<size_t isize
, size_t minheap
, typename origalloc
>
232 bool operator==(const TinyVector
<T
, isize
, minheap
, origalloc
>& tv
) const {
233 if (size() != tv
.size()) return false;
235 for (size_t i
= 0; i
< size(); ++i
) {
236 if ((*this)[i
] != tv
[i
]) return false;
243 uint32_t capacity
; // numbers of vals---excludes this capacity field
247 static constexpr std::size_t allocSize(uint32_t capacity
) {
248 return sizeof(HeapData
) + sizeof(T
) * capacity
;
251 T
* location(size_t index
) {
252 return index
< InternalSize
253 ? &m_impl
.m_vals
[index
]
254 : &m_impl
.m_data
.ptr()->vals
[index
- InternalSize
];
258 struct Impl
: Allocator
{
259 CompactSizedPtr
<HeapData
> m_data
;
260 T m_vals
[InternalSize
];
265 //////////////////////////////////////////////////////////////////////
269 size_t MinHeapCapacity
,
271 struct TinyVector
<T
,InternalSize
,MinHeapCapacity
,Allocator
>::const_iterator
272 : boost::iterator_facade
<const_iterator
,
274 boost::forward_traversal_tag
>
276 explicit const_iterator(const TinyVector
* p
, uint32_t idx
)
281 explicit const_iterator(const TinyVector
* p
)
287 friend class boost::iterator_core_access
;
293 bool equal(const const_iterator
& o
) const {
294 assert(m_p
== o
.m_p
);
295 return m_idx
== o
.m_idx
;
298 const T
& dereference() const {
299 return (*m_p
)[m_idx
];
303 const TinyVector
* m_p
;
307 //////////////////////////////////////////////////////////////////////