2016-01-27 12:05:25 +01:00
// Copyright (c) 2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-08-26 12:59:13 +02:00
# ifndef BITCOIN_PREVECTOR_H
# define BITCOIN_PREVECTOR_H
2015-10-29 07:11:24 +01:00
2017-02-28 11:37:00 +01:00
# include <assert.h>
2015-10-29 07:11:24 +01:00
# include <stdlib.h>
# include <stdint.h>
# include <string.h>
2018-11-12 10:41:18 +01:00
# include <algorithm>
2018-03-01 12:12:55 +01:00
# include <cstddef>
2015-10-29 07:11:24 +01:00
# include <iterator>
2017-03-14 10:42:17 +01:00
# include <type_traits>
2015-10-29 07:11:24 +01:00
2018-03-01 12:12:55 +01:00
# include <compat.h>
2015-10-29 07:11:24 +01:00
/** Implements a drop-in replacement for std::vector<T> which stores up to N
* elements directly ( without heap allocation ) . The types Size and Diff are
* used to store element counts , and can be any unsigned + signed type .
*
* Storage layout is either :
* - Direct allocation :
* - Size _size : the number of used elements ( between 0 and N )
* - T direct [ N ] : an array of N elements of type T
* ( only the first _size are initialized ) .
* - Indirect allocation :
* - Size _size : the number of used elements plus N + 1
* - Size capacity : the number of allocated elements
* - T * indirect : a pointer to an array of capacity elements of type T
* ( only the first _size are initialized ) .
*
* The data type T must be movable by memmove / realloc ( ) . Once we switch to C + + ,
* move constructors can be used instead .
*/
template < unsigned int N , typename T , typename Size = uint32_t , typename Diff = int32_t >
class prevector {
public :
typedef Size size_type ;
typedef Diff difference_type ;
typedef T value_type ;
typedef value_type & reference ;
typedef const value_type & const_reference ;
typedef value_type * pointer ;
typedef const value_type * const_pointer ;
class iterator {
T * ptr ;
public :
typedef Diff difference_type ;
typedef T value_type ;
typedef T * pointer ;
typedef T & reference ;
typedef std : : random_access_iterator_tag iterator_category ;
iterator ( T * ptr_ ) : ptr ( ptr_ ) { }
T & operator * ( ) const { return * ptr ; }
T * operator - > ( ) const { return ptr ; }
T & operator [ ] ( size_type pos ) { return ptr [ pos ] ; }
const T & operator [ ] ( size_type pos ) const { return ptr [ pos ] ; }
iterator & operator + + ( ) { ptr + + ; return * this ; }
iterator & operator - - ( ) { ptr - - ; return * this ; }
iterator operator + + ( int ) { iterator copy ( * this ) ; + + ( * this ) ; return copy ; }
iterator operator - - ( int ) { iterator copy ( * this ) ; - - ( * this ) ; return copy ; }
difference_type friend operator - ( iterator a , iterator b ) { return ( & ( * a ) - & ( * b ) ) ; }
iterator operator + ( size_type n ) { return iterator ( ptr + n ) ; }
iterator & operator + = ( size_type n ) { ptr + = n ; return * this ; }
iterator operator - ( size_type n ) { return iterator ( ptr - n ) ; }
iterator & operator - = ( size_type n ) { ptr - = n ; return * this ; }
bool operator = = ( iterator x ) const { return ptr = = x . ptr ; }
bool operator ! = ( iterator x ) const { return ptr ! = x . ptr ; }
bool operator > = ( iterator x ) const { return ptr > = x . ptr ; }
bool operator < = ( iterator x ) const { return ptr < = x . ptr ; }
bool operator > ( iterator x ) const { return ptr > x . ptr ; }
bool operator < ( iterator x ) const { return ptr < x . ptr ; }
} ;
class reverse_iterator {
T * ptr ;
public :
typedef Diff difference_type ;
typedef T value_type ;
typedef T * pointer ;
typedef T & reference ;
typedef std : : bidirectional_iterator_tag iterator_category ;
reverse_iterator ( T * ptr_ ) : ptr ( ptr_ ) { }
T & operator * ( ) { return * ptr ; }
const T & operator * ( ) const { return * ptr ; }
T * operator - > ( ) { return ptr ; }
const T * operator - > ( ) const { return ptr ; }
reverse_iterator & operator - - ( ) { ptr + + ; return * this ; }
reverse_iterator & operator + + ( ) { ptr - - ; return * this ; }
reverse_iterator operator + + ( int ) { reverse_iterator copy ( * this ) ; + + ( * this ) ; return copy ; }
reverse_iterator operator - - ( int ) { reverse_iterator copy ( * this ) ; - - ( * this ) ; return copy ; }
bool operator = = ( reverse_iterator x ) const { return ptr = = x . ptr ; }
bool operator ! = ( reverse_iterator x ) const { return ptr ! = x . ptr ; }
} ;
class const_iterator {
const T * ptr ;
public :
typedef Diff difference_type ;
typedef const T value_type ;
typedef const T * pointer ;
typedef const T & reference ;
typedef std : : random_access_iterator_tag iterator_category ;
const_iterator ( const T * ptr_ ) : ptr ( ptr_ ) { }
const_iterator ( iterator x ) : ptr ( & ( * x ) ) { }
const T & operator * ( ) const { return * ptr ; }
const T * operator - > ( ) const { return ptr ; }
const T & operator [ ] ( size_type pos ) const { return ptr [ pos ] ; }
const_iterator & operator + + ( ) { ptr + + ; return * this ; }
const_iterator & operator - - ( ) { ptr - - ; return * this ; }
const_iterator operator + + ( int ) { const_iterator copy ( * this ) ; + + ( * this ) ; return copy ; }
const_iterator operator - - ( int ) { const_iterator copy ( * this ) ; - - ( * this ) ; return copy ; }
difference_type friend operator - ( const_iterator a , const_iterator b ) { return ( & ( * a ) - & ( * b ) ) ; }
const_iterator operator + ( size_type n ) { return const_iterator ( ptr + n ) ; }
const_iterator & operator + = ( size_type n ) { ptr + = n ; return * this ; }
const_iterator operator - ( size_type n ) { return const_iterator ( ptr - n ) ; }
const_iterator & operator - = ( size_type n ) { ptr - = n ; return * this ; }
bool operator = = ( const_iterator x ) const { return ptr = = x . ptr ; }
bool operator ! = ( const_iterator x ) const { return ptr ! = x . ptr ; }
bool operator > = ( const_iterator x ) const { return ptr > = x . ptr ; }
bool operator < = ( const_iterator x ) const { return ptr < = x . ptr ; }
bool operator > ( const_iterator x ) const { return ptr > x . ptr ; }
bool operator < ( const_iterator x ) const { return ptr < x . ptr ; }
} ;
class const_reverse_iterator {
const T * ptr ;
public :
typedef Diff difference_type ;
typedef const T value_type ;
typedef const T * pointer ;
typedef const T & reference ;
typedef std : : bidirectional_iterator_tag iterator_category ;
2017-06-19 01:45:23 +02:00
const_reverse_iterator ( const T * ptr_ ) : ptr ( ptr_ ) { }
2015-10-29 07:11:24 +01:00
const_reverse_iterator ( reverse_iterator x ) : ptr ( & ( * x ) ) { }
const T & operator * ( ) const { return * ptr ; }
const T * operator - > ( ) const { return ptr ; }
const_reverse_iterator & operator - - ( ) { ptr + + ; return * this ; }
const_reverse_iterator & operator + + ( ) { ptr - - ; return * this ; }
const_reverse_iterator operator + + ( int ) { const_reverse_iterator copy ( * this ) ; + + ( * this ) ; return copy ; }
const_reverse_iterator operator - - ( int ) { const_reverse_iterator copy ( * this ) ; - - ( * this ) ; return copy ; }
bool operator = = ( const_reverse_iterator x ) const { return ptr = = x . ptr ; }
bool operator ! = ( const_reverse_iterator x ) const { return ptr ! = x . ptr ; }
} ;
private :
Merge #17708: prevector: avoid misaligned member accesses
5f26855f109af53a336d5f98ed0ae584e7a31f84 test: Remove ubsan alignment suppressions (Wladimir J. van der Laan)
9d933ef9191417b4b7d29eaa3c3a571f814acc8e prevector: avoid misaligned member accesses (Anthony Towns)
Pull request description:
Ensure prevector data is appropriately aligned. Earlier discussion in #17530.
**Edit laanwj**: In contrast to #17530, it does this without increase in size of any of the coin cache data structures (x86_64, clang)
| Struct | (size,align) before | (size,align) after |
| ------------- | ------------- | ------- |
| Coin | 48, 8 | 48, 8 |
| CCoinsCacheEntry | 56, 8 | 56, 8 |
| CScript | 32, 1 | 32, 8 |
ACKs for top commit:
laanwj:
ACK 5f26855f109af53a336d5f98ed0ae584e7a31f84
practicalswift:
ACK 5f26855f109af53a336d5f98ed0ae584e7a31f84
jonatack:
ACK 5f26855f109af53a336d5f98ed0ae584e7a31f84
Tree-SHA512: 98d112d6856f683d5b212410b73f3071d2994f1efb046a2418a35890aa1cf1aa7c96a960fc2e963fa15241e861093c1ea41951cf5b4b5431f88345eb1dd0a98a
2020-02-12 17:48:22 +01:00
# pragma pack(push, 1)
2015-12-01 15:20:35 +01:00
union direct_or_indirect {
2015-10-29 07:11:24 +01:00
char direct [ sizeof ( T ) * N ] ;
struct {
char * indirect ;
Merge #17708: prevector: avoid misaligned member accesses
5f26855f109af53a336d5f98ed0ae584e7a31f84 test: Remove ubsan alignment suppressions (Wladimir J. van der Laan)
9d933ef9191417b4b7d29eaa3c3a571f814acc8e prevector: avoid misaligned member accesses (Anthony Towns)
Pull request description:
Ensure prevector data is appropriately aligned. Earlier discussion in #17530.
**Edit laanwj**: In contrast to #17530, it does this without increase in size of any of the coin cache data structures (x86_64, clang)
| Struct | (size,align) before | (size,align) after |
| ------------- | ------------- | ------- |
| Coin | 48, 8 | 48, 8 |
| CCoinsCacheEntry | 56, 8 | 56, 8 |
| CScript | 32, 1 | 32, 8 |
ACKs for top commit:
laanwj:
ACK 5f26855f109af53a336d5f98ed0ae584e7a31f84
practicalswift:
ACK 5f26855f109af53a336d5f98ed0ae584e7a31f84
jonatack:
ACK 5f26855f109af53a336d5f98ed0ae584e7a31f84
Tree-SHA512: 98d112d6856f683d5b212410b73f3071d2994f1efb046a2418a35890aa1cf1aa7c96a960fc2e963fa15241e861093c1ea41951cf5b4b5431f88345eb1dd0a98a
2020-02-12 17:48:22 +01:00
size_type capacity ;
2015-10-29 07:11:24 +01:00
} ;
Merge #17708: prevector: avoid misaligned member accesses
5f26855f109af53a336d5f98ed0ae584e7a31f84 test: Remove ubsan alignment suppressions (Wladimir J. van der Laan)
9d933ef9191417b4b7d29eaa3c3a571f814acc8e prevector: avoid misaligned member accesses (Anthony Towns)
Pull request description:
Ensure prevector data is appropriately aligned. Earlier discussion in #17530.
**Edit laanwj**: In contrast to #17530, it does this without increase in size of any of the coin cache data structures (x86_64, clang)
| Struct | (size,align) before | (size,align) after |
| ------------- | ------------- | ------- |
| Coin | 48, 8 | 48, 8 |
| CCoinsCacheEntry | 56, 8 | 56, 8 |
| CScript | 32, 1 | 32, 8 |
ACKs for top commit:
laanwj:
ACK 5f26855f109af53a336d5f98ed0ae584e7a31f84
practicalswift:
ACK 5f26855f109af53a336d5f98ed0ae584e7a31f84
jonatack:
ACK 5f26855f109af53a336d5f98ed0ae584e7a31f84
Tree-SHA512: 98d112d6856f683d5b212410b73f3071d2994f1efb046a2418a35890aa1cf1aa7c96a960fc2e963fa15241e861093c1ea41951cf5b4b5431f88345eb1dd0a98a
2020-02-12 17:48:22 +01:00
} ;
# pragma pack(pop)
alignas ( char * ) direct_or_indirect _union = { } ;
size_type _size = 0 ;
static_assert ( alignof ( char * ) % alignof ( size_type ) = = 0 & & sizeof ( char * ) % alignof ( size_type ) = = 0 , " size_type cannot have more restrictive alignment requirement than pointer " ) ;
static_assert ( alignof ( char * ) % alignof ( T ) = = 0 , " value_type T cannot have more restrictive alignment requirement than pointer " ) ;
2015-10-29 07:11:24 +01:00
T * direct_ptr ( difference_type pos ) { return reinterpret_cast < T * > ( _union . direct ) + pos ; }
const T * direct_ptr ( difference_type pos ) const { return reinterpret_cast < const T * > ( _union . direct ) + pos ; }
T * indirect_ptr ( difference_type pos ) { return reinterpret_cast < T * > ( _union . indirect ) + pos ; }
const T * indirect_ptr ( difference_type pos ) const { return reinterpret_cast < const T * > ( _union . indirect ) + pos ; }
bool is_direct ( ) const { return _size < = N ; }
void change_capacity ( size_type new_capacity ) {
if ( new_capacity < = N ) {
if ( ! is_direct ( ) ) {
T * indirect = indirect_ptr ( 0 ) ;
T * src = indirect ;
T * dst = direct_ptr ( 0 ) ;
memcpy ( dst , src , size ( ) * sizeof ( T ) ) ;
free ( indirect ) ;
_size - = N + 1 ;
}
} else {
if ( ! is_direct ( ) ) {
2017-02-28 11:37:00 +01:00
/* FIXME: Because malloc/realloc here won't call new_handler if allocation fails, assert
success . These should instead use an allocator or new / delete so that handlers
are called as necessary , but performance would be slightly degraded by doing so . */
2015-10-29 07:11:24 +01:00
_union . indirect = static_cast < char * > ( realloc ( _union . indirect , ( ( size_t ) sizeof ( T ) ) * new_capacity ) ) ;
2017-02-28 11:37:00 +01:00
assert ( _union . indirect ) ;
2015-10-29 07:11:24 +01:00
_union . capacity = new_capacity ;
} else {
char * new_indirect = static_cast < char * > ( malloc ( ( ( size_t ) sizeof ( T ) ) * new_capacity ) ) ;
2017-02-28 11:37:00 +01:00
assert ( new_indirect ) ;
2015-10-29 07:11:24 +01:00
T * src = direct_ptr ( 0 ) ;
T * dst = reinterpret_cast < T * > ( new_indirect ) ;
memcpy ( dst , src , size ( ) * sizeof ( T ) ) ;
_union . indirect = new_indirect ;
_union . capacity = new_capacity ;
_size + = N + 1 ;
}
}
}
T * item_ptr ( difference_type pos ) { return is_direct ( ) ? direct_ptr ( pos ) : indirect_ptr ( pos ) ; }
const T * item_ptr ( difference_type pos ) const { return is_direct ( ) ? direct_ptr ( pos ) : indirect_ptr ( pos ) ; }
2018-03-01 12:12:55 +01:00
void fill ( T * dst , ptrdiff_t count ) {
2018-11-12 10:41:18 +01:00
std : : fill_n ( dst , count , T { } ) ;
2018-03-01 12:12:55 +01:00
}
void fill ( T * dst , ptrdiff_t count , const T & value ) {
2018-11-12 10:41:18 +01:00
std : : fill_n ( dst , count , value ) ;
2018-03-01 12:12:55 +01:00
}
template < typename InputIterator >
void fill ( T * dst , InputIterator first , InputIterator last ) {
while ( first ! = last ) {
new ( static_cast < void * > ( dst ) ) T ( * first ) ;
+ + dst ;
+ + first ;
}
}
2020-01-08 08:02:17 +01:00
void fill ( T * dst , const_iterator first , const_iterator last ) {
ptrdiff_t count = last - first ;
fill ( dst , & * first , count ) ;
}
void fill ( T * dst , const T * src , ptrdiff_t count ) {
if ( IS_TRIVIALLY_CONSTRUCTIBLE < T > : : value ) {
: : memmove ( dst , src , count * sizeof ( T ) ) ;
} else {
for ( ptrdiff_t i = 0 ; i < count ; i + + ) {
new ( static_cast < void * > ( dst ) ) T ( * src ) ;
+ + dst ;
+ + src ;
}
}
}
2015-10-29 07:11:24 +01:00
public :
void assign ( size_type n , const T & val ) {
clear ( ) ;
if ( capacity ( ) < n ) {
change_capacity ( n ) ;
}
2018-03-01 12:12:55 +01:00
_size + = n ;
fill ( item_ptr ( 0 ) , n , val ) ;
2015-10-29 07:11:24 +01:00
}
template < typename InputIterator >
void assign ( InputIterator first , InputIterator last ) {
size_type n = last - first ;
clear ( ) ;
if ( capacity ( ) < n ) {
change_capacity ( n ) ;
}
2018-03-01 12:12:55 +01:00
_size + = n ;
fill ( item_ptr ( 0 ) , first , last ) ;
2015-10-29 07:11:24 +01:00
}
2019-05-06 21:32:01 +02:00
prevector ( ) { }
2015-10-29 07:11:24 +01:00
2019-05-06 21:32:01 +02:00
explicit prevector ( size_type n ) {
2015-10-29 07:11:24 +01:00
resize ( n ) ;
}
2019-05-06 21:32:01 +02:00
explicit prevector ( size_type n , const T & val ) {
2015-10-29 07:11:24 +01:00
change_capacity ( n ) ;
2018-03-01 12:12:55 +01:00
_size + = n ;
fill ( item_ptr ( 0 ) , n , val ) ;
2015-10-29 07:11:24 +01:00
}
template < typename InputIterator >
2019-05-06 21:32:01 +02:00
prevector ( InputIterator first , InputIterator last ) {
2015-10-29 07:11:24 +01:00
size_type n = last - first ;
change_capacity ( n ) ;
2018-03-01 12:12:55 +01:00
_size + = n ;
fill ( item_ptr ( 0 ) , first , last ) ;
2015-10-29 07:11:24 +01:00
}
2019-05-06 21:32:01 +02:00
prevector ( const prevector < N , T , Size , Diff > & other ) {
2018-03-01 12:12:55 +01:00
size_type n = other . size ( ) ;
change_capacity ( n ) ;
_size + = n ;
fill ( item_ptr ( 0 ) , other . begin ( ) , other . end ( ) ) ;
2015-10-29 07:11:24 +01:00
}
2019-05-06 21:32:01 +02:00
prevector ( prevector < N , T , Size , Diff > & & other ) {
2016-12-27 19:50:43 +01:00
swap ( other ) ;
}
2015-10-29 07:11:24 +01:00
prevector & operator = ( const prevector < N , T , Size , Diff > & other ) {
if ( & other = = this ) {
return * this ;
}
2018-03-01 12:12:55 +01:00
assign ( other . begin ( ) , other . end ( ) ) ;
2015-10-29 07:11:24 +01:00
return * this ;
}
2016-12-27 19:50:43 +01:00
prevector & operator = ( prevector < N , T , Size , Diff > & & other ) {
swap ( other ) ;
return * this ;
}
2015-10-29 07:11:24 +01:00
size_type size ( ) const {
return is_direct ( ) ? _size : _size - N - 1 ;
}
bool empty ( ) const {
return size ( ) = = 0 ;
}
iterator begin ( ) { return iterator ( item_ptr ( 0 ) ) ; }
const_iterator begin ( ) const { return const_iterator ( item_ptr ( 0 ) ) ; }
iterator end ( ) { return iterator ( item_ptr ( size ( ) ) ) ; }
const_iterator end ( ) const { return const_iterator ( item_ptr ( size ( ) ) ) ; }
reverse_iterator rbegin ( ) { return reverse_iterator ( item_ptr ( size ( ) - 1 ) ) ; }
const_reverse_iterator rbegin ( ) const { return const_reverse_iterator ( item_ptr ( size ( ) - 1 ) ) ; }
reverse_iterator rend ( ) { return reverse_iterator ( item_ptr ( - 1 ) ) ; }
const_reverse_iterator rend ( ) const { return const_reverse_iterator ( item_ptr ( - 1 ) ) ; }
size_t capacity ( ) const {
if ( is_direct ( ) ) {
return N ;
} else {
return _union . capacity ;
}
}
T & operator [ ] ( size_type pos ) {
return * item_ptr ( pos ) ;
}
const T & operator [ ] ( size_type pos ) const {
return * item_ptr ( pos ) ;
}
void resize ( size_type new_size ) {
2018-03-01 12:12:55 +01:00
size_type cur_size = size ( ) ;
if ( cur_size = = new_size ) {
return ;
}
if ( cur_size > new_size ) {
2016-04-13 19:09:16 +02:00
erase ( item_ptr ( new_size ) , end ( ) ) ;
2018-03-01 12:12:55 +01:00
return ;
2015-10-29 07:11:24 +01:00
}
if ( new_size > capacity ( ) ) {
change_capacity ( new_size ) ;
}
2018-03-01 12:12:55 +01:00
ptrdiff_t increase = new_size - cur_size ;
fill ( item_ptr ( cur_size ) , increase ) ;
_size + = increase ;
2015-10-29 07:11:24 +01:00
}
void reserve ( size_type new_capacity ) {
if ( new_capacity > capacity ( ) ) {
change_capacity ( new_capacity ) ;
}
}
void shrink_to_fit ( ) {
change_capacity ( size ( ) ) ;
}
void clear ( ) {
resize ( 0 ) ;
}
iterator insert ( iterator pos , const T & value ) {
size_type p = pos - begin ( ) ;
size_type new_size = size ( ) + 1 ;
if ( capacity ( ) < new_size ) {
change_capacity ( new_size + ( new_size > > 1 ) ) ;
}
2018-03-01 12:12:55 +01:00
T * ptr = item_ptr ( p ) ;
memmove ( ptr + 1 , ptr , ( size ( ) - p ) * sizeof ( T ) ) ;
2015-10-29 07:11:24 +01:00
_size + + ;
2018-03-01 12:12:55 +01:00
new ( static_cast < void * > ( ptr ) ) T ( value ) ;
return iterator ( ptr ) ;
2015-10-29 07:11:24 +01:00
}
void insert ( iterator pos , size_type count , const T & value ) {
size_type p = pos - begin ( ) ;
size_type new_size = size ( ) + count ;
if ( capacity ( ) < new_size ) {
change_capacity ( new_size + ( new_size > > 1 ) ) ;
}
2018-03-01 12:12:55 +01:00
T * ptr = item_ptr ( p ) ;
memmove ( ptr + count , ptr , ( size ( ) - p ) * sizeof ( T ) ) ;
2015-10-29 07:11:24 +01:00
_size + = count ;
2018-03-01 12:12:55 +01:00
fill ( item_ptr ( p ) , count , value ) ;
2015-10-29 07:11:24 +01:00
}
template < typename InputIterator >
void insert ( iterator pos , InputIterator first , InputIterator last ) {
size_type p = pos - begin ( ) ;
difference_type count = last - first ;
size_type new_size = size ( ) + count ;
if ( capacity ( ) < new_size ) {
change_capacity ( new_size + ( new_size > > 1 ) ) ;
}
2018-03-01 12:12:55 +01:00
T * ptr = item_ptr ( p ) ;
memmove ( ptr + count , ptr , ( size ( ) - p ) * sizeof ( T ) ) ;
2015-10-29 07:11:24 +01:00
_size + = count ;
2018-03-01 12:12:55 +01:00
fill ( ptr , first , last ) ;
2015-10-29 07:11:24 +01:00
}
Merge #12324: speed up Unserialize_impl for prevector
86b47fa741408b061ab0bda784b8678bfd7dfa88 speed up Unserialize_impl for prevector (Akio Nakamura)
Pull request description:
The unserializer for prevector uses `resize()` for reserve the area, but it's prefer to use `reserve()` because `resize()` have overhead to call its constructor many times.
However, `reserve()` does not change the value of `_size` (a private member of prevector).
This PR make the logic of read from stream to callback function, and prevector handles initilizing new values with that call-back and ajust the value of `_size`.
The changes are as follows:
1. prevector.h
Add a public member function named 'append'.
This function has 2 params, number of elemenst to append and call-back function that initilizing new appended values.
2. serialize.h
In the following two function:
- `Unserialize_impl(Stream& is, prevector<N, T>& v, const unsigned char&)`
- `Unserialize_impl(Stream& is, prevector<N, T>& v, const V&)`
Make a callback function from each original logic of reading values from stream, and call prevector's `append()`.
3. test/prevector_tests.cpp
Add a test for `append()`.
## A benchmark result is following:
[Machine]
MacBook Pro (macOS 10.13.3/i7 2.2GHz/mem 16GB/SSD)
[result]
DeserializeAndCheckBlockTest => 22% faster
DeserializeBlockTest => 29% faster
[before PR]
# Benchmark, evals, iterations, total, min, max, median
DeserializeAndCheckBlockTest, 60, 160, 94.4901, 0.0094644, 0.0104715, 0.0098339
DeserializeBlockTest, 60, 130, 65.0964, 0.00800362, 0.00895134, 0.00824187
[After PR]
# Benchmark, evals, iterations, total, min, max, median
DeserializeAndCheckBlockTest, 60, 160, 77.1597, 0.00767013, 0.00858959, 0.00805757
DeserializeBlockTest, 60, 130, 49.9443, 0.00613926, 0.00691187, 0.00635527
ACKs for top commit:
laanwj:
utACK 86b47fa741408b061ab0bda784b8678bfd7dfa88
Tree-SHA512: 62ea121ccd45a306fefc67485a1b03a853435af762607dae2426a87b15a3033d802c8556e1923727ddd1023a1837d0e5f6720c2c77b38196907e750e15fbb902
2019-06-18 16:52:11 +02:00
inline void resize_uninitialized ( size_type new_size ) {
// resize_uninitialized changes the size of the prevector but does not initialize it.
// If size < new_size, the added elements must be initialized explicitly.
if ( capacity ( ) < new_size ) {
change_capacity ( new_size ) ;
_size + = new_size - size ( ) ;
return ;
}
if ( new_size < size ( ) ) {
erase ( item_ptr ( new_size ) , end ( ) ) ;
} else {
_size + = new_size - size ( ) ;
}
}
2015-10-29 07:11:24 +01:00
iterator erase ( iterator pos ) {
2016-04-13 19:09:16 +02:00
return erase ( pos , pos + 1 ) ;
2015-10-29 07:11:24 +01:00
}
iterator erase ( iterator first , iterator last ) {
2017-06-13 18:28:24 +02:00
// Erase is not allowed to the change the object's capacity. That means
// that when starting with an indirectly allocated prevector with
// size and capacity > N, the result may be a still indirectly allocated
// prevector with size <= N and capacity > N. A shrink_to_fit() call is
// necessary to switch to the (more efficient) directly allocated
// representation (with capacity N and size <= N).
2015-10-29 07:11:24 +01:00
iterator p = first ;
char * endp = ( char * ) & ( * end ( ) ) ;
2017-03-14 10:42:17 +01:00
if ( ! std : : is_trivially_destructible < T > : : value ) {
while ( p ! = last ) {
( * p ) . ~ T ( ) ;
_size - - ;
+ + p ;
}
} else {
_size - = last - p ;
2015-10-29 07:11:24 +01:00
}
memmove ( & ( * first ) , & ( * last ) , endp - ( ( char * ) ( & ( * last ) ) ) ) ;
return first ;
}
2020-02-04 04:49:10 +01:00
template < typename . . . Args >
void emplace_back ( Args & & . . . args ) {
2015-10-29 07:11:24 +01:00
size_type new_size = size ( ) + 1 ;
if ( capacity ( ) < new_size ) {
change_capacity ( new_size + ( new_size > > 1 ) ) ;
}
2020-02-04 04:49:10 +01:00
new ( item_ptr ( size ( ) ) ) T ( std : : forward < Args > ( args ) . . . ) ;
2015-10-29 07:11:24 +01:00
_size + + ;
}
2020-02-04 04:49:10 +01:00
void push_back ( const T & value ) {
emplace_back ( value ) ;
}
2015-10-29 07:11:24 +01:00
void pop_back ( ) {
2016-04-13 19:09:16 +02:00
erase ( end ( ) - 1 , end ( ) ) ;
2015-10-29 07:11:24 +01:00
}
T & front ( ) {
return * item_ptr ( 0 ) ;
}
const T & front ( ) const {
return * item_ptr ( 0 ) ;
}
T & back ( ) {
return * item_ptr ( size ( ) - 1 ) ;
}
const T & back ( ) const {
return * item_ptr ( size ( ) - 1 ) ;
}
void swap ( prevector < N , T , Size , Diff > & other ) {
2016-04-14 18:26:32 +02:00
std : : swap ( _union , other . _union ) ;
2015-10-29 07:11:24 +01:00
std : : swap ( _size , other . _size ) ;
}
~ prevector ( ) {
2017-03-14 10:42:17 +01:00
if ( ! std : : is_trivially_destructible < T > : : value ) {
clear ( ) ;
}
2015-10-29 07:11:24 +01:00
if ( ! is_direct ( ) ) {
free ( _union . indirect ) ;
2019-08-06 05:08:33 +02:00
_union . indirect = nullptr ;
2015-10-29 07:11:24 +01:00
}
}
bool operator = = ( const prevector < N , T , Size , Diff > & other ) const {
if ( other . size ( ) ! = size ( ) ) {
return false ;
}
const_iterator b1 = begin ( ) ;
const_iterator b2 = other . begin ( ) ;
const_iterator e1 = end ( ) ;
while ( b1 ! = e1 ) {
if ( ( * b1 ) ! = ( * b2 ) ) {
return false ;
}
+ + b1 ;
+ + b2 ;
}
return true ;
}
bool operator ! = ( const prevector < N , T , Size , Diff > & other ) const {
return ! ( * this = = other ) ;
}
bool operator < ( const prevector < N , T , Size , Diff > & other ) const {
if ( size ( ) < other . size ( ) ) {
return true ;
}
if ( size ( ) > other . size ( ) ) {
return false ;
}
const_iterator b1 = begin ( ) ;
const_iterator b2 = other . begin ( ) ;
const_iterator e1 = end ( ) ;
while ( b1 ! = e1 ) {
if ( ( * b1 ) < ( * b2 ) ) {
return true ;
}
if ( ( * b2 ) < ( * b1 ) ) {
return false ;
}
+ + b1 ;
+ + b2 ;
}
return false ;
}
size_t allocated_memory ( ) const {
if ( is_direct ( ) ) {
return 0 ;
} else {
return ( ( size_t ) ( sizeof ( T ) ) ) * _union . capacity ;
}
}
2016-10-04 10:34:18 +02:00
value_type * data ( ) {
return item_ptr ( 0 ) ;
}
const value_type * data ( ) const {
return item_ptr ( 0 ) ;
}
2020-01-08 08:02:17 +01:00
template < typename V >
static void assign_to ( const_iterator b , const_iterator e , V & v ) {
// We know that internally the iterators are pointing to continues memory, so we can directly use the pointers here
// This avoids internal use of std::copy and operator++ on the iterators and instead allows efficient memcpy/memmove
if ( IS_TRIVIALLY_CONSTRUCTIBLE < T > : : value ) {
auto s = e - b ;
if ( v . size ( ) ! = s ) {
v . resize ( s ) ;
}
: : memmove ( v . data ( ) , & * b , s ) ;
} else {
v . assign ( & * b , & * e ) ;
}
}
2015-10-29 07:11:24 +01:00
} ;
2020-01-08 08:02:17 +01:00
2017-08-26 12:59:13 +02:00
# endif // BITCOIN_PREVECTOR_H