1 // Copyright (c) 2015-2016 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 #ifndef _BITCOIN_PREVECTOR_H_
6 #define _BITCOIN_PREVECTOR_H_
14 #include <type_traits>
17 /** Implements a drop-in replacement for std::vector<T> which stores up to N
18 * elements directly (without heap allocation). The types Size and Diff are
19 * used to store element counts, and can be any unsigned + signed type.
21 * Storage layout is either:
22 * - Direct allocation:
23 * - Size _size: the number of used elements (between 0 and N)
24 * - T direct[N]: an array of N elements of type T
25 * (only the first _size are initialized).
26 * - Indirect allocation:
27 * - Size _size: the number of used elements plus N + 1
28 * - Size capacity: the number of allocated elements
29 * - T* indirect: a pointer to an array of capacity elements of type T
30 * (only the first _size are initialized).
32 * The data type T must be movable by memmove/realloc(). Once we switch to C++,
33 * move constructors can be used instead.
35 template<unsigned int N
, typename T
, typename Size
= uint32_t, typename Diff
= int32_t>
38 typedef Size size_type
;
39 typedef Diff difference_type
;
41 typedef value_type
& reference
;
42 typedef const value_type
& const_reference
;
43 typedef value_type
* pointer
;
44 typedef const value_type
* const_pointer
;
49 typedef Diff difference_type
;
53 typedef std::random_access_iterator_tag iterator_category
;
54 iterator(T
* ptr_
) : ptr(ptr_
) {}
55 T
& operator*() const { return *ptr
; }
56 T
* operator->() const { return ptr
; }
57 T
& operator[](size_type pos
) { return ptr
[pos
]; }
58 const T
& operator[](size_type pos
) const { return ptr
[pos
]; }
59 iterator
& operator++() { ptr
++; return *this; }
60 iterator
& operator--() { ptr
--; return *this; }
61 iterator
operator++(int) { iterator
copy(*this); ++(*this); return copy
; }
62 iterator
operator--(int) { iterator
copy(*this); --(*this); return copy
; }
63 difference_type
friend operator-(iterator a
, iterator b
) { return (&(*a
) - &(*b
)); }
64 iterator
operator+(size_type n
) { return iterator(ptr
+ n
); }
65 iterator
& operator+=(size_type n
) { ptr
+= n
; return *this; }
66 iterator
operator-(size_type n
) { return iterator(ptr
- n
); }
67 iterator
& operator-=(size_type n
) { ptr
-= n
; return *this; }
68 bool operator==(iterator x
) const { return ptr
== x
.ptr
; }
69 bool operator!=(iterator x
) const { return ptr
!= x
.ptr
; }
70 bool operator>=(iterator x
) const { return ptr
>= x
.ptr
; }
71 bool operator<=(iterator x
) const { return ptr
<= x
.ptr
; }
72 bool operator>(iterator x
) const { return ptr
> x
.ptr
; }
73 bool operator<(iterator x
) const { return ptr
< x
.ptr
; }
76 class reverse_iterator
{
79 typedef Diff difference_type
;
83 typedef std::bidirectional_iterator_tag iterator_category
;
84 reverse_iterator(T
* ptr_
) : ptr(ptr_
) {}
85 T
& operator*() { return *ptr
; }
86 const T
& operator*() const { return *ptr
; }
87 T
* operator->() { return ptr
; }
88 const T
* operator->() const { return ptr
; }
89 reverse_iterator
& operator--() { ptr
++; return *this; }
90 reverse_iterator
& operator++() { ptr
--; return *this; }
91 reverse_iterator
operator++(int) { reverse_iterator
copy(*this); ++(*this); return copy
; }
92 reverse_iterator
operator--(int) { reverse_iterator
copy(*this); --(*this); return copy
; }
93 bool operator==(reverse_iterator x
) const { return ptr
== x
.ptr
; }
94 bool operator!=(reverse_iterator x
) const { return ptr
!= x
.ptr
; }
97 class const_iterator
{
100 typedef Diff difference_type
;
101 typedef const T value_type
;
102 typedef const T
* pointer
;
103 typedef const T
& reference
;
104 typedef std::random_access_iterator_tag iterator_category
;
105 const_iterator(const T
* ptr_
) : ptr(ptr_
) {}
106 const_iterator(iterator x
) : ptr(&(*x
)) {}
107 const T
& operator*() const { return *ptr
; }
108 const T
* operator->() const { return ptr
; }
109 const T
& operator[](size_type pos
) const { return ptr
[pos
]; }
110 const_iterator
& operator++() { ptr
++; return *this; }
111 const_iterator
& operator--() { ptr
--; return *this; }
112 const_iterator
operator++(int) { const_iterator
copy(*this); ++(*this); return copy
; }
113 const_iterator
operator--(int) { const_iterator
copy(*this); --(*this); return copy
; }
114 difference_type
friend operator-(const_iterator a
, const_iterator b
) { return (&(*a
) - &(*b
)); }
115 const_iterator
operator+(size_type n
) { return const_iterator(ptr
+ n
); }
116 const_iterator
& operator+=(size_type n
) { ptr
+= n
; return *this; }
117 const_iterator
operator-(size_type n
) { return const_iterator(ptr
- n
); }
118 const_iterator
& operator-=(size_type n
) { ptr
-= n
; return *this; }
119 bool operator==(const_iterator x
) const { return ptr
== x
.ptr
; }
120 bool operator!=(const_iterator x
) const { return ptr
!= x
.ptr
; }
121 bool operator>=(const_iterator x
) const { return ptr
>= x
.ptr
; }
122 bool operator<=(const_iterator x
) const { return ptr
<= x
.ptr
; }
123 bool operator>(const_iterator x
) const { return ptr
> x
.ptr
; }
124 bool operator<(const_iterator x
) const { return ptr
< x
.ptr
; }
127 class const_reverse_iterator
{
130 typedef Diff difference_type
;
131 typedef const T value_type
;
132 typedef const T
* pointer
;
133 typedef const T
& reference
;
134 typedef std::bidirectional_iterator_tag iterator_category
;
135 const_reverse_iterator(const T
* ptr_
) : ptr(ptr_
) {}
136 const_reverse_iterator(reverse_iterator x
) : ptr(&(*x
)) {}
137 const T
& operator*() const { return *ptr
; }
138 const T
* operator->() const { return ptr
; }
139 const_reverse_iterator
& operator--() { ptr
++; return *this; }
140 const_reverse_iterator
& operator++() { ptr
--; return *this; }
141 const_reverse_iterator
operator++(int) { const_reverse_iterator
copy(*this); ++(*this); return copy
; }
142 const_reverse_iterator
operator--(int) { const_reverse_iterator
copy(*this); --(*this); return copy
; }
143 bool operator==(const_reverse_iterator x
) const { return ptr
== x
.ptr
; }
144 bool operator!=(const_reverse_iterator x
) const { return ptr
!= x
.ptr
; }
149 union direct_or_indirect
{
150 char direct
[sizeof(T
) * N
];
157 T
* direct_ptr(difference_type pos
) { return reinterpret_cast<T
*>(_union
.direct
) + pos
; }
158 const T
* direct_ptr(difference_type pos
) const { return reinterpret_cast<const T
*>(_union
.direct
) + pos
; }
159 T
* indirect_ptr(difference_type pos
) { return reinterpret_cast<T
*>(_union
.indirect
) + pos
; }
160 const T
* indirect_ptr(difference_type pos
) const { return reinterpret_cast<const T
*>(_union
.indirect
) + pos
; }
161 bool is_direct() const { return _size
<= N
; }
163 void change_capacity(size_type new_capacity
) {
164 if (new_capacity
<= N
) {
166 T
* indirect
= indirect_ptr(0);
168 T
* dst
= direct_ptr(0);
169 memcpy(dst
, src
, size() * sizeof(T
));
175 /* FIXME: Because malloc/realloc here won't call new_handler if allocation fails, assert
176 success. These should instead use an allocator or new/delete so that handlers
177 are called as necessary, but performance would be slightly degraded by doing so. */
178 _union
.indirect
= static_cast<char*>(realloc(_union
.indirect
, ((size_t)sizeof(T
)) * new_capacity
));
179 assert(_union
.indirect
);
180 _union
.capacity
= new_capacity
;
182 char* new_indirect
= static_cast<char*>(malloc(((size_t)sizeof(T
)) * new_capacity
));
183 assert(new_indirect
);
184 T
* src
= direct_ptr(0);
185 T
* dst
= reinterpret_cast<T
*>(new_indirect
);
186 memcpy(dst
, src
, size() * sizeof(T
));
187 _union
.indirect
= new_indirect
;
188 _union
.capacity
= new_capacity
;
194 T
* item_ptr(difference_type pos
) { return is_direct() ? direct_ptr(pos
) : indirect_ptr(pos
); }
195 const T
* item_ptr(difference_type pos
) const { return is_direct() ? direct_ptr(pos
) : indirect_ptr(pos
); }
198 void assign(size_type n
, const T
& val
) {
200 if (capacity() < n
) {
205 new(static_cast<void*>(item_ptr(size() - 1))) T(val
);
209 template<typename InputIterator
>
210 void assign(InputIterator first
, InputIterator last
) {
211 size_type n
= last
- first
;
213 if (capacity() < n
) {
216 while (first
!= last
) {
218 new(static_cast<void*>(item_ptr(size() - 1))) T(*first
);
223 prevector() : _size(0), _union
{{}} {}
225 explicit prevector(size_type n
) : _size(0) {
229 explicit prevector(size_type n
, const T
& val
= T()) : _size(0) {
233 new(static_cast<void*>(item_ptr(size() - 1))) T(val
);
237 template<typename InputIterator
>
238 prevector(InputIterator first
, InputIterator last
) : _size(0) {
239 size_type n
= last
- first
;
241 while (first
!= last
) {
243 new(static_cast<void*>(item_ptr(size() - 1))) T(*first
);
248 prevector(const prevector
<N
, T
, Size
, Diff
>& other
) : _size(0) {
249 change_capacity(other
.size());
250 const_iterator it
= other
.begin();
251 while (it
!= other
.end()) {
253 new(static_cast<void*>(item_ptr(size() - 1))) T(*it
);
258 prevector(prevector
<N
, T
, Size
, Diff
>&& other
) : _size(0) {
262 prevector
& operator=(const prevector
<N
, T
, Size
, Diff
>& other
) {
263 if (&other
== this) {
267 change_capacity(other
.size());
268 const_iterator it
= other
.begin();
269 while (it
!= other
.end()) {
271 new(static_cast<void*>(item_ptr(size() - 1))) T(*it
);
277 prevector
& operator=(prevector
<N
, T
, Size
, Diff
>&& other
) {
282 size_type
size() const {
283 return is_direct() ? _size
: _size
- N
- 1;
290 iterator
begin() { return iterator(item_ptr(0)); }
291 const_iterator
begin() const { return const_iterator(item_ptr(0)); }
292 iterator
end() { return iterator(item_ptr(size())); }
293 const_iterator
end() const { return const_iterator(item_ptr(size())); }
295 reverse_iterator
rbegin() { return reverse_iterator(item_ptr(size() - 1)); }
296 const_reverse_iterator
rbegin() const { return const_reverse_iterator(item_ptr(size() - 1)); }
297 reverse_iterator
rend() { return reverse_iterator(item_ptr(-1)); }
298 const_reverse_iterator
rend() const { return const_reverse_iterator(item_ptr(-1)); }
300 size_t capacity() const {
304 return _union
.capacity
;
308 T
& operator[](size_type pos
) {
309 return *item_ptr(pos
);
312 const T
& operator[](size_type pos
) const {
313 return *item_ptr(pos
);
316 void resize(size_type new_size
) {
317 if (size() > new_size
) {
318 erase(item_ptr(new_size
), end());
320 if (new_size
> capacity()) {
321 change_capacity(new_size
);
323 while (size() < new_size
) {
325 new(static_cast<void*>(item_ptr(size() - 1))) T();
329 void reserve(size_type new_capacity
) {
330 if (new_capacity
> capacity()) {
331 change_capacity(new_capacity
);
335 void shrink_to_fit() {
336 change_capacity(size());
343 iterator
insert(iterator pos
, const T
& value
) {
344 size_type p
= pos
- begin();
345 size_type new_size
= size() + 1;
346 if (capacity() < new_size
) {
347 change_capacity(new_size
+ (new_size
>> 1));
349 memmove(item_ptr(p
+ 1), item_ptr(p
), (size() - p
) * sizeof(T
));
351 new(static_cast<void*>(item_ptr(p
))) T(value
);
352 return iterator(item_ptr(p
));
355 void insert(iterator pos
, size_type count
, const T
& value
) {
356 size_type p
= pos
- begin();
357 size_type new_size
= size() + count
;
358 if (capacity() < new_size
) {
359 change_capacity(new_size
+ (new_size
>> 1));
361 memmove(item_ptr(p
+ count
), item_ptr(p
), (size() - p
) * sizeof(T
));
363 for (size_type i
= 0; i
< count
; i
++) {
364 new(static_cast<void*>(item_ptr(p
+ i
))) T(value
);
368 template<typename InputIterator
>
369 void insert(iterator pos
, InputIterator first
, InputIterator last
) {
370 size_type p
= pos
- begin();
371 difference_type count
= last
- first
;
372 size_type new_size
= size() + count
;
373 if (capacity() < new_size
) {
374 change_capacity(new_size
+ (new_size
>> 1));
376 memmove(item_ptr(p
+ count
), item_ptr(p
), (size() - p
) * sizeof(T
));
378 while (first
!= last
) {
379 new(static_cast<void*>(item_ptr(p
))) T(*first
);
385 iterator
erase(iterator pos
) {
386 return erase(pos
, pos
+ 1);
389 iterator
erase(iterator first
, iterator last
) {
390 // Erase is not allowed to the change the object's capacity. That means
391 // that when starting with an indirectly allocated prevector with
392 // size and capacity > N, the result may be a still indirectly allocated
393 // prevector with size <= N and capacity > N. A shrink_to_fit() call is
394 // necessary to switch to the (more efficient) directly allocated
395 // representation (with capacity N and size <= N).
397 char* endp
= (char*)&(*end());
398 if (!std::is_trivially_destructible
<T
>::value
) {
407 memmove(&(*first
), &(*last
), endp
- ((char*)(&(*last
))));
411 void push_back(const T
& value
) {
412 size_type new_size
= size() + 1;
413 if (capacity() < new_size
) {
414 change_capacity(new_size
+ (new_size
>> 1));
416 new(item_ptr(size())) T(value
);
421 erase(end() - 1, end());
428 const T
& front() const {
433 return *item_ptr(size() - 1);
436 const T
& back() const {
437 return *item_ptr(size() - 1);
440 void swap(prevector
<N
, T
, Size
, Diff
>& other
) {
441 std::swap(_union
, other
._union
);
442 std::swap(_size
, other
._size
);
446 if (!std::is_trivially_destructible
<T
>::value
) {
450 free(_union
.indirect
);
451 _union
.indirect
= NULL
;
455 bool operator==(const prevector
<N
, T
, Size
, Diff
>& other
) const {
456 if (other
.size() != size()) {
459 const_iterator b1
= begin();
460 const_iterator b2
= other
.begin();
461 const_iterator e1
= end();
463 if ((*b1
) != (*b2
)) {
472 bool operator!=(const prevector
<N
, T
, Size
, Diff
>& other
) const {
473 return !(*this == other
);
476 bool operator<(const prevector
<N
, T
, Size
, Diff
>& other
) const {
477 if (size() < other
.size()) {
480 if (size() > other
.size()) {
483 const_iterator b1
= begin();
484 const_iterator b2
= other
.begin();
485 const_iterator e1
= end();
499 size_t allocated_memory() const {
503 return ((size_t)(sizeof(T
))) * _union
.capacity
;
511 const value_type
* data() const {