├── API.md ├── LICENSE ├── README.md ├── include └── veque.hpp ├── performance └── main.cpp └── test ├── catch.hpp ├── main.cpp ├── test_deque_parity.cpp ├── test_end_growth.cpp ├── test_hashing.cpp ├── test_insert_erase.cpp ├── test_insertion_growth.cpp ├── test_ordering.cpp ├── test_reassignment.cpp ├── test_resize.cpp ├── test_strong_exception_guarantee.cpp ├── test_types.h └── test_vector_parity.cpp /API.md: -------------------------------------------------------------------------------- 1 | # veque API 2 | _The double-ended vector_ 3 | 4 | ------ 5 | 6 | This is intended to be a thorough description of the entire `veque` interface. 7 | 8 | To jump to the API details that are unique to `veque`, go to the [Capacity](#capacity) and [Modifiers](#modifiers) sections. 9 | 10 | template> 11 | class veque 12 | 13 | ## Iterator invalidation 14 | 15 | Iterator invalidation rules can be identical to `std::vector` when using `vector_compatible_resize_traits`. 16 | 17 | When using the default `fast_resize_traits`, invalidation rules are a little more aggressive when inserting and erasing. 18 | 19 | | Operations | Invalidated | 20 | |---|---| 21 | | _Different from `std::vector`_ | 22 | | `insert`, `emplace`, `erase`
using resize traits with `resize_from_closest_side=true` | **Always**. Consider using the returned iterator | 23 | | _Same as `std::vector`_ | 24 | | All read only operations, swap | Never | 25 | | `clear`, `operator=`, `assign` | Always | 26 | | `insert`, `emplace`, `erase`
using resize traits with `resize_from_closest_side=false`| **Same as `std::vector`**. If the new `size()` is greater than `capacity()`, all iterators and references are invalidated. Otherwise, only the iterators and references before the insert/erase point remain valid. | 27 | | all `reserve`s, `shrink_to_fit` | If the vector changed capacity, all of them. If not, none | 28 | | `push_back`, `emplace_back`, `resize`, `resize_back` | If the new `size()` is greater than `capacity_back()`, all of them. If not, only `end()` | 29 | | `push_front`, `emplace_front`, `resize_front` | If the new `size()` is greater than `capacity_front()`, all of them. If not, only `begin()` | 30 | | All `resize`s | If the vector changed capacity, all of them. If not, only `begin()` or `end()` | 31 | | All `pop_back`s | The element erased and `end()` | 32 | | All `pop_front`s | The element erased and `begin()` | 33 | 34 | ## Member types 35 | 36 | using allocator_type = Allocator 37 | using alloc_traits = std::allocator_traits; 38 | using value_type = T 39 | using reference = T & 40 | using const_reference = const T & 41 | using pointer = T * 42 | using const_pointer = const T * 43 | using iterator = T * 44 | using const_iterator = const T * 45 | using reverse_iterator = std::reverse_iterator 46 | using const_reverse_iterator = std::reverse_iterator 47 | using difference_type = std::ptrdiff_t 48 | using size_type = std::size_t 49 | using ssize_type = std::ptrdiff_t 50 | 51 | ## Member functions 52 | 53 | All constructors match the behavior, complexity, and exception rules of [C++17 `std::vector` constructors](https://en.cppreference.com/w/cpp/container/vector/vector) 54 | 55 | Construction from a veque with different `ResizeTraits` is supported. 56 | 57 | veque() noexcept ( noexcept(Allocator()) ) 58 | 59 | explicit veque( const Allocator& ) noexcept 60 | 61 | explicit veque( size_type n, const Allocator& = Allocator() ) 62 | 63 | veque( size_type n, const T &val, const Allocator& = Allocator() ) 64 | 65 | template 66 | veque( InputIt first, InputIt last, const Allocator& = Allocator() ) 67 | 68 | veque( std::initializer_list, const Allocator& = Allocator() ) 69 | 70 | veque( const veque & ) 71 | 72 | veque( const veque &, const Allocator& ) 73 | 74 | veque( veque && ) noexcept 75 | 76 | veque( veque &&, const Allocator& ) noexcept 77 | 78 | Destructs the container. The destructors of the elements are called and the used storage is deallocated. 79 | 80 | ~veque() 81 | 82 | All assignment operators match the behavior, complexity, and exception rules of [C++17 `std::vector` assignment operators](https://en.cppreference.com/w/cpp/container/vector/operator%3D) 83 | 84 | Assignment from a veque with different `ResizeTraits` is supported. 85 | 86 | veque & operator=( const veque & ) 87 | 88 | veque & operator=( veque && ) noexcept( 89 | noexcept(std::allocator_traits::propagate_on_container_move_assignment::value 90 | || std::allocator_traits::is_always_equal::value) ) 91 | 92 | veque & operator=( std::initializer_list ) 93 | 94 | All `assign` functions match the behavior, complexity, and exception rules of [C++17 `std::vector::assign`](https://en.cppreference.com/w/cpp/container/vector/assign) 95 | 96 | void assign( size_type, const T &value ) 97 | 98 | template 99 | void assign( InputIt, InputIt ) 100 | 101 | void assign( std::initializer_list ) 102 | 103 | allocator_type get_allocator() const 104 | 105 | ### Element access 106 | 107 | All element access functions match the behavior, complexity, and exception rules of [C++17 `std::vector` element access functions](https://en.cppreference.com/w/cpp/container/vector#Element_access) 108 | 109 | reference at( size_type ) 110 | 111 | const_reference at( size_type ) const 112 | 113 | reference operator[]( size_type ) 114 | 115 | const_reference operator[]( size_type ) const 116 | 117 | reference front() 118 | 119 | const_reference front() const 120 | 121 | reference back() 122 | 123 | const_reference back() const 124 | 125 | T * data() noexcept 126 | 127 | const T * data() const noexcept 128 | 129 | ### Iterators 130 | 131 | All iterator functions match the behavior, complexity, and exception rules of [C++17 `std::vector` iterator functions](https://en.cppreference.com/w/cpp/container/vector#Iterators) 132 | 133 | iterator begin() noexcept 134 | 135 | const_iterator begin() const noexcept 136 | 137 | const_iterator cbegin() const noexcept 138 | 139 | iterator end() noexcept 140 | 141 | const_iterator end() const noexcept 142 | 143 | const_iterator cend() const noexcept 144 | 145 | reverse_iterator rbegin() noexcept 146 | 147 | const_reverse_iterator rbegin() const noexcept 148 | 149 | const_reverse_iterator crbegin() const noexcept 150 | 151 | reverse_iterator rend() noexcept 152 | 153 | const_reverse_iterator rend() const noexcept 154 | 155 | const_reverse_iterator crend() const noexcept 156 | 157 | ### Capacity 158 | 159 | size_type capacity_front() const noexcept 160 | 161 | > Returns current size + unused allocated storage before front(). Can be used to determine if adding elements at begin() will trigger a reallocation 162 | 163 | size_type capacity_back() const noexcept 164 | 165 | > Returns current size + unused allocated storage after back(). Can be used to determine if adding elements at end() will trigger a reallocation. This function behaves identically to `capacity()` 166 | 167 | size_type capacity_full() const noexcept 168 | 169 | > Returns all allocated storage, used and unused 170 | 171 | void reserve_back( size_type ) 172 | 173 | > Ensures sufficient storage for growth after end(). This function behaves identically to `std::vector::reserve` 174 | 175 | void reserve_front( size_type ) 176 | 177 | > Ensures sufficient storage for growth before begin() 178 | 179 | void reserve( size_type front, size_type back ); 180 | 181 | > Equivalent to `reserve_front(front); reserve_back(back);`, performing at most one reallocation 182 | 183 | void reserve( size_type size ) 184 | 185 | > Ensures sufficient storage for both front and back growth. Equivalent to `reserve(size,size)` 186 | 187 | ssize_type ssize() const noexcept 188 | 189 | > Returns the size as a signed integer type 190 | 191 | All other capacity functions match the behavior, complexity, and exception rules of C++17 `std::vector` 192 | 193 | [[nodiscard]] bool empty() const noexcept 194 | 195 | size_type size() const noexcept 196 | 197 | size_type max_size() const noexcept 198 | 199 | void reserve( size_type ) 200 | 201 | size_type capacity() const noexcept 202 | 203 | void shrink_to_fit() 204 | 205 | ### Modifiers 206 | 207 | T pop_back_element() 208 | 209 | > Pops and returns back element. Strong exception safety guaranteed. Moves element when appropriate 210 | 211 | T pop_front_element() 212 | 213 | > Pops and returns front element. Strong exception safety guaranteed. Moves element when appropriate 214 | 215 | void resize_front( size_type ) 216 | void resize_front( size_type, const T & ) 217 | 218 | > Resizes the veque, by adding or removing from the front. 219 | 220 | void resize_back( size_type ) 221 | void resize_back( size_type, const T & ) 222 | 223 | > Resizes the veque, by adding or removing from the back. This function behaves identically to `resize()` 224 | 225 | void push_front( const T & ) 226 | 227 | void push_front( T && ) 228 | 229 | template reference 230 | emplace_front( Args && ... args ) 231 | 232 | > Adds a new element to the front of the veque 233 | 234 | void pop_front() 235 | 236 | > Removes element at front of the veque 237 | 238 | All `insert`, `emplace` and `erase` functions perform the same tasks as their `std::vector` counterparts. **However**, 239 | * The `veque` may be resized from either end. 240 | * This makes these operations often perform much faster 241 | * All iterators are invalidated, though. Consider utilizing the returned iterator. 242 | 243 | iterator insert( const_iterator, const T & ) 244 | 245 | iterator insert( const_iterator, T && ) 246 | 247 | iterator insert( const_iterator, size_type, const T& ) 248 | 249 | template 250 | iterator insert( const_iterator, InputIt, InputIt ) 251 | 252 | iterator insert( const_iterator, std::initializer_list ) 253 | 254 | template iterator emplace( const_iterator, Args && ... ) 255 | 256 | iterator erase( const_iterator ) 257 | 258 | iterator erase( const_iterator, const_iterator ) 259 | 260 | All other modifier functions match the behavior, complexity, and exception rules of C++17 `std::vector` 261 | 262 | void clear() noexcept 263 | 264 | void push_back( const T & ) 265 | 266 | void push_back( T && ) 267 | 268 | template 269 | reference emplace_back( Args && ... args ) 270 | 271 | void pop_back() 272 | 273 | void resize( size_type ) 274 | 275 | void resize( size_type, const T & ) 276 | 277 | void swap( veque & ) noexcept( 278 | noexcept(std::allocator_traits::propagate_on_container_swap::value 279 | || std::allocator_traits::is_always_equal::value)) 280 | 281 | ## Non-member functions 282 | 283 | bool operator==( const veque &lhs, const veque &rhs ) 284 | 285 | bool operator!=( const veque &lhs, const veque &rhs ) 286 | 287 | bool operator< ( const veque &lhs, const veque &rhs ) 288 | 289 | bool operator<=( const veque &lhs, const veque &rhs ) 290 | 291 | bool operator> ( const veque &lhs, const veque &rhs ) 292 | 293 | bool operator>=( const veque &lhs, const veque &rhs ) 294 | 295 | void swap( veque & lhs, veque & rhs ) noexcept( 296 | noexcept(std::allocator_traits::propagate_on_container_swap::value 297 | || std::allocator_traits::is_always_equal::value)) 298 | 299 | ## Non-member types 300 | 301 | struct std::hash> 302 | 303 | ## Deduction Guides 304 | 305 | (It does what you'd imagine) 306 | 307 | template< class InputIt, class Alloc = std::allocator::value_type>> 308 | veque(InputIt, InputIt, Alloc = Alloc()) 309 | -> veque::value_type, Alloc> 310 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # veque 2 | _The double-ended vector_ 3 | 4 | ------ 5 | 6 | A [very fast](performance/main.cpp#L7) C++17 container combining the best features of `std::vector` and `std::deque` 7 | 8 | > _"In Most Cases, Prefer Using deque (Controversial)"_ 9 | > 10 | > -Herb Sutter, [GotW #54](http://www.gotw.ca/gotw/054.htm) 11 | 12 | **veque** is an allocator-aware, efficient container with interface matching both `std::vector` and `std::deque`. Its data layout is very similar to `std::vector`, but with unused storage maintained both _before_ and _after_ the used storage. 13 | 14 | ### Features 15 | * Like `std::vector`, **veque** is an ordered container in cache-friendly, array-compatible contiguous memory. That makes the data compatible with C APIs, pointer iteration, `gsl::span`, and similar. 16 | * Like `std::deque`, **veque** allows fast insertion/deletion from the front of the container 17 | * Because **veque** can resize from both sides, insertions and erasures from arbitrary locations will be **twice as fast** on average, because there are two choices for _what data to shift_. 18 | 19 | ### Usage 20 | **The complete API documentation may be viewed [here](API.md).** 21 | 22 | The interface for **veque** maintains the entire interface for `std::vector`, allowing **veque** to be considered as a drop-in replacement. (See [tradeoffs](#tradeoffs)) 23 | 24 | #### In addition, **veque** provides the following additional functions: 25 | 26 | _`std::deque` interface:_ 27 | * `push_front()` 28 | * `emplace_front()` 29 | * `pop_front()` 30 | 31 | _End-specific resizing:_ 32 | * `resize_front()` 33 | * `resize_back()` (Same as `resize()`, to match `std::vector` and `std::deque` behavior) 34 | * `capacity_front()` 35 | * `capacity_back()` (Same as `capacity()`, to match `std::vector` and `std::deque` behavior) 36 | * `capacity_full()` 37 | * `reserve(size_type, size_type)` 38 | 39 | _Strong exception guarantee pop-and-return, courtesy C++17:_ 40 | * `pop_back_element()` 41 | * `pop_front_element()` 42 | 43 | _In the spirit of C++20, it is reasonable to ask for the size as a signed type:_ 44 | * `ssize()` 45 | 46 | ### Tradeoffs 47 | Is **veque** better than `std::vector` in every conceivable way? No. But the tradeoffs are appealing. 48 | * **veque** is a bit more eager to preallocate memory than a typical `std::vector` implementation, to anticipate resizing from either end. **(New - configurable via traits class!)** 49 | * `insert()` and `erase()` function calls should be assumed to invalidate all iterators and references, since the resizing could happen from either direction. By comparison, the same `std::vector` and `std::deque` operations will sometimes only invalidate *some* of the iterators and references. **(New - configurable via traits class!)** 50 | * `veque` is *not* specialized. Whether that makes it better or worse is up to you. 51 | 52 | ### Why "veque"? 53 | 54 | As a developer, I am not good at naming things. 55 | 56 | `double_ended_vector`? 57 | 58 | `dextor`? 59 | 60 | ### To do: 61 | * Perhaps C++14 support? 62 | -------------------------------------------------------------------------------- /include/veque.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * veque.hpp 3 | * 4 | * Efficient generic C++ container combining useful features of std::vector and std::deque 5 | * 6 | * Copyright (C) 2019 Drew Dormann 7 | * 8 | */ 9 | 10 | #ifndef VEQUE_HEADER_GUARD 11 | #define VEQUE_HEADER_GUARD 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | namespace veque 25 | { 26 | // Very fast resizing behavior 27 | struct fast_resize_traits 28 | { 29 | // Relative to size(), amount of unused space to reserve when reallocating 30 | using allocation_before_front = std::ratio<1>; 31 | using allocation_after_back = std::ratio<1>; 32 | 33 | // If true, arbitrary insert and erase operations are twice the speed of 34 | // std::vector, but those operations invalidate all iterators 35 | static constexpr auto resize_from_closest_side = true; 36 | }; 37 | 38 | // Match std::vector iterator invalidation rules 39 | struct vector_compatible_resize_traits 40 | { 41 | // Relative to size(), amount of unused space to reserve when reallocating 42 | using allocation_before_front = std::ratio<1>; 43 | using allocation_after_back = std::ratio<1>; 44 | 45 | // If false, veque is a 100% compatible drop-in replacement for 46 | // std::vector including iterator invalidation rules 47 | static constexpr auto resize_from_closest_side = false; 48 | }; 49 | 50 | // Resizing behavior resembling std::vector. Also ideal for queue-like push_back/pop_front behavior. 51 | struct std_vector_traits 52 | { 53 | // Reserve storage only at back, like std::vector 54 | using allocation_before_front = std::ratio<0>; 55 | using allocation_after_back = std::ratio<1>; 56 | 57 | // Same iterator invalidation rules as std::vector 58 | static constexpr auto resize_from_closest_side = false; 59 | }; 60 | 61 | // Never reallocate more storage than is needed 62 | struct no_reserve_traits 63 | { 64 | // Any operation requiring a greater size reserves only that size 65 | using allocation_before_front = std::ratio<0>; 66 | using allocation_after_back = std::ratio<0>; 67 | 68 | // Same iterator invalidation rules as std::vector 69 | static constexpr auto resize_from_closest_side = false; 70 | }; 71 | 72 | template< typename T, typename ResizeTraits = fast_resize_traits, typename Allocator = std::allocator > 73 | class veque 74 | { 75 | public: 76 | 77 | // Types 78 | using allocator_type = Allocator; 79 | using alloc_traits = std::allocator_traits; 80 | using value_type = T; 81 | using reference = T &; 82 | using const_reference = const T &; 83 | using pointer = T *; 84 | using const_pointer = const T *; 85 | using iterator = T *; 86 | using const_iterator = const T *; 87 | using reverse_iterator = std::reverse_iterator; 88 | using const_reverse_iterator = std::reverse_iterator; 89 | using difference_type = std::ptrdiff_t; 90 | using size_type = std::size_t; 91 | using ssize_type = std::ptrdiff_t; 92 | 93 | // Common member functions 94 | veque() noexcept ( noexcept(Allocator()) ) 95 | : veque( Allocator() ) 96 | { 97 | } 98 | 99 | explicit veque( const Allocator& alloc ) noexcept 100 | : _data { 0, alloc } 101 | { 102 | } 103 | 104 | explicit veque( size_type n, const Allocator& alloc = Allocator() ) 105 | : veque( _allocate_uninitialized_tag{}, n, alloc ) 106 | { 107 | _value_construct_range( begin(), end() ); 108 | } 109 | 110 | veque( size_type n, const T &value, const Allocator& alloc = Allocator() ) 111 | : veque( _allocate_uninitialized_tag{}, n, alloc ) 112 | { 113 | _value_construct_range( begin(), end(), value ); 114 | } 115 | 116 | template< typename InputIt, typename ItCat = typename std::iterator_traits::iterator_category > 117 | veque( InputIt b, InputIt e, const Allocator& alloc = Allocator() ) 118 | : veque( b, e, alloc, ItCat{} ) 119 | { 120 | } 121 | 122 | veque( std::initializer_list lst, const Allocator& alloc = Allocator() ) 123 | : veque( _allocate_uninitialized_tag{}, lst.size(), alloc ) 124 | { 125 | _copy_construct_range( lst.begin(), lst.end(), begin() ); 126 | } 127 | 128 | veque( const veque & other ) 129 | : veque( _allocate_uninitialized_tag{}, other.size(), alloc_traits::select_on_container_copy_construction( other._allocator() ) ) 130 | { 131 | _copy_construct_range( other.begin(), other.end(), begin() ); 132 | } 133 | 134 | template< typename OtherResizeTraits > 135 | veque( const veque & other ) 136 | : veque( _allocate_uninitialized_tag{}, other.size(), alloc_traits::select_on_container_copy_construction( other._allocator() ) ) 137 | { 138 | _copy_construct_range( other.begin(), other.end(), begin() ); 139 | } 140 | 141 | template< typename OtherResizeTraits > 142 | veque( const veque & other, const Allocator & alloc ) 143 | : veque( _allocate_uninitialized_tag{}, other.size(), alloc ) 144 | { 145 | _copy_construct_range( other.begin(), other.end(), begin() ); 146 | } 147 | 148 | veque( veque && other ) noexcept 149 | { 150 | _swap_with_allocator( std::move(other) ); 151 | } 152 | 153 | template< typename OtherResizeTraits > 154 | veque( veque && other ) noexcept 155 | { 156 | _swap_with_allocator( std::move(other) ); 157 | } 158 | 159 | template< typename OtherResizeTraits > 160 | veque( veque && other, const Allocator & alloc ) noexcept 161 | : veque( alloc ) 162 | { 163 | if constexpr ( !alloc_traits::is_always_equal::value ) 164 | { 165 | if ( alloc != other._allocator() ) 166 | { 167 | // Incompatible allocators. Allocate new storage. 168 | auto replacement = veque( _allocate_uninitialized_tag{}, other.size(), alloc ); 169 | _nothrow_move_construct_range( other.begin(), other.end(), replacement.begin() ); 170 | _swap_without_allocator( std::move(replacement) ); 171 | return; 172 | } 173 | } 174 | _swap_without_allocator( std::move(other) ); 175 | } 176 | 177 | ~veque() 178 | { 179 | _destroy( begin(), end() ); 180 | } 181 | 182 | veque & operator=( const veque & other ) 183 | { 184 | return _copy_assignment( other ); 185 | } 186 | 187 | template< typename OtherResizeTraits > 188 | veque & operator=( const veque & other ) 189 | { 190 | return _copy_assignment( other ); 191 | } 192 | 193 | veque & operator=( veque && other ) noexcept( 194 | noexcept(alloc_traits::propagate_on_container_move_assignment::value 195 | || alloc_traits::is_always_equal::value) ) 196 | { 197 | return _move_assignment( std::move(other) ); 198 | } 199 | 200 | template< typename OtherResizeTraits > 201 | veque & operator=( veque && other ) noexcept( 202 | noexcept(alloc_traits::propagate_on_container_move_assignment::value 203 | || alloc_traits::is_always_equal::value) ) 204 | { 205 | return _move_assignment( std::move(other) ); 206 | } 207 | 208 | veque & operator=( std::initializer_list lst ) 209 | { 210 | _assign( lst.begin(), lst.end() ); 211 | return *this; 212 | } 213 | 214 | void assign( size_type count, const T &value ) 215 | { 216 | if ( count > capacity_full() ) 217 | { 218 | _swap_without_allocator( veque( count, value, _allocator() ) ); 219 | } 220 | else 221 | { 222 | _reassign_existing_storage( count, value ); 223 | } 224 | } 225 | 226 | template< typename InputIt, typename ItCat = typename std::iterator_traits::iterator_category > 227 | void assign( InputIt b, InputIt e ) 228 | { 229 | _assign( b, e, ItCat{} ); 230 | } 231 | 232 | void assign( std::initializer_list lst ) 233 | { 234 | _assign( lst.begin(), lst.end() ); 235 | } 236 | 237 | allocator_type get_allocator() const 238 | { 239 | return _allocator(); 240 | } 241 | 242 | // Element access 243 | reference at( size_type idx ) 244 | { 245 | if ( idx >= size() ) 246 | { 247 | throw std::out_of_range("veque::at(" + std::to_string(idx) + ") out of range"); 248 | } 249 | return (*this)[idx]; 250 | } 251 | 252 | const_reference at( size_type idx ) const 253 | { 254 | if ( idx >= size() ) 255 | { 256 | throw std::out_of_range("veque::at(" + std::to_string(idx) + ") out of range"); 257 | } 258 | return (*this)[idx]; 259 | } 260 | 261 | reference operator[]( size_type idx ) 262 | { 263 | return *(begin() + idx); 264 | } 265 | 266 | const_reference operator[]( size_type idx ) const 267 | { 268 | return *(begin() + idx); 269 | } 270 | 271 | reference front() 272 | { 273 | return (*this)[0]; 274 | } 275 | 276 | const_reference front() const 277 | { 278 | return (*this)[0]; 279 | } 280 | 281 | reference back() 282 | { 283 | return (*this)[size() - 1]; 284 | } 285 | 286 | const_reference back() const 287 | { 288 | return (*this)[size() - 1]; 289 | } 290 | 291 | T * data() noexcept 292 | { 293 | return begin(); 294 | } 295 | 296 | const T * data() const noexcept 297 | { 298 | return begin(); 299 | } 300 | 301 | // Iterators 302 | const_iterator cbegin() const noexcept 303 | { 304 | return _storage_begin() + _offset; 305 | } 306 | 307 | iterator begin() noexcept 308 | { 309 | return _storage_begin() + _offset; 310 | } 311 | 312 | const_iterator begin() const noexcept 313 | { 314 | return cbegin(); 315 | } 316 | 317 | const_iterator cend() const noexcept 318 | { 319 | return _storage_begin() + _offset + size(); 320 | } 321 | 322 | iterator end() noexcept 323 | { 324 | return _storage_begin() + _offset + size(); 325 | } 326 | 327 | const_iterator end() const noexcept 328 | { 329 | return cend(); 330 | } 331 | 332 | const_reverse_iterator crbegin() const noexcept 333 | { 334 | return const_reverse_iterator(cend()); 335 | } 336 | 337 | reverse_iterator rbegin() noexcept 338 | { 339 | return reverse_iterator(end()); 340 | } 341 | 342 | const_reverse_iterator rbegin() const noexcept 343 | { 344 | return crbegin(); 345 | } 346 | 347 | const_reverse_iterator crend() const noexcept 348 | { 349 | return const_reverse_iterator(cbegin()); 350 | } 351 | 352 | reverse_iterator rend() noexcept 353 | { 354 | return reverse_iterator(begin()); 355 | } 356 | 357 | const_reverse_iterator rend() const noexcept 358 | { 359 | return crend(); 360 | } 361 | 362 | // Capacity 363 | [[nodiscard]] bool empty() const noexcept 364 | { 365 | return size() == 0; 366 | } 367 | 368 | size_type size() const noexcept 369 | { 370 | return _size; 371 | } 372 | 373 | ssize_type ssize() const noexcept 374 | { 375 | return _size; 376 | } 377 | 378 | size_type max_size() const noexcept 379 | { 380 | constexpr auto compile_time_limit = std::min( 381 | // The ssize type's ceiling 382 | std::numeric_limits::max() / sizeof(T), 383 | // Ceiling imposed by std::ratio math 384 | std::numeric_limits::max() / _full_realloc::num 385 | ); 386 | 387 | // The allocator's ceiling 388 | auto runtime_limit = alloc_traits::max_size(_allocator() ); 389 | 390 | return std::min( compile_time_limit, runtime_limit ); 391 | } 392 | 393 | // Reserve front and back capacity, in one operation. 394 | void reserve( size_type front, size_type back ) 395 | { 396 | if ( front > capacity_front() || back > capacity_back() ) 397 | { 398 | auto allocated_before_begin = std::max( capacity_front(), front ) - size(); 399 | auto allocated_after_begin = std::max( capacity_back(), back ); 400 | auto new_full_capacity = allocated_before_begin + allocated_after_begin; 401 | 402 | if ( new_full_capacity > max_size() ) 403 | { 404 | throw std::length_error("veque::reserve(" + std::to_string(front) + ", " + std::to_string(back) + ") exceeds max_size()"); 405 | } 406 | _reallocate( new_full_capacity, allocated_before_begin ); 407 | } 408 | } 409 | 410 | void reserve_front( size_type count ) 411 | { 412 | reserve( count, 0 ); 413 | } 414 | 415 | void reserve_back( size_type count ) 416 | { 417 | reserve( 0, count ); 418 | } 419 | 420 | void reserve( size_type count ) 421 | { 422 | reserve( count, count ); 423 | } 424 | 425 | // Returns current size + unused allocated storage before front() 426 | size_type capacity_front() const noexcept 427 | { 428 | return _offset + size(); 429 | } 430 | 431 | // Returns current size + unused allocated storage after back() 432 | size_type capacity_back() const noexcept 433 | { 434 | return capacity_full() - _offset; 435 | } 436 | 437 | // Returns current size + all unused allocated storage 438 | size_type capacity_full() const noexcept 439 | { 440 | return _data._allocated; 441 | } 442 | 443 | // To achieve interface parity with std::vector, capacity() returns capacity_back(); 444 | size_type capacity() const noexcept 445 | { 446 | return capacity_back(); 447 | } 448 | 449 | void shrink_to_fit() 450 | { 451 | if ( size() < capacity_full() ) 452 | { 453 | _reallocate( size(), 0 ); 454 | } 455 | } 456 | 457 | // Modifiers 458 | void clear() noexcept 459 | { 460 | _destroy( begin(), end() ); 461 | _size = 0; 462 | _offset = 0; 463 | if constexpr ( std::ratio_greater_v<_unused_realloc, std::ratio<0>> ) 464 | { 465 | using unused_front_ratio = std::ratio_divide<_front_realloc,_unused_realloc>; 466 | _offset = capacity_full() * unused_front_ratio::num / unused_front_ratio::den; 467 | } 468 | } 469 | 470 | iterator insert( const_iterator it, const T & value ) 471 | { 472 | return emplace( it, value ); 473 | } 474 | 475 | iterator insert( const_iterator it, T && value ) 476 | { 477 | return emplace( it, std::move(value) ); 478 | } 479 | 480 | iterator insert( const_iterator it, size_type count, const T & value ) 481 | { 482 | auto res = _insert_storage( it, count ); 483 | _value_construct_range( res, res + count, value ); 484 | return res; 485 | } 486 | 487 | template< typename InputIt, typename ItCat = typename std::iterator_traits::iterator_category > 488 | iterator insert( const_iterator it, InputIt b, InputIt e ) 489 | { 490 | return _insert( it, b, e, ItCat{} ); 491 | } 492 | 493 | iterator insert( const_iterator it, std::initializer_list lst ) 494 | { 495 | return insert( it, lst.begin(), lst.end() ); 496 | } 497 | 498 | template< typename ...Args > 499 | iterator emplace( const_iterator it, Args && ... args ) 500 | { 501 | auto res = _insert_storage( it, 1 ); 502 | alloc_traits::construct( _allocator(), res, std::forward(args)... ); 503 | return res; 504 | } 505 | 506 | iterator erase( const_iterator it ) 507 | { 508 | return erase( it, std::next(it) ); 509 | } 510 | 511 | iterator erase( const_iterator b, const_iterator e ) 512 | { 513 | auto count = std::distance( b, e ); 514 | if constexpr ( _resize_from_closest_side ) 515 | { 516 | auto elements_before = std::distance( cbegin(), b ); 517 | auto elements_after = std::distance( e, cend( ) ); 518 | if ( elements_before < elements_after ) 519 | { 520 | _shift_back( begin(), b, count ); 521 | _move_begin( count ); 522 | return _mutable_iterator(e); 523 | } 524 | } 525 | _shift_front( e, end(), count ); 526 | _move_end(-count); 527 | return _mutable_iterator(b); 528 | } 529 | 530 | void push_back( const T & value ) 531 | { 532 | emplace_back( value ); 533 | } 534 | 535 | void push_back( T && value ) 536 | { 537 | emplace_back( std::move(value) ); 538 | } 539 | 540 | template< typename ... Args> 541 | reference emplace_back( Args && ...args ) 542 | { 543 | if ( size() == capacity_back() ) 544 | { 545 | _reallocate_space_at_back( size() + 1 ); 546 | } 547 | alloc_traits::construct( _allocator(), end(), std::forward(args)... ); 548 | _move_end( 1 ); 549 | return back(); 550 | } 551 | 552 | void push_front( const T & value ) 553 | { 554 | emplace_front( value ); 555 | } 556 | 557 | void push_front( T && value ) 558 | { 559 | emplace_front( std::move(value) ); 560 | } 561 | 562 | template< typename ... Args> 563 | reference emplace_front( Args && ...args ) 564 | { 565 | if ( size() == capacity_front() ) 566 | { 567 | _reallocate_space_at_front( size() + 1 ); 568 | } 569 | alloc_traits::construct( _allocator(), begin()-1, std::forward(args)... ); 570 | _move_begin( -1 ); 571 | return front(); 572 | } 573 | 574 | void pop_back() 575 | { 576 | alloc_traits::destroy( _allocator(), &back() ); 577 | _move_end( -1 ); 578 | } 579 | 580 | // Move-savvy pop back with strong exception guarantee 581 | T pop_back_element() 582 | { 583 | auto res( _nothrow_construct_move(back()) ); 584 | pop_back(); 585 | return res; 586 | } 587 | 588 | void pop_front() 589 | { 590 | alloc_traits::destroy( _allocator(), &front() ); 591 | _move_begin( 1 ); 592 | } 593 | 594 | // Move-savvy pop front with strong exception guarantee 595 | T pop_front_element() 596 | { 597 | auto res( _nothrow_construct_move(front()) ); 598 | pop_front(); 599 | return res; 600 | } 601 | 602 | // Resizes the veque, by adding or removing from the front. 603 | void resize_front( size_type count ) 604 | { 605 | _resize_front( count ); 606 | } 607 | 608 | void resize_front( size_type count, const T & value ) 609 | { 610 | _resize_front( count, value ); 611 | } 612 | 613 | // Resizes the veque, by adding or removing from the back. 614 | void resize_back( size_type count ) 615 | { 616 | _resize_back( count ); 617 | } 618 | 619 | void resize_back( size_type count, const T & value ) 620 | { 621 | _resize_back( count, value ); 622 | } 623 | 624 | // To achieve interface parity with std::vector, resize() performs resize_back(); 625 | void resize( size_type count ) 626 | { 627 | _resize_back( count ); 628 | } 629 | 630 | void resize( size_type count, const T & value ) 631 | { 632 | _resize_back( count, value ); 633 | } 634 | 635 | template< typename OtherResizeTraits > 636 | void swap( veque & other ) noexcept( 637 | noexcept(alloc_traits::propagate_on_container_swap::value 638 | || alloc_traits::is_always_equal::value)) 639 | { 640 | if constexpr ( alloc_traits::propagate_on_container_swap::value ) 641 | { 642 | _swap_with_allocator( std::move(other) ); 643 | } 644 | else 645 | { 646 | if ( _allocator() == other._allocator() ) 647 | { 648 | _swap_without_allocator( std::move(other) ); 649 | } 650 | else 651 | { 652 | // std::vector would declare this UB. Allocate compatible storage and make it work. 653 | auto new_this = veque( _allocate_uninitialized_tag{}, other.size(), _allocator() ); 654 | _nothrow_move_construct_range( other.begin(), other.end(), new_this.begin() ); 655 | 656 | auto new_other = veque( _allocate_uninitialized_tag{}, size(), other._allocator() ); 657 | _nothrow_move_construct_range( begin(), end(), new_other.begin() ); 658 | 659 | _swap_without_allocator( std::move(new_this) ); 660 | other._swap_without_allocator( std::move(new_other) ); 661 | } 662 | } 663 | } 664 | 665 | private: 666 | 667 | using _front_realloc = typename ResizeTraits::allocation_before_front::type; 668 | using _back_realloc = typename ResizeTraits::allocation_after_back::type; 669 | using _unused_realloc = std::ratio_add< _front_realloc, _back_realloc >; 670 | using _full_realloc = std::ratio_add< std::ratio<1>, _unused_realloc >; 671 | 672 | static constexpr auto _resize_from_closest_side = ResizeTraits::resize_from_closest_side; 673 | 674 | static_assert( _front_realloc::den > 0 ); 675 | static_assert( _back_realloc::den > 0 ); 676 | static_assert( std::ratio_greater_equal_v<_front_realloc,std::ratio<0>>, "Reserving negative space is not well-defined" ); 677 | static_assert( std::ratio_greater_equal_v<_back_realloc,std::ratio<0>>, "Reserving negative space is not well-defined" ); 678 | static_assert( std::ratio_greater_equal_v<_unused_realloc,std::ratio<0>>, "Reserving negative space is not well-defined" ); 679 | 680 | // Confirmation that allocator_traits will only directly call placement new(ptr)T() 681 | static constexpr auto _calls_default_constructor_directly = 682 | std::is_same_v>; 683 | // Confirmation that allocator_traits will only directly call placement new(ptr)T(const T&) 684 | static constexpr auto _calls_copy_constructor_directly = 685 | std::is_same_v>; 686 | // Confirmation that allocator_traits will only directly call ~T() 687 | static constexpr auto _calls_destructor_directly = 688 | std::is_same_v>; 689 | 690 | size_type _size = 0; // Number of elements in use 691 | size_type _offset = 0; // Number of uninitialized elements before begin() 692 | 693 | // Deriving from allocator to leverage empty base optimization 694 | struct Data : Allocator 695 | { 696 | T *_storage = nullptr; 697 | size_type _allocated = 0; 698 | 699 | Data() = default; 700 | Data( size_type size, const Allocator & alloc ) 701 | : Allocator{alloc} 702 | , _storage{size ? std::allocator_traits::allocate( allocator(), size ) : nullptr} 703 | , _allocated{size} 704 | { 705 | } 706 | Data( const Data& ) = delete; 707 | Data( Data && other ) 708 | { 709 | *this = std::move(other); 710 | } 711 | ~Data() 712 | { 713 | if ( _storage ) 714 | { 715 | std::allocator_traits::deallocate( allocator(), _storage, _allocated ); 716 | } 717 | } 718 | Data& operator=( const Data & ) = delete; 719 | Data& operator=( Data && other ) 720 | { 721 | using std::swap; 722 | if constexpr( ! std::is_empty_v ) 723 | { 724 | swap(allocator(), other.allocator()); 725 | } 726 | swap(_allocated, other._allocated); 727 | swap(_storage, other._storage); 728 | return *this; 729 | } 730 | Allocator& allocator() { return *this; } 731 | const Allocator& allocator() const { return *this; } 732 | } _data; 733 | 734 | template< typename InputIt > 735 | veque( InputIt b, InputIt e, const Allocator & alloc, std::input_iterator_tag ) 736 | : veque{alloc} 737 | { 738 | for ( ; b != e; ++b ) 739 | { 740 | push_back( *b ); 741 | } 742 | } 743 | 744 | template< typename InputIt > 745 | veque( InputIt b, InputIt e, const Allocator & alloc, std::forward_iterator_tag ) 746 | : veque( _allocate_uninitialized_tag{}, std::distance( b, e ), alloc ) 747 | { 748 | _copy_construct_range( b, e, begin() ); 749 | } 750 | 751 | // Private tag to indicate initial allocation 752 | struct _allocate_uninitialized_tag {}; 753 | // Private tag to indicate resizing allocation 754 | struct _reallocate_uninitialized_tag {}; 755 | 756 | // Create an uninitialized empty veque, with specified storage params 757 | veque( _allocate_uninitialized_tag, size_type size, size_type allocated, size_type offset, const Allocator & alloc ) 758 | : _size{ size } 759 | , _offset{ offset } 760 | , _data { allocated, alloc } 761 | { 762 | } 763 | 764 | // Create an uninitialized empty veque, with storage for expected size 765 | veque( _allocate_uninitialized_tag, size_type size, const Allocator & alloc ) 766 | : veque( _allocate_uninitialized_tag{}, size, size, 0, alloc ) 767 | { 768 | } 769 | 770 | // Create an uninitialized empty veque, with storage for expected reallocated size 771 | veque( _reallocate_uninitialized_tag, size_type size, const Allocator & alloc ) 772 | : veque( _allocate_uninitialized_tag{}, size, _calc_reallocation(size), _calc_offset(size), alloc ) 773 | { 774 | } 775 | 776 | static constexpr size_type _calc_reallocation( size_type size ) 777 | { 778 | return size * _full_realloc::num / _full_realloc::den; 779 | } 780 | 781 | static constexpr size_type _calc_offset( size_type size ) 782 | { 783 | return size * _front_realloc::num / _front_realloc::den; 784 | } 785 | 786 | // Acquire Allocator 787 | Allocator& _allocator() noexcept 788 | { 789 | return _data.allocator(); 790 | } 791 | 792 | const Allocator& _allocator() const noexcept 793 | { 794 | return _data.allocator(); 795 | } 796 | 797 | // Destroy elements in range 798 | void _destroy( const_iterator b, const_iterator e ) 799 | { 800 | if constexpr ( std::is_trivially_destructible_v && _calls_destructor_directly ) 801 | { 802 | (void)b; (void)e; // Unused 803 | } 804 | else 805 | { 806 | auto start = _mutable_iterator(b); 807 | for ( auto i = start; i != e; ++i ) 808 | { 809 | alloc_traits::destroy( _allocator(), i ); 810 | } 811 | } 812 | } 813 | 814 | template< typename OtherResizeTraits > 815 | veque & _copy_assignment( const veque & other ) 816 | { 817 | if constexpr ( alloc_traits::propagate_on_container_copy_assignment::value ) 818 | { 819 | if constexpr ( !alloc_traits::is_always_equal::value ) 820 | { 821 | if ( other._allocator() != _allocator() || other.size() > capacity_full() ) 822 | { 823 | _swap_with_allocator( veque( other, other._allocator() ) ); 824 | return *this; 825 | } 826 | } 827 | } 828 | if ( other.size() > capacity_full() ) 829 | { 830 | _swap_without_allocator( veque( other, _allocator() ) ); 831 | } 832 | else 833 | { 834 | _reassign_existing_storage( other.begin(), other.end() ); 835 | } 836 | return *this; 837 | } 838 | 839 | template< typename OtherResizeTraits > 840 | veque & _move_assignment( veque && other ) noexcept( 841 | noexcept(alloc_traits::propagate_on_container_move_assignment::value 842 | || alloc_traits::is_always_equal::value) ) 843 | { 844 | if constexpr ( !alloc_traits::is_always_equal::value ) 845 | { 846 | if ( _allocator() != other._allocator() ) 847 | { 848 | if constexpr ( alloc_traits::propagate_on_container_move_assignment::value ) 849 | { 850 | _swap_with_allocator( std::move(other) ); 851 | } 852 | else 853 | { 854 | if ( other.size() > capacity_full() ) 855 | { 856 | _swap_without_allocator( veque( std::move(other), _allocator() ) ); 857 | } 858 | else 859 | { 860 | _reassign_existing_storage( std::move_iterator(other.begin()), std::move_iterator(other.end()) ); 861 | } 862 | } 863 | return *this; 864 | } 865 | } 866 | _swap_without_allocator( std::move(other) ); 867 | return *this; 868 | } 869 | 870 | // Construct elements in range 871 | template< typename ...Args > 872 | void _value_construct_range( const_iterator b, const_iterator e, const Args & ...args ) 873 | { 874 | static_assert( sizeof...(args) <= 1, "This is for default- or copy-constructing" ); 875 | 876 | if constexpr ( std::is_trivially_copy_constructible_v && _calls_default_constructor_directly ) 877 | { 878 | if constexpr ( sizeof...(args) == 0 ) 879 | { 880 | std::memset( _mutable_iterator(b), 0, std::distance( b, e ) * sizeof(T) ); 881 | } 882 | else 883 | { 884 | std::fill( _mutable_iterator(b), _mutable_iterator(e), args...); 885 | } 886 | } 887 | else 888 | { 889 | for ( auto dest = _mutable_iterator(b); dest != e; ++dest ) 890 | { 891 | alloc_traits::construct( _allocator(), dest, args... ); 892 | } 893 | } 894 | } 895 | 896 | template< typename It > 897 | void _copy_construct_range( It b, It e, const_iterator dest ) 898 | { 899 | static_assert( std::is_convertible_v::iterator_category,std::forward_iterator_tag> ); 900 | if constexpr ( std::is_trivially_copy_constructible_v && _calls_copy_constructor_directly ) 901 | { 902 | std::memcpy( _mutable_iterator(dest), b, std::distance( b, e ) * sizeof(T) ); 903 | } 904 | else 905 | { 906 | for ( ; b != e; ++dest, ++b ) 907 | { 908 | alloc_traits::construct( _allocator(), dest, *b ); 909 | } 910 | } 911 | } 912 | 913 | template< typename It > 914 | void _assign( It b, It e ) 915 | { 916 | static_assert( std::is_convertible_v::iterator_category,std::forward_iterator_tag> ); 917 | if ( std::distance( b, e ) > static_cast(capacity_full()) ) 918 | { 919 | _swap_without_allocator( veque( b, e, _allocator() ) ); 920 | } 921 | else 922 | { 923 | _reassign_existing_storage( b, e ); 924 | } 925 | } 926 | 927 | template< typename It > 928 | void _assign( It b, It e, std::forward_iterator_tag ) 929 | { 930 | _assign( b, e ); 931 | } 932 | 933 | template< typename It > 934 | void _assign( It b, It e, std::input_iterator_tag ) 935 | { 936 | // Input Iterators require a single-pass solution 937 | clear(); 938 | for ( ; b != e; ++b ) 939 | { 940 | push_back( *b ); 941 | } 942 | } 943 | 944 | template< typename It > 945 | iterator _insert( const_iterator it, It b, It e ) 946 | { 947 | static_assert( std::is_convertible_v::iterator_category,std::forward_iterator_tag> ); 948 | auto res = _insert_storage( it, std::distance( b, e ) ); 949 | _copy_construct_range( b, e, res ); 950 | return res; 951 | } 952 | 953 | template< typename It > 954 | iterator _insert( const_iterator it, It b, It e, std::forward_iterator_tag ) 955 | { 956 | return _insert( it, b, e ); 957 | } 958 | 959 | template< typename It > 960 | iterator _insert( const_iterator it, It b, It e, std::input_iterator_tag ) 961 | { 962 | // Input Iterators require a single-pass solution 963 | auto allocated = veque( b, e ); 964 | _insert( it, allocated.begin(), allocated.end() ); 965 | } 966 | 967 | template< typename OtherResizeTraits > 968 | void _swap_with_allocator( veque && other ) noexcept 969 | { 970 | // Swap everything 971 | std::swap( _size, other._size ); 972 | std::swap( _offset, other._offset ); 973 | std::swap( _data, other._data ); 974 | } 975 | 976 | template< typename OtherResizeTraits > 977 | void _swap_without_allocator( veque && other ) noexcept 978 | { 979 | // Don't swap _data.allocator(). 980 | std::swap( _size, other._size ); 981 | std::swap( _offset, other._offset ); 982 | std::swap( _data._allocated, other._data._allocated); 983 | std::swap( _data._storage, other._data._storage); 984 | } 985 | 986 | template< typename ...Args > 987 | void _resize_front( size_type count, const Args & ...args ) 988 | { 989 | difference_type delta = count - size(); 990 | if ( delta > 0 ) 991 | { 992 | if ( count > capacity_front() ) 993 | { 994 | _reallocate_space_at_front( count ); 995 | } 996 | _value_construct_range( begin() - delta, begin(), args... ); 997 | } 998 | else 999 | { 1000 | _destroy( begin(), begin() - delta ); 1001 | } 1002 | _move_begin( -delta ); 1003 | } 1004 | 1005 | template< typename ...Args > 1006 | void _resize_back( size_type count, const Args & ...args ) 1007 | { 1008 | difference_type delta = count - size(); 1009 | if ( delta > 0 ) 1010 | { 1011 | if ( count > capacity_back() ) 1012 | { 1013 | _reallocate_space_at_back( count ); 1014 | } 1015 | _value_construct_range( end(), end() + delta, args... ); 1016 | } 1017 | else 1018 | { 1019 | _destroy( end() + delta, end() ); 1020 | } 1021 | _move_end( delta ); 1022 | } 1023 | 1024 | // Move veque to new storage, with specified capacity... 1025 | // ...and yet-unused space at back of this storage 1026 | void _reallocate_space_at_back( size_type count ) 1027 | { 1028 | auto storage_needed = _calc_reallocation(count); 1029 | auto current_capacity = capacity_full(); 1030 | auto new_offset = _calc_offset(count); 1031 | if ( storage_needed <= current_capacity ) 1032 | { 1033 | // Shift elements toward front 1034 | auto distance = _offset - new_offset; 1035 | _shift_front( begin(), end(), distance ); 1036 | _move_begin(-distance); 1037 | _move_end(-distance); 1038 | } 1039 | else 1040 | { 1041 | _reallocate( storage_needed, new_offset ); 1042 | } 1043 | } 1044 | 1045 | // ...and yet-unused space at front of this storage 1046 | void _reallocate_space_at_front( size_type count ) 1047 | { 1048 | auto storage_needed = _calc_reallocation(count); 1049 | auto current_capacity = capacity_full(); 1050 | auto new_offset = count - size() + _calc_offset(count); 1051 | if ( storage_needed <= current_capacity ) 1052 | { 1053 | // Shift elements toward back 1054 | auto distance = new_offset - _offset; 1055 | _shift_back( begin(), end(), distance ); 1056 | _move_begin(distance); 1057 | _move_end(distance); 1058 | } 1059 | else 1060 | { 1061 | _reallocate( storage_needed, new_offset ); 1062 | } 1063 | } 1064 | 1065 | // Move veque to new storage, with specified capacity 1066 | void _reallocate( size_type allocated, size_type offset ) 1067 | { 1068 | auto replacement = veque( _allocate_uninitialized_tag{}, size(), allocated, offset, _allocator() ); 1069 | _nothrow_move_construct_range( begin(), end(), replacement.begin() ); 1070 | _swap_without_allocator( std::move(replacement) ); 1071 | } 1072 | 1073 | // Insert empty space, choosing the most efficient way to shift existing elements 1074 | iterator _insert_storage( const_iterator it, size_type count ) 1075 | { 1076 | auto required_size = size() + count; 1077 | auto can_shift_back = capacity_back() >= required_size; 1078 | if constexpr ( std::ratio_greater_v<_front_realloc,std::ratio<0>> ) 1079 | { 1080 | if ( can_shift_back && it == begin() ) 1081 | { 1082 | // Don't favor shifting entire contents back 1083 | // if realloc will create space 1084 | can_shift_back = false; 1085 | } 1086 | } 1087 | 1088 | if constexpr ( _resize_from_closest_side ) 1089 | { 1090 | auto can_shift_front = capacity_front() >= required_size; 1091 | if constexpr ( std::ratio_greater_v<_back_realloc,std::ratio<0>> ) 1092 | { 1093 | if ( can_shift_front && it == end() ) 1094 | { 1095 | // Don't favor shifting entire contents front 1096 | // if realloc will create space 1097 | can_shift_front = false; 1098 | } 1099 | } 1100 | 1101 | if ( can_shift_back && can_shift_front) 1102 | { 1103 | // Capacity allows shifting in either direction. 1104 | // Remove the choice with the greater operation count. 1105 | auto index = std::distance( cbegin(), it ); 1106 | if ( index <= ssize() / 2 ) 1107 | { 1108 | can_shift_back = false; 1109 | } 1110 | else 1111 | { 1112 | can_shift_front = false; 1113 | } 1114 | } 1115 | 1116 | if ( can_shift_front ) 1117 | { 1118 | _shift_front( begin(), it, count ); 1119 | _move_begin( -count ); 1120 | return _mutable_iterator(it) - count; 1121 | } 1122 | } 1123 | if ( can_shift_back ) 1124 | { 1125 | _shift_back( it, end(), count ); 1126 | _move_end( count ); 1127 | return _mutable_iterator(it); 1128 | } 1129 | 1130 | // Insufficient capacity. Allocate new storage. 1131 | auto replacement = veque( _reallocate_uninitialized_tag{}, required_size, _allocator() ); 1132 | auto index = std::distance( cbegin(), it ); 1133 | auto insertion_point = begin() + index; 1134 | 1135 | _nothrow_move_construct_range( begin(), insertion_point, replacement.begin() ); 1136 | _nothrow_move_construct_range( insertion_point, end(), replacement.begin() + index + count ); 1137 | _swap_with_allocator( std::move(replacement) ); 1138 | return begin() + index; 1139 | } 1140 | 1141 | // Moves a valid subrange in the front direction. 1142 | // Veque will grow, if range moves past begin(). 1143 | // Veque will shrink if range includes end(). 1144 | // Returns iterator to beginning of destructed gap 1145 | void _shift_front( const_iterator b, const_iterator e, size_type count ) 1146 | { 1147 | if ( e == begin() ) 1148 | { 1149 | return; 1150 | } 1151 | auto element_count = std::distance( b, e ); 1152 | auto start = _mutable_iterator(b); 1153 | if ( element_count > 0 ) 1154 | { 1155 | auto dest = start - count; 1156 | if constexpr ( std::is_trivially_copyable_v && std::is_trivially_copy_constructible_v && _calls_copy_constructor_directly ) 1157 | { 1158 | std::memmove( dest, start, element_count * sizeof(T) ); 1159 | } 1160 | else 1161 | { 1162 | auto src = start; 1163 | auto dest_construct_end = std::min( begin(), _mutable_iterator(e) - count ); 1164 | for ( ; dest < dest_construct_end; ++src, ++dest ) 1165 | { 1166 | _nothrow_move_construct( dest, src ); 1167 | } 1168 | for ( ; src != e; ++src, ++dest ) 1169 | { 1170 | _nothrow_move_assign( dest, src ); 1171 | } 1172 | } 1173 | } 1174 | _destroy( std::max( cbegin(), e - count ), e ); 1175 | } 1176 | 1177 | // Moves a range towards the back. Veque will grow, if needed. Vacated elements are destructed. 1178 | // Moves a valid subrange in the back direction. 1179 | // Veque will grow, if range moves past end(). 1180 | // Veque will shrink if range includes begin(). 1181 | // Returns iterator to beginning of destructed gap 1182 | void _shift_back( const_iterator b, const_iterator e, size_type count ) 1183 | { 1184 | auto start = _mutable_iterator(b); 1185 | if ( b == end() ) 1186 | { 1187 | return; 1188 | } 1189 | auto element_count = std::distance( b, e ); 1190 | if ( element_count > 0 ) 1191 | { 1192 | if constexpr ( std::is_trivially_copyable_v && std::is_trivially_copy_constructible_v && _calls_copy_constructor_directly ) 1193 | { 1194 | std::memmove( start + count, start, element_count * sizeof(T) ); 1195 | } 1196 | else 1197 | { 1198 | auto src = _mutable_iterator(e-1); 1199 | auto dest = src + count; 1200 | auto dest_construct_end = std::max( end()-1, dest - element_count ); 1201 | for ( ; dest > dest_construct_end; --src, --dest ) 1202 | { 1203 | // Construct to destinations at or after end() 1204 | _nothrow_move_construct( dest, src ); 1205 | } 1206 | for ( ; src != b-1; --src, --dest ) 1207 | { 1208 | // Assign to destinations before before end() 1209 | _nothrow_move_assign( dest, src ); 1210 | } 1211 | } 1212 | } 1213 | _destroy( b, std::min( cend(), b + count ) ); 1214 | } 1215 | 1216 | // Assigns a fitting range of new elements to currently held storage. 1217 | // Favors copying over constructing firstly, and positioning the new elements 1218 | // at the center of storage secondly 1219 | template< typename It > 1220 | void _reassign_existing_storage( It b, It e ) 1221 | { 1222 | static_assert( std::is_convertible_v::iterator_category,std::forward_iterator_tag> ); 1223 | 1224 | auto count = std::distance( b, e ); 1225 | auto size_delta = static_cast( count - size() ); 1226 | // The "ideal" begin would put the new data in the center of storage 1227 | auto ideal_begin = _storage_begin() + (capacity_full() - count) / 2; 1228 | 1229 | if ( size() == 0 ) 1230 | { 1231 | // Existing veque is empty. Construct at the ideal location 1232 | _copy_construct_range( b, e, ideal_begin ); 1233 | } 1234 | else if ( size_delta == 0 ) 1235 | { 1236 | // Existing veque is the same size. Avoid any construction by copy-assigning everything 1237 | std::copy( b, e, begin() ); 1238 | return; 1239 | } 1240 | else if ( size_delta < 0 ) 1241 | { 1242 | // New size is smaller. Copy-assign everything, placing results as close to center as possible 1243 | ideal_begin = std::clamp( ideal_begin, begin(), end() - count ); 1244 | 1245 | _destroy( begin(), ideal_begin ); 1246 | auto ideal_end = std::copy( b, e, ideal_begin ); 1247 | _destroy( ideal_end, end() ); 1248 | } 1249 | else 1250 | { 1251 | // New size is larger. Copy-assign all existing elements, placing newly 1252 | // constructed elements so final store is as close to center as possible 1253 | ideal_begin = std::clamp( ideal_begin, end() - count, begin() ); 1254 | 1255 | auto src = b; 1256 | auto copy_src = src + std::distance( ideal_begin, begin() ); 1257 | _copy_construct_range( src, copy_src, ideal_begin ); 1258 | std::copy( copy_src, copy_src + ssize(), begin() ); 1259 | _copy_construct_range( copy_src + ssize(), e, end() ); 1260 | } 1261 | _move_begin( std::distance( begin(), ideal_begin ) ); 1262 | _move_end( std::distance( end(), ideal_begin + count ) ); 1263 | } 1264 | 1265 | void _reassign_existing_storage( size_type count, const T & value ) 1266 | { 1267 | auto size_delta = static_cast( count - size() ); 1268 | auto ideal_begin = _storage_begin(); 1269 | // The "ideal" begin would put the new data in the center of storage 1270 | if constexpr ( std::ratio_greater_v<_unused_realloc, std::ratio<0>> ) 1271 | { 1272 | using ideal_begin_ratio = std::ratio_divide<_front_realloc, _unused_realloc >; 1273 | ideal_begin += (capacity_full() - count) * ideal_begin_ratio::num / ideal_begin_ratio::den; 1274 | } 1275 | 1276 | if ( size() == 0 ) 1277 | { 1278 | // Existing veque is empty. Construct at the ideal location 1279 | _value_construct_range( ideal_begin, ideal_begin + count, value ); 1280 | } 1281 | else if ( size_delta == 0 ) 1282 | { 1283 | // Existing veque is the same size. Avoid any construction by copy-assigning everything 1284 | std::fill( begin(), end(), value ); 1285 | return; 1286 | } 1287 | else if ( size_delta < 0 ) 1288 | { 1289 | // New size is smaller. Copy-assign everything, placing results as close to center as possible 1290 | ideal_begin = std::clamp( ideal_begin, begin(), end() - count ); 1291 | 1292 | _destroy( begin(), ideal_begin ); 1293 | std::fill( ideal_begin, ideal_begin + count, value ); 1294 | _destroy( ideal_begin + count, end() ); 1295 | } 1296 | else 1297 | { 1298 | // New size is larger. Copy-assign all existing elements, placing newly 1299 | // constructed elements so final store is as close to center as possible 1300 | ideal_begin += _calc_offset(capacity_full() - count) / 2; 1301 | _value_construct_range( ideal_begin, begin(), value ); 1302 | std::fill( begin(), end(), value ); 1303 | _value_construct_range( end(), ideal_begin + count, value ); 1304 | } 1305 | _move_begin( std::distance( begin(), ideal_begin ) ); 1306 | _move_end( std::distance( end(), ideal_begin + count ) ); 1307 | } 1308 | 1309 | // Casts to T&& or T&, depending on whether move construction is noexcept 1310 | static decltype(auto) _nothrow_construct_move( T & t ) 1311 | { 1312 | if constexpr ( std::is_nothrow_move_constructible_v ) 1313 | { 1314 | return std::move(t); 1315 | } 1316 | else 1317 | { 1318 | return t; 1319 | } 1320 | } 1321 | 1322 | // Move-constructs if noexcept, copies otherwise 1323 | void _nothrow_move_construct( iterator dest, iterator src ) 1324 | { 1325 | if constexpr ( std::is_trivially_copy_constructible_v && _calls_copy_constructor_directly ) 1326 | { 1327 | *dest = *src; 1328 | } 1329 | else 1330 | { 1331 | alloc_traits::construct( _allocator(), dest, _nothrow_construct_move(*src) ); 1332 | } 1333 | } 1334 | 1335 | void _nothrow_move_construct_range( iterator b, iterator e, iterator dest ) 1336 | { 1337 | auto size = std::distance( b, e ); 1338 | if ( size ) 1339 | { 1340 | if constexpr ( std::is_trivially_copy_constructible_v && _calls_copy_constructor_directly ) 1341 | { 1342 | std::memcpy( dest, b, size * sizeof(T) ); 1343 | } 1344 | else 1345 | { 1346 | for ( ; b != e; ++dest, ++b ) 1347 | { 1348 | _nothrow_move_construct( dest, b ); 1349 | } 1350 | } 1351 | } 1352 | } 1353 | 1354 | // Move-assigns if noexcept, copies otherwise 1355 | static void _nothrow_move_assign( iterator dest, iterator src ) 1356 | { 1357 | if constexpr ( std::is_nothrow_move_assignable_v ) 1358 | { 1359 | *dest = std::move(*src); 1360 | } 1361 | else 1362 | { 1363 | *dest = *src; 1364 | } 1365 | } 1366 | 1367 | static void _nothrow_move_assign_range( iterator b, iterator e, iterator src ) 1368 | { 1369 | for ( auto dest = b; dest != e; ++dest, ++src ) 1370 | { 1371 | _nothrow_move_assign( dest, src ); 1372 | } 1373 | } 1374 | 1375 | // Adjust begin(), end() iterators 1376 | void _move_begin( difference_type count ) noexcept 1377 | { 1378 | _size -= count; 1379 | _offset += count; 1380 | } 1381 | 1382 | void _move_end( difference_type count ) noexcept 1383 | { 1384 | _size += count; 1385 | } 1386 | 1387 | // Convert a local const_iterator to iterator 1388 | iterator _mutable_iterator( const_iterator i ) 1389 | { 1390 | return begin() + std::distance( cbegin(), i ); 1391 | } 1392 | 1393 | // Retrieves beginning of storage, which may be before begin() 1394 | const_iterator _storage_begin() const noexcept 1395 | { 1396 | return _data._storage; 1397 | } 1398 | 1399 | iterator _storage_begin() noexcept 1400 | { 1401 | return _data._storage; 1402 | } 1403 | }; 1404 | 1405 | template< typename T, typename LResizeTraits, typename LAlloc, typename RResizeTraits, typename RAlloc > 1406 | inline bool operator==( const veque &lhs, const veque &rhs ) 1407 | { 1408 | return std::equal( lhs.begin(), lhs.end(), rhs.begin(), rhs.end() ); 1409 | } 1410 | 1411 | template< typename T, typename LResizeTraits, typename LAlloc, typename RResizeTraits, typename RAlloc > 1412 | inline bool operator!=( const veque &lhs, const veque &rhs ) 1413 | { 1414 | return !( lhs == rhs ); 1415 | } 1416 | 1417 | template< typename T, typename LResizeTraits, typename LAlloc, typename RResizeTraits, typename RAlloc > 1418 | inline bool operator<( const veque &lhs, const veque &rhs ) 1419 | { 1420 | return std::lexicographical_compare( lhs.begin(), lhs.end(), rhs.begin(), rhs.end() ); 1421 | } 1422 | 1423 | template< typename T, typename LResizeTraits, typename LAlloc, typename RResizeTraits, typename RAlloc > 1424 | inline bool operator<=( const veque &lhs, const veque &rhs ) 1425 | { 1426 | return !( rhs < lhs ); 1427 | } 1428 | 1429 | template< typename T, typename LResizeTraits, typename LAlloc, typename RResizeTraits, typename RAlloc > 1430 | inline bool operator>( const veque &lhs, const veque &rhs ) 1431 | { 1432 | return ( rhs < lhs ); 1433 | } 1434 | 1435 | template< typename T, typename LResizeTraits, typename LAlloc, typename RResizeTraits, typename RAlloc > 1436 | inline bool operator>=( const veque &lhs, const veque &rhs ) 1437 | { 1438 | return !( lhs < rhs ); 1439 | } 1440 | 1441 | template< typename T, typename ResizeTraits, typename Alloc > 1442 | inline void swap( veque & lhs, veque & rhs ) noexcept(noexcept(lhs.swap(rhs))) 1443 | { 1444 | lhs.swap(rhs); 1445 | } 1446 | 1447 | // Template deduction guide for iterator pair 1448 | template< typename InputIt, 1449 | typename Alloc = std::allocator::value_type>> 1450 | veque(InputIt, InputIt, Alloc = Alloc()) 1451 | -> veque::value_type, fast_resize_traits, Alloc>; 1452 | 1453 | } 1454 | 1455 | namespace std 1456 | { 1457 | template< typename T, typename ResizeTraits, typename Alloc > 1458 | struct hash> 1459 | { 1460 | size_t operator()( const veque::veque & v ) const 1461 | { 1462 | size_t hash = 0; 1463 | auto hasher = std::hash(); 1464 | for ( auto && val : v ) 1465 | { 1466 | hash ^= hasher(val) + 0x9e3779b9 + (hash<<6) + (hash>>2); 1467 | } 1468 | return hash; 1469 | } 1470 | }; 1471 | } 1472 | 1473 | #endif 1474 | -------------------------------------------------------------------------------- /performance/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * veque performance test 4 | * 5 | * Copyright (C) 2019 Drew Dormann 6 | * 7 | * SAMPLE OUTPUT (g++-9 -O3) 8 | 9 | 10 | Test std::deque std::vector veque 11 | ----------------------------------------------------------------------- 12 | Resizing: 500,704us 201,622us 166,267us 13 | Back growth: 342,218us 735,035us 552,027us 14 | Front growth: 16,386us 6,685,217us 30,739us 15 | Arbitrary insertion: 913,553us 1,092,013us 529,713us 16 | Iteration: 265,642us 203,040us 207,987us 17 | Random operations: 491,843us 455,366us 260,006us 18 | Reassignment: 949,994us 876,424us 746,213us 19 | Total: 3,480,344us 10,248,719us 2,492,954us 20 | 21 | */ 22 | 23 | #include "veque.hpp" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | struct LargeTrivialObject 39 | { 40 | std::array data; 41 | }; 42 | 43 | struct NonTrivialObject 44 | { 45 | std::string data = std::string(1024, 'W'); 46 | 47 | bool operator==(const NonTrivialObject & other) const { 48 | return data == other.data; 49 | } 50 | }; 51 | 52 | struct ThrowingMoveConstructObject 53 | { 54 | ThrowingMoveConstructObject() = default; 55 | ThrowingMoveConstructObject(const ThrowingMoveConstructObject&) = default; 56 | 57 | ThrowingMoveConstructObject(ThrowingMoveConstructObject&&) noexcept(false) { 58 | throw std::runtime_error("Failed move construction"); 59 | } 60 | 61 | ThrowingMoveConstructObject& operator=(const ThrowingMoveConstructObject&) = default; 62 | ThrowingMoveConstructObject& operator=(ThrowingMoveConstructObject&&) = default; 63 | 64 | bool operator==(const ThrowingMoveConstructObject & other) const { 65 | return data == other.data; 66 | } 67 | std::string data = std::string(1024, 'X'); 68 | }; 69 | 70 | struct ThrowingMoveAssignObject 71 | { 72 | ThrowingMoveAssignObject() = default; 73 | ThrowingMoveAssignObject(const ThrowingMoveAssignObject&) = default; 74 | ThrowingMoveAssignObject(ThrowingMoveAssignObject&&) = default; 75 | 76 | ThrowingMoveAssignObject& operator=(const ThrowingMoveAssignObject&) = default; 77 | 78 | ThrowingMoveAssignObject& operator=(ThrowingMoveAssignObject&&) noexcept(false) { 79 | throw std::runtime_error("Failed move assignment"); 80 | } 81 | 82 | bool operator==(const ThrowingMoveAssignObject & other) const { 83 | return data == other.data; 84 | } 85 | std::string data = std::string(1024, 'Y'); 86 | }; 87 | 88 | struct ThrowingMoveObject 89 | { 90 | ThrowingMoveObject() = default; 91 | ThrowingMoveObject(const ThrowingMoveObject&) = default; 92 | 93 | ThrowingMoveObject(ThrowingMoveObject&&) noexcept(false) { 94 | throw std::runtime_error("Failed move construction"); 95 | } 96 | 97 | ThrowingMoveObject& operator=(const ThrowingMoveObject&) = default; 98 | 99 | ThrowingMoveObject& operator=(ThrowingMoveObject&&) noexcept(false) { 100 | throw std::runtime_error("Failed move assignment"); 101 | } 102 | 103 | bool operator==(const ThrowingMoveObject & other) const { 104 | return data == other.data; 105 | } 106 | std::string data = std::string(1024, 'Z'); 107 | }; 108 | 109 | template< typename Container > 110 | int reassignment_test(int i) 111 | { 112 | // Valgrind doesn't like std::random_device. 113 | //std::random_device rd; 114 | //std::mt19937 gen(rd()); 115 | srand(time(NULL)); 116 | for ( int x = 0; x < 2'000; ++x ) 117 | { 118 | auto v1 = Container( rand() % 100 ); 119 | auto v2 = Container( rand() % 100 ); 120 | auto v3 = Container( rand() % 100 ); 121 | v1 = v2; 122 | v3 = std::move(v2); 123 | i += *reinterpret_cast(&v1); 124 | } 125 | 126 | for ( int x = 0; x < 2'000; ++x ) 127 | { 128 | auto v1 = Container( rand() % 100 ); 129 | auto v2 = Container( rand() % 100 ); 130 | auto v3 = Container( rand() % 100 ); 131 | v1.assign( v2.begin(), v2.end() ); 132 | i += *reinterpret_cast(&v1); 133 | } 134 | return i; 135 | } 136 | 137 | template< typename Container > 138 | int resizing_test(int i) 139 | { 140 | using ValType = typename Container::value_type; 141 | 142 | for ( int y = 0; y != 100; ++y ) 143 | { 144 | Container v(5); 145 | 146 | v.resize(15); 147 | ValType x = v[0]; 148 | i += *reinterpret_cast(&x); 149 | v.resize(20); 150 | x = v[0]; 151 | i += *reinterpret_cast(&x); 152 | v.resize(25, ValType{}); 153 | x = v[0]; 154 | i += *reinterpret_cast(&x); 155 | v.resize(30); 156 | x = v[0]; 157 | i += *reinterpret_cast(&x); 158 | v.resize(35, ValType{}); 159 | x = v[0]; 160 | i += *reinterpret_cast(&x); 161 | v.resize(999); 162 | x = v[0]; 163 | i += *reinterpret_cast(&x); 164 | v.resize(0); 165 | v.resize(999, ValType{}); 166 | x = v[0]; 167 | i += *reinterpret_cast(&x); 168 | v.resize(5); 169 | x = v[0]; 170 | i += *reinterpret_cast(&x); 171 | } 172 | return i; 173 | } 174 | 175 | template< typename Container > 176 | int back_growth_test(int i) 177 | { 178 | using ValType = typename Container::value_type; 179 | 180 | for ( int y = 0; y != 20; ++y ) 181 | { 182 | { 183 | typename Container::size_type size = 5; 184 | Container v(size); 185 | 186 | ValType val{}; 187 | for (int i = 0; i < 2'000; ++i) { 188 | v.push_back(val); 189 | ++size; 190 | } 191 | while (v.size()) { 192 | ValType x = v.back(); 193 | v.pop_back(); 194 | --size; 195 | i += *reinterpret_cast(&x); 196 | } 197 | } 198 | 199 | { 200 | typename Container::size_type size = 5; 201 | Container v(size); 202 | 203 | for (int i = 0; i < 2'000; ++i) { 204 | v.push_back(ValType{}); 205 | ++size; 206 | } 207 | while (v.size()) { 208 | ValType x = v.back(); 209 | v.pop_back(); 210 | --size; 211 | i += *reinterpret_cast(&x); 212 | } 213 | } 214 | 215 | { 216 | typename Container::size_type size = 5; 217 | Container v(size); 218 | 219 | for (int i = 0; i < 2'000; ++i) { 220 | v.emplace_back(); 221 | ++size; 222 | } 223 | while (v.size()) { 224 | ValType x = v.back(); 225 | v.pop_back(); 226 | --size; 227 | i += *reinterpret_cast(&x); 228 | } 229 | } 230 | } 231 | 232 | return i; 233 | } 234 | 235 | template< typename Container > 236 | int front_growth_test(int i) 237 | { 238 | using ValType = typename Container::value_type; 239 | 240 | { 241 | typename Container::size_type size = 5; 242 | Container v(size); 243 | 244 | ValType val{}; 245 | for (int i = 0; i < 2'000; ++i) { 246 | if constexpr( std::is_same_v>) 247 | v.insert(v.begin(), val); 248 | else 249 | v.push_front(val); 250 | ++size; 251 | } 252 | while (v.size()) { 253 | ValType x = v.back(); 254 | v.pop_back(); 255 | i += *reinterpret_cast(&x); 256 | --size; 257 | } 258 | } 259 | 260 | 261 | { 262 | typename Container::size_type size = 5; 263 | Container v(size); 264 | 265 | for (int i = 0; i < 2'000; ++i) { 266 | if constexpr( std::is_same_v>) 267 | v.insert(v.begin(), ValType{}); 268 | else 269 | v.push_front(ValType{}); 270 | ++size; 271 | } 272 | while (v.size()) { 273 | if constexpr( std::is_same_v>) 274 | { 275 | ValType x = v.pop_back_element(); 276 | i += *reinterpret_cast(&x); 277 | } 278 | else 279 | { 280 | // Closest functionality match 281 | ValType x = v.back(); 282 | v.pop_back(); 283 | i += *reinterpret_cast(&x); 284 | } 285 | } 286 | } 287 | 288 | 289 | { 290 | typename Container::size_type size = 5; 291 | Container v(size); 292 | 293 | for (int i = 0; i < 2'000; ++i) { 294 | if constexpr( std::is_same_v>) 295 | v.emplace(v.begin()); 296 | else 297 | v.emplace_front(); 298 | ++size; 299 | } 300 | while (v.size()) { 301 | if constexpr( std::is_same_v>) 302 | { 303 | ValType x = v.pop_front_element(); 304 | i += *reinterpret_cast(&x); 305 | } 306 | else if constexpr( std::is_same_v>) 307 | { 308 | ValType x = v.front(); 309 | v.erase( v.begin() ); 310 | i += *reinterpret_cast(&x); 311 | } 312 | else 313 | { 314 | // Closest functionality match 315 | ValType x = v.front(); 316 | v.pop_front(); 317 | i += *reinterpret_cast(&x); 318 | } 319 | } 320 | } 321 | return i; 322 | } 323 | 324 | template< typename Container > 325 | int arbitrary_insertion_test(int i) 326 | { 327 | using ValType = typename Container::value_type; 328 | 329 | { 330 | typename Container::size_type size = 5; 331 | Container v(size); 332 | 333 | for (int i = 0; i < 1'000; ++i) { 334 | ValType val{}; 335 | v.insert(v.begin(), val); 336 | ++size; 337 | } 338 | while (v.size()) { 339 | ValType x = v.back(); 340 | v.pop_back(); 341 | --size; 342 | i += *reinterpret_cast(&x); 343 | } 344 | } 345 | 346 | { 347 | typename Container::size_type size = 5; 348 | Container v(size); 349 | 350 | for (int i = 0; i < 1'000; ++i) { 351 | ValType val{}; 352 | v.insert(v.end(), val); 353 | ++size; 354 | } 355 | while (v.size()) { 356 | ValType x = v.back(); 357 | v.pop_back(); 358 | --size; 359 | i += *reinterpret_cast(&x); 360 | } 361 | } 362 | 363 | { 364 | typename Container::size_type size = 5; 365 | Container v(size); 366 | 367 | for (int i = 0; i < 1'000; ++i) { 368 | ValType val{}; 369 | v.insert(v.begin() + v.size() / 2, val); 370 | ++size; 371 | } 372 | while (v.size()) { 373 | ValType x = v.back(); 374 | v.pop_back(); 375 | --size; 376 | i += *reinterpret_cast(&x); 377 | } 378 | } 379 | 380 | { 381 | typename Container::size_type size = 5; 382 | Container v(size); 383 | 384 | for (int i = 0; i < 1'000; ++i) { 385 | ValType val{}; 386 | v.insert(v.begin() + v.size() / 3, val); 387 | ++size; 388 | } 389 | while (v.size()) { 390 | ValType x = v.back(); 391 | v.pop_back(); 392 | --size; 393 | i += *reinterpret_cast(&x); 394 | } 395 | } 396 | 397 | { 398 | typename Container::size_type size = 5; 399 | Container v(size); 400 | 401 | for (int i = 0; i < 1'000; ++i) { 402 | v.insert(v.begin() + 2 * v.size() / 3, {}); 403 | ++size; 404 | } 405 | while (v.size()) { 406 | ValType x = v.front(); 407 | v.erase(v.begin()); 408 | --size; 409 | i += *reinterpret_cast(&x); 410 | } 411 | } 412 | 413 | { 414 | typename Container::size_type size = 5; 415 | Container v(size); 416 | 417 | // Valgrind doesn't like std::random_device. 418 | //std::random_device rd; 419 | //std::mt19937 gen(rd()); 420 | for (int i = 0; i < 1'000; ++i) { 421 | ValType val{}; 422 | //auto index = std::uniform_int_distribution<>(0, v.size())(gen); 423 | auto index = rand() % (v.size() + 1); 424 | //v.insert( v.begin() + dis(gen), val ); 425 | v.insert(v.begin() + index, val); 426 | ++size; 427 | } 428 | while (v.size()) { 429 | ValType x = v.front(); 430 | v.erase(v.begin()); 431 | --size; 432 | i += *reinterpret_cast(&x); 433 | } 434 | } 435 | return i; 436 | } 437 | 438 | template< typename Container > 439 | int iteration_test(int i) 440 | { 441 | using ValType = typename Container::value_type; 442 | 443 | Container v(2'000'000); 444 | 445 | for (const ValType & val : v) 446 | { 447 | i += *reinterpret_cast(&val); 448 | } 449 | 450 | return i; 451 | } 452 | 453 | // Sample data, each in increasing comparison order 454 | template const T val; 455 | template<> const int val = 0; 456 | template<> const int val = 1; 457 | template<> const int val = 2; 458 | template<> const int val = 3; 459 | template<> const int val = 4; 460 | template<> const std::string val = std::string(100, 'A'); 461 | template<> const std::string val = std::string(200, 'B'); 462 | template<> const std::string val = std::string(300, 'C'); 463 | template<> const std::string val = std::string(400, 'D'); 464 | template<> const std::string val = std::string(500, 'E'); 465 | template<> const double val = 00.0; 466 | template<> const double val = 11.0; 467 | template<> const double val = 22.0; 468 | template<> const double val = 33.0; 469 | template<> const double val = 44.0; 470 | template<> const std::vector val,0> = { 0, 1, 2 }; 471 | template<> const std::vector val,1> = { 1, 2, 3 }; 472 | template<> const std::vector val,2> = { 2, 3, 4 }; 473 | template<> const std::vector val,3> = { 3, 4, 5 }; 474 | template<> const std::vector val,4> = { 4, 5, 6 }; 475 | 476 | template< typename Container > 477 | int random_operations_test(int i) 478 | { 479 | using ValType = typename Container::value_type; 480 | 481 | Container veq; 482 | 483 | srand(time(NULL)); 484 | 485 | auto tests = std::vector>{ 486 | [&] 487 | { 488 | auto new_size = rand() % 20'000; 489 | veq.resize(new_size); 490 | i += *reinterpret_cast(&veq); 491 | }, 492 | [&] 493 | { 494 | auto new_size = rand() % 10'000; 495 | veq.resize(new_size); 496 | i += *reinterpret_cast(&veq); 497 | }, 498 | [&] 499 | { 500 | auto new_size = rand() % 5'000; 501 | veq.resize(new_size); 502 | i += *reinterpret_cast(&veq); 503 | }, 504 | [&] 505 | { 506 | if ( veq.size() ) 507 | { 508 | auto index = rand() % veq.size(); 509 | ValType x = veq.at(index); 510 | i += *reinterpret_cast(&x); 511 | } 512 | }, 513 | [&] 514 | { 515 | if ( veq.size() ) 516 | { 517 | auto index = rand() % veq.size(); 518 | ValType x = veq[index]; 519 | i += *reinterpret_cast(&x); 520 | } 521 | }, 522 | [&] 523 | { 524 | if ( veq.size() ) 525 | { 526 | ValType x = veq.front(); 527 | i += *reinterpret_cast(&x); 528 | } 529 | }, 530 | [&] 531 | { 532 | if ( veq.size() ) 533 | { 534 | ValType x = veq.back(); 535 | i += *reinterpret_cast(&x); 536 | } 537 | }, 538 | [&] 539 | { 540 | veq.push_back( {} ); 541 | }, 542 | [&] 543 | { 544 | ValType item = val; 545 | veq.push_back( ValType{item} ); 546 | i += *reinterpret_cast(&item); 547 | }, 548 | [&] 549 | { 550 | ValType item = val; 551 | veq.emplace_back( ValType{item} ); 552 | }, 553 | [&] 554 | { 555 | if ( veq.size() ) 556 | { 557 | ValType item = val; 558 | auto index = rand() % veq.size(); 559 | veq.insert( veq.begin() + index, item ); 560 | } 561 | }, 562 | [&] 563 | { 564 | if ( veq.size() ) 565 | { 566 | ValType item = val; 567 | auto index = rand() % veq.size(); 568 | veq.insert( veq.begin() + index, ValType{item} ); 569 | } 570 | }, 571 | [&] 572 | { 573 | if ( veq.size() ) 574 | { 575 | ValType x = val; 576 | auto index = rand() % veq.size(); 577 | veq.emplace( veq.begin() + index ); 578 | i += *reinterpret_cast(&x); 579 | } 580 | }, 581 | [&] 582 | { 583 | if ( veq.size() ) 584 | { 585 | ValType x = *veq.begin(); 586 | i += *reinterpret_cast(&x); 587 | } 588 | }, 589 | [&] 590 | { 591 | if ( veq.size() ) 592 | { 593 | ValType x = *veq.rbegin(); 594 | i += *reinterpret_cast(&x); 595 | } 596 | }, 597 | [&] 598 | { 599 | veq.clear(); 600 | }, 601 | [&] 602 | { 603 | if ( veq.size() ) 604 | { 605 | if constexpr( std::is_same_v> ) 606 | { 607 | ValType x = veq.pop_front_element(); 608 | i += *reinterpret_cast(&x); 609 | } 610 | else if constexpr( std::is_same_v> ) 611 | { 612 | ValType x = veq.front(); 613 | veq.erase( veq.begin() ); 614 | i += *reinterpret_cast(&x); 615 | } 616 | else 617 | { 618 | ValType x = veq.front(); 619 | veq.pop_front(); 620 | i += *reinterpret_cast(&x); 621 | } 622 | } 623 | }, 624 | [&] 625 | { 626 | if ( veq.size() ) 627 | { 628 | if constexpr( std::is_same_v> ) 629 | { 630 | ValType x = veq.pop_back_element(); 631 | i += *reinterpret_cast(&x); 632 | } 633 | else 634 | { 635 | ValType x = veq.back(); 636 | veq.pop_back(); 637 | i += *reinterpret_cast(&x); 638 | } 639 | } 640 | } 641 | }; 642 | 643 | for ( auto test_counter = 0; test_counter != 30'000; ++test_counter ) 644 | { 645 | tests[ rand() % tests.size() ](); 646 | } 647 | return i; 648 | } 649 | 650 | template< template typename Container > 651 | int run_resizing_test(int i) { 652 | i += resizing_test >(i); 653 | i += resizing_test >(i); 654 | i += resizing_test >(i); 655 | i += resizing_test >(i); 656 | i += resizing_test >(i); 657 | i += resizing_test >(i); 658 | i += resizing_test >(i); 659 | return i; 660 | } 661 | 662 | template< template typename Container > 663 | int run_reassignment_test(int i) { 664 | i += reassignment_test >(i); 665 | i += reassignment_test >(i); 666 | i += reassignment_test >(i); 667 | i += reassignment_test >(i); 668 | i += reassignment_test >(i); 669 | i += reassignment_test >(i); 670 | i += reassignment_test >(i); 671 | return i; 672 | } 673 | 674 | template< template typename Container > 675 | int run_back_growth_test(int i) { 676 | i += back_growth_test >(i); 677 | i += back_growth_test >(i); 678 | i += back_growth_test >(i); 679 | i += back_growth_test >(i); 680 | i += back_growth_test >(i); 681 | return i; 682 | } 683 | 684 | template< template typename Container > 685 | int run_front_growth_test(int i) { 686 | i += front_growth_test >(i); 687 | i += front_growth_test >(i); 688 | i += front_growth_test >(i); 689 | i += front_growth_test >(i); 690 | i += front_growth_test >(i); 691 | return i; 692 | } 693 | 694 | template< template typename Container > 695 | int run_arbitrary_insertion_test(int i) { 696 | i += arbitrary_insertion_test >(i); 697 | i += arbitrary_insertion_test >(i); 698 | i += arbitrary_insertion_test >(i); 699 | i += arbitrary_insertion_test >(i); 700 | i += arbitrary_insertion_test >(i); 701 | return i; 702 | } 703 | 704 | template< template typename Container > 705 | int run_iteration_test(int i) { 706 | i += iteration_test >(i); 707 | i += iteration_test >(i); 708 | i += iteration_test >(i); 709 | i += iteration_test> >(i); 710 | return i; 711 | } 712 | 713 | template< template typename Container > 714 | int run_random_operations_test(int i) { 715 | i += random_operations_test >(i); 716 | i += random_operations_test >(i); 717 | i += random_operations_test >(i); 718 | i += random_operations_test> >(i); 719 | return i; 720 | } 721 | 722 | 723 | 724 | template< template typename Container > 725 | int test(std::array & results, char i ) 726 | { 727 | auto t1 = std::chrono::steady_clock::now(); 728 | 729 | i += run_resizing_test((int) i); 730 | auto t2 = std::chrono::steady_clock::now(); 731 | results[0] += (t2 - t1); 732 | 733 | i += run_back_growth_test((int) i); 734 | auto t3 = std::chrono::steady_clock::now(); 735 | results[1] += (t3 - t2); 736 | 737 | i += run_front_growth_test((int) i); 738 | auto t4 = std::chrono::steady_clock::now(); 739 | results[2] += (t4 - t3); 740 | 741 | i += run_arbitrary_insertion_test((int) i); 742 | auto t5 = std::chrono::steady_clock::now(); 743 | results[3] += (t5 - t4); 744 | 745 | i += run_iteration_test((int) i); 746 | auto t6 = std::chrono::steady_clock::now(); 747 | results[4] += (t6 - t5); 748 | 749 | i += run_random_operations_test((int) i); 750 | auto t7 = std::chrono::steady_clock::now(); 751 | results[5] += (t7 - t6); 752 | 753 | i += run_reassignment_test((int) i); 754 | auto t8 = std::chrono::steady_clock::now(); 755 | results[6] += (t8 - t7); 756 | 757 | return i; 758 | } 759 | 760 | int main(int argc, char** argv) 761 | { 762 | static std::array deque_results; 763 | static std::array vector_results; 764 | static std::array veque_results; 765 | 766 | std::cout << "\ntesting std::deque (1 of 3)\n"; 767 | argc += test(deque_results, argv[0][0]); 768 | 769 | std::cout << "\ntesting std::vector (1 of 3)\n"; 770 | argc += test(vector_results, argv[0][0]); 771 | 772 | std::cout << "\ntesting veque::veque (1 of 3)\n"; 773 | argc += test(veque_results, argv[0][0]); 774 | 775 | std::cout << "\ntesting std::deque (2 of 3)\n"; 776 | argc += test(deque_results, argv[0][0]); 777 | 778 | std::cout << "\ntesting std::vector (2 of 3)\n"; 779 | argc += test(vector_results, argv[0][0]); 780 | 781 | std::cout << "\ntesting veque::veque (2 of 3 )\n"; 782 | argc += test(veque_results, argv[0][0]); 783 | 784 | std::cout << "\ntesting std::deque (3 of 3)\n"; 785 | argc += test(deque_results, argv[0][0]); 786 | 787 | std::cout << "\ntesting std::vector (3 of 3)\n"; 788 | argc += test(vector_results, argv[0][0]); 789 | 790 | std::cout << "\ntesting veque::veque (3 of 3)\n"; 791 | argc += test(veque_results, argv[0][0]); 792 | 793 | 794 | std::cout.imbue(std::locale("")); 795 | std::cout << "\nTest std::deque std::vector veque\n"; 796 | std::cout << "-----------------------------------------------------------------------\n"; 797 | std::cout << std::setw(10) << std::right; 798 | 799 | std::cout << "Resizing: "; 800 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(deque_results[0]).count() << "us "; 801 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(vector_results[0]).count() << "us "; 802 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(veque_results[0]).count() << "us\n"; 803 | 804 | std::cout << "Back growth: "; 805 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(deque_results[1]).count() << "us "; 806 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(vector_results[1]).count() << "us "; 807 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(veque_results[1]).count() << "us\n"; 808 | 809 | std::cout << "Front growth: "; 810 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(deque_results[2]).count() << "us "; 811 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(vector_results[2]).count() << "us "; 812 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(veque_results[2]).count() << "us\n"; 813 | 814 | std::cout << "Arbitrary insertion: "; 815 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(deque_results[3]).count() << "us "; 816 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(vector_results[3]).count() << "us "; 817 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(veque_results[3]).count() << "us\n"; 818 | 819 | std::cout << "Iteration: "; 820 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(deque_results[4]).count() << "us "; 821 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(vector_results[4]).count() << "us "; 822 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(veque_results[4]).count() << "us\n"; 823 | 824 | std::cout << "Random operations: "; 825 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(deque_results[5]).count() << "us "; 826 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(vector_results[5]).count() << "us "; 827 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(veque_results[5]).count() << "us\n"; 828 | 829 | std::cout << "Reassignment: "; 830 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(deque_results[6]).count() << "us "; 831 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(vector_results[6]).count() << "us "; 832 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(veque_results[6]).count() << "us\n"; 833 | 834 | std::cout << "Total: "; 835 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(std::accumulate(deque_results.begin(), deque_results.end(), std::chrono::steady_clock::duration{})).count() << "us "; 836 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(std::accumulate(vector_results.begin(), vector_results.end(), std::chrono::steady_clock::duration{})).count() << "us "; 837 | std::cout << std::setw(10) << std::right << std::chrono::duration_cast(std::accumulate(veque_results.begin(), veque_results.end(), std::chrono::steady_clock::duration{})).count() << "us\n"; 838 | 839 | return argc; 840 | } 841 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * veque::veque test suite. 4 | * 5 | * Additionally, valgrind claims there is no bad behavior throughout this usage. 6 | * 7 | * Copyright (C) 2019 Drew Dormann 8 | * 9 | */ 10 | 11 | #define CATCH_CONFIG_MAIN 12 | 13 | #include "catch.hpp" 14 | 15 | -------------------------------------------------------------------------------- /test/test_deque_parity.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * veque::veque test suite. 4 | * 5 | * Additionally, valgrind claims there is no bad behavior throughout this usage. 6 | * 7 | * Copyright (C) 2019 Drew Dormann 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "catch.hpp" 17 | #include "test_types.h" 18 | 19 | TEMPLATE_PRODUCT_TEST_CASE( "std::deque interface parity", "[veque::veque][template]", (StdVeque, GrumpyVeque, PropogatingGrumpyVeque, AllocCountingVeque), ( 20 | (int,veque::fast_resize_traits), (int,veque::std_vector_traits), (int,veque::no_reserve_traits), (int,front_vector_traits), 21 | (std::string,veque::fast_resize_traits), (std::string,veque::std_vector_traits), (std::string,veque::no_reserve_traits), (std::string,front_vector_traits), 22 | (double,veque::fast_resize_traits), (double,veque::std_vector_traits), (double,veque::no_reserve_traits), (double,front_vector_traits), 23 | (std::vector,veque::fast_resize_traits), (std::vector,veque::std_vector_traits), (std::vector,veque::no_reserve_traits), (std::vector,front_vector_traits) 24 | ) ) 25 | { 26 | TestType veq; 27 | std::deque deq; 28 | 29 | srand(time(NULL)); 30 | 31 | auto tests = std::vector>{ 32 | [&] 33 | { 34 | INFO( "resize" ); 35 | auto new_size = rand() % 10'000; 36 | veq.resize(new_size); 37 | deq.resize(new_size); 38 | }, 39 | [&] 40 | { 41 | INFO( "at" ); 42 | if ( veq.size() ) 43 | { 44 | auto index = rand() % veq.size(); 45 | CHECK( veq.at(index) == deq.at(index) ); 46 | } 47 | }, 48 | [&] 49 | { 50 | INFO( "const at" ); 51 | if ( veq.size() ) 52 | { 53 | const auto veq2 = veq; 54 | const auto deq2 = deq; 55 | auto index = rand() % veq.size(); 56 | CHECK( veq2.at(index) == deq2.at(index) ); 57 | } 58 | }, 59 | [&] 60 | { 61 | INFO( "[]" ); 62 | if ( veq.size() ) 63 | { 64 | auto index = rand() % veq.size(); 65 | CHECK( veq[index] == deq[index] ); 66 | } 67 | }, 68 | [&] 69 | { 70 | INFO( "[]" ); 71 | if ( veq.size() ) 72 | { 73 | const auto & veq2 = veq; 74 | const auto & deq2 = deq; 75 | auto index = rand() % veq.size(); 76 | CHECK( veq2[index] == deq2[index] ); 77 | } 78 | }, 79 | [&] 80 | { 81 | INFO( "front" ); 82 | if ( veq.size() ) 83 | { 84 | CHECK( veq.front() == deq.front() ); 85 | } 86 | }, 87 | [&] 88 | { 89 | INFO( "const front" ); 90 | if ( veq.size() ) 91 | { 92 | const auto & veq2 = veq; 93 | const auto & deq2 = deq; 94 | CHECK( veq2.front() == deq2.front() ); 95 | } 96 | }, 97 | [&] 98 | { 99 | INFO( "back" ); 100 | if ( veq.size() ) 101 | { 102 | CHECK( veq.back() == deq.back() ); 103 | } 104 | }, 105 | [&] 106 | { 107 | INFO( "const back" ); 108 | if ( veq.size() ) 109 | { 110 | const auto & veq2 = veq; 111 | const auto & deq2 = deq; 112 | CHECK( veq2.back() == deq2.back() ); 113 | } 114 | }, 115 | [&] 116 | { 117 | INFO( "push_back0" ); 118 | auto item = val; 119 | veq.push_back( item ); 120 | deq.push_back( item ); 121 | }, 122 | [&] 123 | { 124 | INFO( "push_back1" ); 125 | auto item = val; 126 | veq.push_back( typename TestType::value_type{item} ); 127 | deq.push_back( typename TestType::value_type{item} ); 128 | }, 129 | [&] 130 | { 131 | INFO( "emplace_back" ); 132 | auto item = val; 133 | veq.emplace_back( typename TestType::value_type{item} ); 134 | deq.emplace_back( typename TestType::value_type{item} ); 135 | }, 136 | [&] 137 | { 138 | INFO( "push_front5" ); 139 | auto item = val; 140 | veq.push_front( item ); 141 | deq.push_front( item ); 142 | }, 143 | [&] 144 | { 145 | INFO( "push_front4" ); 146 | auto item = val; 147 | veq.push_front( typename TestType::value_type{item} ); 148 | deq.push_front( typename TestType::value_type{item} ); 149 | }, 150 | [&] 151 | { 152 | INFO( "emplace_front" ); 153 | auto item = val; 154 | veq.emplace_front( typename TestType::value_type{item} ); 155 | deq.emplace_front( typename TestType::value_type{item} ); 156 | }, 157 | [&] 158 | { 159 | INFO( "insert2" ); 160 | auto item = val; 161 | auto index = veq.size() ? rand() % veq.size() : 0; 162 | veq.insert( veq.begin() + index, item ); 163 | deq.insert( deq.begin() + index, item ); 164 | }, 165 | [&] 166 | { 167 | INFO( "insert3" ); 168 | auto item = val; 169 | auto index = veq.size() ? rand() % veq.size() : 0; 170 | veq.insert( veq.begin() + index, typename TestType::value_type{item} ); 171 | deq.insert( deq.begin() + index, typename TestType::value_type{item}); 172 | }, 173 | [&] 174 | { 175 | INFO( "emplace" ); 176 | auto index = veq.size() ? rand() % veq.size() : 0; 177 | veq.emplace( veq.begin() + index ); 178 | deq.emplace( deq.begin() + index ); 179 | }, 180 | [&] 181 | { 182 | INFO( "begin" ); 183 | if ( veq.size() ) 184 | { 185 | CHECK( *veq.begin() == *deq.begin() ); 186 | } 187 | }, 188 | [&] 189 | { 190 | INFO( "rbegin" ); 191 | if ( veq.size() ) 192 | { 193 | CHECK( *veq.rbegin() == *deq.rbegin() ); 194 | } 195 | }, 196 | [&] 197 | { 198 | INFO( "end" ); 199 | if ( veq.size() ) 200 | { 201 | CHECK( *(veq.end()-1) == *--deq.end() ); 202 | } 203 | }, 204 | [&] 205 | { 206 | INFO( "rend" ); 207 | if ( veq.size() ) 208 | { 209 | CHECK( *(veq.rend()-1) == *--deq.rend() ); 210 | } 211 | }, 212 | [&] 213 | { 214 | INFO( "const begin" ); 215 | if ( veq.size() ) 216 | { 217 | const auto veq2 = veq; 218 | const auto deq2 = deq; 219 | CHECK( *veq2.begin() == *deq2.begin() ); 220 | } 221 | }, 222 | [&] 223 | { 224 | INFO( "const rbegin" ); 225 | if ( veq.size() ) 226 | { 227 | const auto veq2 = veq; 228 | const auto deq2 = deq; 229 | CHECK( *veq2.rbegin() == *deq2.rbegin() ); 230 | } 231 | }, 232 | [&] 233 | { 234 | INFO( "const end" ); 235 | if ( veq.size() ) 236 | { 237 | const auto veq2 = veq; 238 | const auto deq2 = deq; 239 | CHECK( *(veq2.end()-1) == *--deq2.end() ); 240 | } 241 | }, 242 | [&] 243 | { 244 | INFO( "const rend" ); 245 | if ( veq.size() ) 246 | { 247 | const auto veq2 = veq; 248 | const auto deq2 = deq; 249 | CHECK( *(veq2.rend()-1) == *--deq2.rend() ); 250 | } 251 | }, 252 | [&] 253 | { 254 | INFO( "clear" ); 255 | veq.clear(); 256 | deq.clear(); 257 | }, 258 | [&] 259 | { 260 | INFO( "swap" ); 261 | if ( deq.size() > 2 ) 262 | { 263 | auto veq2 = TestType( veq.begin() + 1, veq.end() - 1 ); 264 | auto vec2 = std::deque( deq.begin() + 1, deq.end() - 1 ); 265 | if ( veq.get_allocator() == veq2.get_allocator() && deq.get_allocator() == deq.get_allocator() ) 266 | { 267 | // UB, otherwise. 268 | veq.swap( veq2 ); 269 | deq.swap( vec2 ); 270 | } 271 | } 272 | } 273 | }; 274 | 275 | for ( auto test_counter = 0; test_counter != 20'000; ++test_counter ) 276 | { 277 | tests[ rand() % tests.size() ](); 278 | REQUIRE( std::equal( veq.begin(), veq.end(), deq.begin(), deq.end() ) ); 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /test/test_end_growth.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * veque::veque test suite. 4 | * 5 | * Additionally, valgrind claims there is no bad behavior throughout this usage. 6 | * 7 | * Copyright (C) 2019 Drew Dormann 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "catch.hpp" 17 | #include "test_types.h" 18 | 19 | TEMPLATE_PRODUCT_TEST_CASE( "large end growth", "[veque::veque][template]", (StdVeque, GrumpyVeque, PropogatingGrumpyVeque, AllocCountingVeque), ( 20 | (int,veque::fast_resize_traits), (int,veque::std_vector_traits), (int,veque::no_reserve_traits), (int,front_vector_traits), 21 | (std::string,veque::fast_resize_traits), (std::string,veque::std_vector_traits), (std::string,veque::no_reserve_traits), (std::string,front_vector_traits), 22 | (double,veque::fast_resize_traits), (double,veque::std_vector_traits), (double,veque::no_reserve_traits), (double,front_vector_traits), 23 | (LargeTrivialObject,veque::fast_resize_traits), (LargeTrivialObject,veque::std_vector_traits), (LargeTrivialObject,veque::no_reserve_traits), (LargeTrivialObject,front_vector_traits), 24 | (NonTrivialObject,veque::fast_resize_traits), (NonTrivialObject,veque::std_vector_traits), (NonTrivialObject,veque::no_reserve_traits), (NonTrivialObject,front_vector_traits), 25 | (ThrowingMoveConstructObject,veque::fast_resize_traits), (ThrowingMoveConstructObject,veque::std_vector_traits), (ThrowingMoveConstructObject,veque::no_reserve_traits), (ThrowingMoveConstructObject,front_vector_traits), 26 | (ThrowingMoveAssignObject,veque::fast_resize_traits), (ThrowingMoveAssignObject,veque::std_vector_traits), (ThrowingMoveAssignObject,veque::no_reserve_traits), (ThrowingMoveAssignObject,front_vector_traits), 27 | (ThrowingMoveObject,veque::fast_resize_traits), (ThrowingMoveObject,veque::std_vector_traits), (ThrowingMoveObject,veque::no_reserve_traits), (ThrowingMoveObject,front_vector_traits) 28 | ) ) 29 | { 30 | typename TestType::size_type size = 5; 31 | TestType v( size ); 32 | 33 | REQUIRE( v.size() == size ); 34 | REQUIRE( v.capacity() >= size ); 35 | 36 | SECTION( "push_back" ) 37 | { 38 | typename TestType::value_type val{}; 39 | for ( int i = 0; i < 2'000; ++i ) 40 | { 41 | v.push_back( val ); 42 | ++size; 43 | REQUIRE( v.size() == size ); 44 | REQUIRE( v.capacity() >= size ); 45 | } 46 | while ( v.size() ) 47 | { 48 | v.pop_back(); 49 | --size; 50 | REQUIRE( v.size() == size ); 51 | REQUIRE( v.capacity() >= size ); 52 | } 53 | REQUIRE( 0 == size ); 54 | REQUIRE( v.empty() ); 55 | } 56 | 57 | SECTION( "emplace_back" ) 58 | { 59 | for ( int i = 0; i < 2'000; ++i ) 60 | { 61 | v.emplace_back(); 62 | ++size; 63 | REQUIRE( v.size() == size ); 64 | REQUIRE( v.capacity() >= size ); 65 | } 66 | while ( v.size() ) 67 | { 68 | v.pop_back(); 69 | --size; 70 | REQUIRE( v.size() == size ); 71 | REQUIRE( v.capacity() >= size ); 72 | } 73 | REQUIRE( 0 == size ); 74 | REQUIRE( v.empty() ); 75 | } 76 | SECTION( "push_front" ) 77 | { 78 | typename TestType::value_type val{}; 79 | for ( int i = 0; i < 2'000; ++i ) 80 | { 81 | v.push_front( val ); 82 | ++size; 83 | REQUIRE( v.size() == size ); 84 | REQUIRE( v.capacity() >= size ); 85 | } 86 | while ( v.size() ) 87 | { 88 | v.pop_back(); 89 | --size; 90 | REQUIRE( v.size() == size ); 91 | REQUIRE( v.capacity() >= size ); 92 | } 93 | REQUIRE( 0 == size ); 94 | REQUIRE( v.empty() ); 95 | } 96 | SECTION( "emplace_front" ) 97 | { 98 | for ( int i = 0; i < 2'000; ++i ) 99 | { 100 | v.emplace_front(); 101 | ++size; 102 | REQUIRE( v.size() == size ); 103 | REQUIRE( v.capacity() >= size ); 104 | } 105 | while ( v.size() ) 106 | { 107 | v.pop_back(); 108 | --size; 109 | REQUIRE( v.size() == size ); 110 | REQUIRE( v.capacity() >= size ); 111 | } 112 | REQUIRE( 0 == size ); 113 | REQUIRE( v.empty() ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /test/test_hashing.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * veque::veque test suite. 4 | * 5 | * Additionally, valgrind claims there is no bad behavior throughout this usage. 6 | * 7 | * Copyright (C) 2019 Drew Dormann 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "catch.hpp" 18 | #include "test_types.h" 19 | 20 | using MyTypes = std::tuple< 21 | StdVeque, StdVeque, StdVeque, StdVeque, 22 | StdVeque, StdVeque, StdVeque, StdVeque, 23 | StdVeque, StdVeque, StdVeque, StdVeque, 24 | GrumpyVeque, GrumpyVeque, GrumpyVeque, GrumpyVeque, 25 | GrumpyVeque, GrumpyVeque, GrumpyVeque, GrumpyVeque, 26 | GrumpyVeque, GrumpyVeque, GrumpyVeque, GrumpyVeque, 27 | PropogatingGrumpyVeque, PropogatingGrumpyVeque, PropogatingGrumpyVeque, PropogatingGrumpyVeque, 28 | PropogatingGrumpyVeque, PropogatingGrumpyVeque, PropogatingGrumpyVeque, PropogatingGrumpyVeque, 29 | PropogatingGrumpyVeque, PropogatingGrumpyVeque, PropogatingGrumpyVeque, PropogatingGrumpyVeque, 30 | AllocCountingVeque, AllocCountingVeque, AllocCountingVeque, AllocCountingVeque, 31 | AllocCountingVeque, AllocCountingVeque, AllocCountingVeque, AllocCountingVeque, 32 | AllocCountingVeque, AllocCountingVeque, AllocCountingVeque, AllocCountingVeque 33 | >; 34 | 35 | TEMPLATE_LIST_TEST_CASE( "hashing", "[veque::veque][template]", MyTypes ) 36 | { 37 | std::unordered_set uset; 38 | 39 | uset.emplace( TestType{val} ); 40 | uset.emplace( TestType{val} ); 41 | uset.emplace( TestType{val} ); 42 | 43 | CHECK( uset.size() == 3 ); 44 | CHECK( uset.count(TestType{val}) == 0 ); 45 | CHECK( uset.count(TestType{val}) == 1 ); 46 | CHECK( uset.count(TestType{val}) == 1 ); 47 | CHECK( uset.count(TestType{val}) == 1 ); 48 | CHECK( uset.count(TestType{val}) == 0 ); 49 | CHECK( uset.count(TestType{val}) == 0 ); 50 | 51 | uset.emplace( TestType{val} ); 52 | 53 | CHECK( uset.size() == 3 ); 54 | CHECK( uset.count(TestType{val}) == 0 ); 55 | CHECK( uset.count(TestType{val}) == 1 ); 56 | CHECK( uset.count(TestType{val}) == 1 ); 57 | CHECK( uset.count(TestType{val}) == 1 ); 58 | CHECK( uset.count(TestType{val}) == 0 ); 59 | CHECK( uset.count({val}) == 0 ); 60 | } 61 | -------------------------------------------------------------------------------- /test/test_insert_erase.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * veque::veque test suite. 4 | * 5 | * Additionally, valgrind claims there is no bad behavior throughout this usage. 6 | * 7 | * Copyright (C) 2019 Drew Dormann 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "catch.hpp" 17 | #include "test_types.h" 18 | 19 | TEMPLATE_PRODUCT_TEST_CASE( "insert/erase", "[veque::veque][template]", (StdVeque, GrumpyVeque, PropogatingGrumpyVeque, AllocCountingVeque), ( 20 | (int,veque::fast_resize_traits), (int,veque::std_vector_traits), (int,veque::no_reserve_traits), (int,front_vector_traits), 21 | (std::string,veque::fast_resize_traits), (std::string,veque::std_vector_traits), (std::string,veque::no_reserve_traits), (std::string,front_vector_traits), 22 | (double,veque::fast_resize_traits), (double,veque::std_vector_traits), (double,veque::no_reserve_traits), (double,front_vector_traits), 23 | (std::vector,veque::fast_resize_traits), (std::vector,veque::std_vector_traits), (std::vector,veque::no_reserve_traits), (std::vector,front_vector_traits) 24 | ) ) 25 | { 26 | TestType veq{ val, val, val }; 27 | veq.reserve(20); 28 | 29 | CHECK( !veq.empty() ); 30 | CHECK( veq.size() == 3 ); 31 | 32 | SECTION( "l-value insertion" ) 33 | { 34 | veq.insert( veq.begin(), val ); 35 | 36 | REQUIRE( veq.size() == 4 ); 37 | CHECK( veq[0] == val ); 38 | CHECK( veq[1] == val ); 39 | CHECK( veq[2] == val ); 40 | CHECK( veq[3] == val ); 41 | CHECK( veq == TestType{ val, val, val, val } ); 42 | 43 | veq.insert( veq.end(), val ); 44 | 45 | CHECK( veq.size() == 5 ); 46 | CHECK( veq == TestType{ val, val, val, val, val } ); 47 | } 48 | SECTION( "l-value resizing insertion" ) 49 | { 50 | veq.shrink_to_fit(); 51 | veq.insert( veq.begin(), val ); 52 | 53 | REQUIRE( veq.size() == 4 ); 54 | CHECK( veq[0] == val ); 55 | CHECK( veq[1] == val ); 56 | CHECK( veq[2] == val ); 57 | CHECK( veq[3] == val ); 58 | CHECK( veq == TestType{ val, val, val, val } ); 59 | 60 | veq.shrink_to_fit(); 61 | veq.insert( veq.end(), val ); 62 | 63 | REQUIRE( veq.size() == 5 ); 64 | CHECK( veq[0] == val ); 65 | CHECK( veq[1] == val ); 66 | CHECK( veq[2] == val ); 67 | CHECK( veq[3] == val ); 68 | CHECK( veq[4] == val ); 69 | CHECK( veq == TestType{ val, val, val, val, val } ); 70 | } 71 | SECTION( "r-value insertion" ) 72 | { 73 | veq.insert( veq.begin(), typename TestType::value_type(val) ); 74 | 75 | CHECK( veq.size() == 4 ); 76 | CHECK( veq == TestType{ val, val, val, val } ); 77 | 78 | veq.insert( veq.end(), typename TestType::value_type(val) ); 79 | 80 | CHECK( veq.size() == 5 ); 81 | CHECK( veq == TestType{ val, val, val, val, val } ); 82 | } 83 | SECTION( "r-value resizing insertion" ) 84 | { 85 | veq.shrink_to_fit(); 86 | veq.insert( veq.begin(), typename TestType::value_type(val) ); 87 | 88 | CHECK( veq.size() == 4 ); 89 | CHECK( veq == TestType{ val, val, val, val } ); 90 | 91 | veq.shrink_to_fit(); 92 | veq.insert( veq.end(), typename TestType::value_type(val) ); 93 | 94 | CHECK( veq.size() == 5 ); 95 | CHECK( veq == TestType{ val, val, val, val, val } ); 96 | } 97 | SECTION( "pop erasure" ) 98 | { 99 | // pop erasure 100 | CHECK( veq.pop_front_element() == val ); 101 | 102 | CHECK( veq.size() == 2 ); 103 | CHECK( veq == TestType{ val, val } ); 104 | 105 | CHECK( veq.pop_back_element() == val ); 106 | 107 | CHECK( veq.size() == 1 ); 108 | CHECK( veq == TestType{ val } ); 109 | } 110 | SECTION( "val,count insertion" ) 111 | { 112 | veq.insert( veq.end(), 2, val ); 113 | 114 | CHECK( veq.size() == 5 ); 115 | CHECK( veq == TestType{ val, val, val, val, val } ); 116 | 117 | } 118 | SECTION( "val,count resizing insertion" ) 119 | { 120 | veq.shrink_to_fit(); 121 | veq.insert( veq.end(), 2, val ); 122 | 123 | CHECK( veq.size() == 5 ); 124 | CHECK( veq == TestType{ val, val, val, val, val } ); 125 | 126 | } 127 | SECTION( "range insertion" ) 128 | { 129 | auto veq2 = TestType{ val, val, val }; 130 | veq.insert( veq.begin(), veq2.begin(), veq2.end() ); 131 | 132 | CHECK( veq.size() == 6 ); 133 | CHECK( veq == TestType{ val, val, val, val, val, val } ); 134 | } 135 | SECTION( "range resizing insertion" ) 136 | { 137 | veq.shrink_to_fit(); 138 | auto veq2 = TestType{ val, val, val }; 139 | veq.insert( veq.begin(), veq2.begin(), veq2.end() ); 140 | 141 | CHECK( veq.size() == 6 ); 142 | CHECK( veq == TestType{ val, val, val, val, val, val } ); 143 | } 144 | SECTION( "resize_back growth 1" ) 145 | { 146 | veq.resize_back( 4 ); 147 | 148 | CHECK( veq.size() == 4 ); 149 | CHECK( veq == TestType{ val, val, val, typename TestType::value_type{} } ); 150 | } 151 | SECTION( "resize_back growth 2" ) 152 | { 153 | veq.resize_back( 4, val ); 154 | 155 | CHECK( veq.size() == 4 ); 156 | CHECK( veq == TestType{ val, val, val, val } ); 157 | } 158 | SECTION( "resize_back erasure" ) 159 | { 160 | // resize erasure 161 | veq.resize_back( 1 ); 162 | 163 | CHECK( veq.size() == 1 ); 164 | CHECK( veq == TestType{ val } ); 165 | } 166 | SECTION( "resize_front growth 1" ) 167 | { 168 | veq.resize_front( 4 ); 169 | 170 | CHECK( veq.size() == 4 ); 171 | CHECK( veq == TestType{ typename TestType::value_type{}, val, val, val } ); 172 | } 173 | SECTION( "resize_front growth 2" ) 174 | { 175 | veq.resize_front( 4, val ); 176 | 177 | CHECK( veq.size() == 4 ); 178 | CHECK( veq == TestType{ val, val, val, val } ); 179 | } 180 | SECTION( "resize_front erasure" ) 181 | { 182 | // resize erasure 183 | veq.resize_front( 1 ); 184 | 185 | CHECK( veq.size() == 1 ); 186 | CHECK( veq == TestType{ val } ); 187 | } 188 | SECTION( "initializer list insertion" ) 189 | { 190 | // initializer list insertion 191 | veq.insert( veq.end(), {val, val, val} ); 192 | 193 | CHECK( veq.size() == 6 ); 194 | CHECK( veq == TestType{ val, val, val, val, val, val } ); 195 | } 196 | SECTION( "iterator erasure 1" ) 197 | { 198 | veq.erase( veq.begin() ); 199 | 200 | CHECK( veq.size() == 2 ); 201 | CHECK( veq[0] == val ); 202 | CHECK( veq[1] == val ); 203 | CHECK( veq == TestType{ val, val } ); 204 | } 205 | SECTION( "iterator erasure 2" ) 206 | { 207 | veq.erase( veq.begin() + 1 ); 208 | 209 | CHECK( veq.size() == 2 ); 210 | CHECK( veq[0] == val ); 211 | CHECK( veq[1] == val ); 212 | CHECK( veq == TestType{ val, val } ); 213 | } 214 | SECTION( "iterator erasure 3" ) 215 | { 216 | veq.erase( veq.end() - 1 ); 217 | 218 | CHECK( veq.size() == 2 ); 219 | CHECK( veq[0] == val ); 220 | CHECK( veq[1] == val ); 221 | CHECK( veq == TestType{ val, val } ); 222 | } 223 | SECTION( "range erasure begin" ) 224 | { 225 | veq.assign( { val, val, val, val, val, val} ); 226 | veq.erase( veq.begin(), veq.begin() + 3 ); 227 | CHECK( veq.size() == 3 ); 228 | CHECK( veq == TestType{ val, val, val } ); 229 | } 230 | SECTION( "range erasure end" ) 231 | { 232 | veq.assign( { val, val, val, val, val, val} ); 233 | veq.erase( veq.begin() + 3, veq.end() ); 234 | CHECK( veq.size() == 3 ); 235 | CHECK( veq == TestType{ val, val, val } ); 236 | } 237 | SECTION( "range erasure mid near front" ) 238 | { 239 | veq.assign( { val, val, val, val, val, val} ); 240 | veq.erase( veq.begin() + 1, veq.begin() + 4 ); 241 | CHECK( veq.size() == 3 ); 242 | CHECK( veq == TestType{ val, val, val } ); 243 | } 244 | SECTION( "range erasure mid near back" ) 245 | { 246 | veq.assign( { val, val, val, val, val, val} ); 247 | veq.erase( veq.begin() + 2, veq.begin() + 5 ); 248 | CHECK( veq.size() == 3 ); 249 | CHECK( veq == TestType{ val, val, val } ); 250 | } 251 | SECTION( "Range assign" ) 252 | { 253 | TestType veq3; 254 | veq3.assign( veq.begin(), veq.end() ); 255 | CHECK( veq3.size() == 3 ); 256 | CHECK( veq3 == TestType{ val, val, val } ); 257 | } 258 | SECTION( "count,val assign" ) 259 | { 260 | TestType veq4; 261 | veq4.assign( 3, val ); 262 | CHECK( veq4.size() == 3 ); 263 | CHECK( veq4 == TestType{ val, val, val } ); 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /test/test_insertion_growth.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * veque::veque test suite. 4 | * 5 | * Additionally, valgrind claims there is no bad behavior throughout this usage. 6 | * 7 | * Copyright (C) 2019 Drew Dormann 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "catch.hpp" 17 | #include "test_types.h" 18 | 19 | TEMPLATE_PRODUCT_TEST_CASE( "large insertion growth", "[veque::veque][template]", (StdVeque, GrumpyVeque, PropogatingGrumpyVeque, AllocCountingVeque), ( 20 | (int,veque::fast_resize_traits), (int,veque::std_vector_traits), (int,veque::no_reserve_traits), (int,front_vector_traits), 21 | (std::string,veque::fast_resize_traits), (std::string,veque::std_vector_traits), (std::string,veque::no_reserve_traits), (std::string,front_vector_traits), 22 | (double,veque::fast_resize_traits), (double,veque::std_vector_traits), (double,veque::no_reserve_traits), (double,front_vector_traits), 23 | (std::vector,veque::fast_resize_traits), (std::vector,veque::std_vector_traits), (std::vector,veque::no_reserve_traits), (std::vector,front_vector_traits) 24 | ) ) 25 | { 26 | typename TestType::size_type size = 5; 27 | TestType v( size ); 28 | 29 | REQUIRE( v.size() == size ); 30 | REQUIRE( v.capacity() >= size ); 31 | 32 | SECTION( "insert begin" ) 33 | { 34 | for ( int i = 0; i < 2'000; ++i ) 35 | { 36 | typename TestType::value_type val{}; 37 | v.insert( v.begin(), val ); 38 | ++size; 39 | REQUIRE( v.size() == size ); 40 | REQUIRE( v.capacity() >= size ); 41 | } 42 | if constexpr( is_using_counting_allocator ) 43 | { 44 | REQUIRE( v.get_allocator().counter == 2'005 ); 45 | } 46 | while ( v.size() ) 47 | { 48 | v.pop_back(); 49 | --size; 50 | REQUIRE( v.size() == size ); 51 | REQUIRE( v.capacity() >= size ); 52 | } 53 | REQUIRE( 0 == size ); 54 | REQUIRE( v.empty() ); 55 | if constexpr( is_using_counting_allocator ) 56 | { 57 | REQUIRE( v.get_allocator().counter == 0 ); 58 | } 59 | } 60 | SECTION( "insert end" ) 61 | { 62 | for ( int i = 0; i < 2'000; ++i ) 63 | { 64 | typename TestType::value_type val{}; 65 | v.insert( v.end(), val ); 66 | ++size; 67 | REQUIRE( v.size() == size ); 68 | REQUIRE( v.capacity() >= size ); 69 | } 70 | while ( v.size() ) 71 | { 72 | v.pop_back(); 73 | --size; 74 | REQUIRE( v.size() == size ); 75 | REQUIRE( v.capacity() >= size ); 76 | } 77 | REQUIRE( 0 == size ); 78 | REQUIRE( v.empty() ); 79 | } 80 | SECTION( "insert middle" ) 81 | { 82 | for ( int i = 0; i < 2'000; ++i ) 83 | { 84 | typename TestType::value_type val{}; 85 | v.insert( v.begin() + v.size() / 2, val ); 86 | ++size; 87 | REQUIRE( v.size() == size ); 88 | REQUIRE( v.capacity() >= size ); 89 | } 90 | while ( v.size() ) 91 | { 92 | v.pop_back(); 93 | --size; 94 | REQUIRE( v.size() == size ); 95 | REQUIRE( v.capacity() >= size ); 96 | } 97 | REQUIRE( 0 == size ); 98 | REQUIRE( v.empty() ); 99 | } 100 | SECTION( "insert near begin" ) 101 | { 102 | for ( int i = 0; i < 2'000; ++i ) 103 | { 104 | typename TestType::value_type val{}; 105 | v.insert( v.begin() + v.size() / 3, val ); 106 | ++size; 107 | REQUIRE( v.size() == size ); 108 | REQUIRE( v.capacity() >= size ); 109 | } 110 | while ( v.size() ) 111 | { 112 | v.pop_back(); 113 | --size; 114 | REQUIRE( v.size() == size ); 115 | REQUIRE( v.capacity() >= size ); 116 | } 117 | REQUIRE( 0 == size ); 118 | REQUIRE( v.empty() ); 119 | } 120 | SECTION( "insert near end" ) 121 | { 122 | for ( int i = 0; i < 2'000; ++i ) 123 | { 124 | typename TestType::value_type val{}; 125 | v.insert( v.begin() + 2 * v.size() / 3, val ); 126 | ++size; 127 | REQUIRE( v.size() == size ); 128 | REQUIRE( v.capacity() >= size ); 129 | } 130 | if constexpr( is_using_counting_allocator ) 131 | { 132 | REQUIRE( v.get_allocator().counter == 2'005 ); 133 | } 134 | while ( v.size() ) 135 | { 136 | v.pop_front(); 137 | --size; 138 | REQUIRE( v.size() == size ); 139 | REQUIRE( v.capacity() >= size ); 140 | } 141 | REQUIRE( 0 == size ); 142 | REQUIRE( v.empty() ); 143 | if constexpr( is_using_counting_allocator ) 144 | { 145 | REQUIRE( v.get_allocator().counter == 0 ); 146 | } 147 | } 148 | SECTION( "insert randomly" ) 149 | { 150 | // Valgrind doesn't like std::random_device. 151 | //std::random_device rd; 152 | //std::mt19937 gen(rd()); 153 | srand(time(NULL)); 154 | for ( int i = 0; i < 2'000; ++i ) 155 | { 156 | typename TestType::value_type val{}; 157 | //auto index = std::uniform_int_distribution<>(0, v.size())(gen); 158 | auto index = rand() % (v.size()+1); 159 | //v.insert( v.begin() + dis(gen), val ); 160 | v.insert( v.begin() + index, val ); 161 | ++size; 162 | REQUIRE( v.size() == size ); 163 | REQUIRE( v.capacity() >= size ); 164 | } 165 | if constexpr( is_using_counting_allocator ) 166 | { 167 | REQUIRE( v.get_allocator().counter == 2'005 ); 168 | } 169 | while ( v.size() ) 170 | { 171 | v.pop_front(); 172 | --size; 173 | REQUIRE( v.size() == size ); 174 | REQUIRE( v.capacity() >= size ); 175 | } 176 | REQUIRE( 0 == size ); 177 | REQUIRE( v.empty() ); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /test/test_ordering.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * veque::veque test suite. 4 | * 5 | * Additionally, valgrind claims there is no bad behavior throughout this usage. 6 | * 7 | * Copyright (C) 2019 Drew Dormann 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "catch.hpp" 17 | #include "test_types.h" 18 | 19 | TEMPLATE_PRODUCT_TEST_CASE( "veque::veque element ordering and access", "[veque::veque][template]", (StdVeque, GrumpyVeque, PropogatingGrumpyVeque, AllocCountingVeque), ( 20 | (int,veque::fast_resize_traits), (int,veque::std_vector_traits), (int,veque::no_reserve_traits), (int,front_vector_traits), 21 | (std::string,veque::fast_resize_traits), (std::string,veque::std_vector_traits), (std::string,veque::no_reserve_traits), (std::string,front_vector_traits), 22 | (double,veque::fast_resize_traits), (double,veque::std_vector_traits), (double,veque::no_reserve_traits), (double,front_vector_traits), 23 | (std::vector,veque::fast_resize_traits), (std::vector,veque::std_vector_traits), (std::vector,veque::no_reserve_traits), (std::vector,front_vector_traits) 24 | ) ) 25 | { 26 | TestType veq1; 27 | 28 | CHECK( veq1.empty() ); 29 | CHECK( veq1.size() == 0 ); 30 | 31 | veq1.push_back( val ); 32 | veq1.push_back( val ); 33 | veq1.emplace_back( val ); 34 | 35 | CHECK( !veq1.empty() ); 36 | CHECK( veq1.size() == 3 ); 37 | CHECK( veq1 == TestType{ val, val, val } ); 38 | CHECK( veq1.front() == val ); 39 | CHECK( veq1.back() == val ); 40 | 41 | veq1.push_front( val ); 42 | veq1.emplace_front( val ); 43 | 44 | CHECK( !veq1.empty() ); 45 | CHECK( veq1.size() == 5 ); 46 | CHECK( veq1 == TestType{ val, val, val, val, val } ); 47 | CHECK( veq1.front() == val ); 48 | CHECK( veq1.back() == val ); 49 | 50 | veq1.pop_back(); 51 | 52 | CHECK( !veq1.empty() ); 53 | CHECK( veq1.size() == 4 ); 54 | CHECK( veq1 == TestType{ val, val, val, val } ); 55 | CHECK( veq1.front() == val ); 56 | CHECK( veq1.back() == val ); 57 | CHECK( veq1[0] == val ); 58 | CHECK( veq1.at(0) == val ); 59 | CHECK( veq1[1] == val ); 60 | CHECK( veq1.at(1) == val ); 61 | CHECK( veq1[2] == val ); 62 | CHECK( veq1.at(2) == val ); 63 | CHECK( veq1[3] == val ); 64 | CHECK( veq1.at(3) == val ); 65 | CHECK_THROWS( veq1.at(4) ); 66 | 67 | auto it = veq1.begin(); 68 | CHECK( it != veq1.end() ); 69 | CHECK( *it == val ); 70 | ++it; 71 | CHECK( it != veq1.end() ); 72 | CHECK( *it == val ); 73 | ++it; 74 | CHECK( it != veq1.end() ); 75 | CHECK( *it == val ); 76 | ++it; 77 | CHECK( it != veq1.end() ); 78 | CHECK( *it == val ); 79 | ++it; 80 | CHECK( it == veq1.end() ); 81 | 82 | auto c_it = veq1.cbegin(); 83 | CHECK( c_it != veq1.cend() ); 84 | CHECK( *c_it == val ); 85 | ++c_it; 86 | CHECK( c_it != veq1.cend() ); 87 | CHECK( *c_it == val ); 88 | ++c_it; 89 | CHECK( c_it != veq1.cend() ); 90 | CHECK( *c_it == val ); 91 | ++c_it; 92 | CHECK( c_it != veq1.cend() ); 93 | CHECK( *c_it == val ); 94 | ++c_it; 95 | CHECK( c_it == veq1.cend() ); 96 | 97 | 98 | auto r_it = veq1.rbegin(); 99 | CHECK( r_it != veq1.rend() ); 100 | CHECK( *r_it == val ); 101 | ++r_it; 102 | CHECK( r_it != veq1.rend() ); 103 | CHECK( *r_it == val ); 104 | ++r_it; 105 | CHECK( r_it != veq1.rend() ); 106 | CHECK( *r_it == val ); 107 | ++r_it; 108 | CHECK( r_it != veq1.rend() ); 109 | CHECK( *r_it == val ); 110 | ++r_it; 111 | CHECK( r_it == veq1.rend() ); 112 | 113 | auto cr_it = veq1.crbegin(); 114 | CHECK( cr_it != veq1.crend() ); 115 | CHECK( *cr_it == val ); 116 | ++cr_it; 117 | CHECK( cr_it != veq1.crend() ); 118 | CHECK( *cr_it == val ); 119 | ++cr_it; 120 | CHECK( cr_it != veq1.crend() ); 121 | CHECK( *cr_it == val ); 122 | ++cr_it; 123 | CHECK( cr_it != veq1.crend() ); 124 | CHECK( *cr_it == val ); 125 | ++cr_it; 126 | CHECK( cr_it == veq1.crend() ); 127 | 128 | veq1.pop_front(); 129 | 130 | CHECK( !veq1.empty() ); 131 | CHECK( veq1.size() == 3 ); 132 | CHECK( veq1 == TestType{ val, val, val } ); 133 | CHECK( veq1.front() == val ); 134 | CHECK( veq1.back() == val ); 135 | 136 | auto veq2 = veq1; 137 | 138 | CHECK( veq1 == veq2 ); 139 | CHECK( !(veq1 != veq2) ); 140 | 141 | veq2.emplace_front( val ); 142 | 143 | CHECK( veq2 < veq1 ); 144 | CHECK( veq2 <= veq1 ); 145 | CHECK( veq2 != veq1 ); 146 | CHECK( veq1 > veq2 ); 147 | CHECK( veq1 >= veq2 ); 148 | CHECK( veq1 != veq2 ); 149 | CHECK( !(veq1 < veq2) ); 150 | CHECK( !(veq1 <= veq2) ); 151 | CHECK( !(veq2 > veq1) ); 152 | CHECK( !(veq2 >= veq1) ); 153 | 154 | veq2.swap( veq1 ); 155 | 156 | CHECK( veq1 < veq2 ); 157 | CHECK( veq1 <= veq2 ); 158 | CHECK( veq1 != veq2 ); 159 | CHECK( veq2 > veq1 ); 160 | CHECK( veq2 >= veq1 ); 161 | CHECK( veq2 != veq1 ); 162 | CHECK( !(veq2 < veq1) ); 163 | CHECK( !(veq2 <= veq1) ); 164 | CHECK( !(veq1 > veq2) ); 165 | CHECK( !(veq1 >= veq2) ); 166 | 167 | veq1 = veq2; 168 | 169 | CHECK( veq1 == veq2 ); 170 | CHECK( !(veq1 != veq2) ); 171 | 172 | veq2 = TestType{ val, val, val }; 173 | 174 | CHECK( veq1 == veq2 ); 175 | CHECK( !(veq1 != veq2) ); 176 | 177 | if constexpr( is_using_counting_allocator ) 178 | { 179 | REQUIRE( veq1.get_allocator().counter == 6 ); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /test/test_reassignment.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * veque::veque test suite. 4 | * 5 | * Additionally, valgrind claims there is no bad behavior throughout this usage. 6 | * 7 | * Copyright (C) 2019 Drew Dormann 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "catch.hpp" 17 | #include "test_types.h" 18 | 19 | TEMPLATE_PRODUCT_TEST_CASE( "reassignment", "[veque::veque][template]", (StdVeque, GrumpyVeque, PropogatingGrumpyVeque, AllocCountingVeque), ( 20 | (int,veque::fast_resize_traits), (int,veque::std_vector_traits), (int,veque::no_reserve_traits), (int,front_vector_traits), 21 | (std::string,veque::fast_resize_traits), (std::string,veque::std_vector_traits), (std::string,veque::no_reserve_traits), (std::string,front_vector_traits), 22 | (double,veque::fast_resize_traits), (double,veque::std_vector_traits), (double,veque::no_reserve_traits), (double,front_vector_traits), 23 | (std::vector,veque::fast_resize_traits), (std::vector,veque::std_vector_traits), (std::vector,veque::no_reserve_traits), (std::vector,front_vector_traits) 24 | ) ) 25 | { 26 | for ( int i = 0; i < 10'000; ++i ) 27 | { 28 | auto v1 = TestType( rand() % 100, val ); 29 | auto v2 = TestType( rand() % 100, val ); 30 | auto v3 = TestType( rand() % 100, val ); 31 | v1 = v2; 32 | REQUIRE( v1 == v2 ); 33 | v3 = std::move(v2); 34 | REQUIRE( v1 == v3 ); 35 | auto v4 = TestType( v1, typename TestType::allocator_type{} ); 36 | REQUIRE( v1 == v4 ); 37 | } 38 | 39 | // Valgrind doesn't like std::random_device. 40 | //std::random_device rd; 41 | //std::mt19937 gen(rd()); 42 | srand(time(NULL)); 43 | for ( int i = 0; i < 10'000; ++i ) 44 | { 45 | auto v1 = TestType( typename TestType::allocator_type{} ); 46 | auto v2 = TestType( rand() % 100, val ); 47 | auto v3 = TestType( rand() % 100, val ); 48 | v1.assign( v2.begin(), v2.end() ); 49 | REQUIRE( v1 == v2 ); 50 | v2.assign( v3.begin(), v3.end() ); 51 | REQUIRE( v2 == v3 ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/test_resize.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * veque::veque test suite. 4 | * 5 | * Additionally, valgrind claims there is no bad behavior throughout this usage. 6 | * 7 | * Copyright (C) 2019 Drew Dormann 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "catch.hpp" 17 | #include "test_types.h" 18 | 19 | TEMPLATE_PRODUCT_TEST_CASE( "veque::veques can be sized and resized", "[veque::veque][template]", (StdVeque, GrumpyVeque, PropogatingGrumpyVeque, AllocCountingVeque), ( 20 | (int,veque::fast_resize_traits), (int,veque::std_vector_traits), (int,veque::no_reserve_traits), (int,front_vector_traits), 21 | (std::string,veque::fast_resize_traits), (std::string,veque::std_vector_traits), (std::string,veque::no_reserve_traits), (std::string,front_vector_traits), 22 | (double,veque::fast_resize_traits), (double,veque::std_vector_traits), (double,veque::no_reserve_traits), (double,front_vector_traits), 23 | (LargeTrivialObject,veque::fast_resize_traits), (LargeTrivialObject,veque::std_vector_traits), (LargeTrivialObject,veque::no_reserve_traits), (LargeTrivialObject,front_vector_traits), 24 | (NonTrivialObject,veque::fast_resize_traits), (NonTrivialObject,veque::std_vector_traits), (NonTrivialObject,veque::no_reserve_traits), (NonTrivialObject,front_vector_traits), 25 | (ThrowingMoveConstructObject,veque::fast_resize_traits), (ThrowingMoveConstructObject,veque::std_vector_traits), (ThrowingMoveConstructObject,veque::no_reserve_traits), (ThrowingMoveConstructObject,front_vector_traits), 26 | (ThrowingMoveAssignObject,veque::fast_resize_traits), (ThrowingMoveAssignObject,veque::std_vector_traits), (ThrowingMoveAssignObject,veque::no_reserve_traits), (ThrowingMoveAssignObject,front_vector_traits), 27 | (ThrowingMoveObject,veque::fast_resize_traits), (ThrowingMoveObject,veque::std_vector_traits), (ThrowingMoveObject,veque::no_reserve_traits), (ThrowingMoveObject,front_vector_traits) 28 | ) ) 29 | { 30 | TestType v( 5 ); 31 | 32 | REQUIRE( v.size() == 5 ); 33 | REQUIRE( v.capacity() >= 5 ); 34 | 35 | SECTION( "resizing bigger changes size and capacity" ) 36 | { 37 | v.resize_back( 10 ); 38 | 39 | REQUIRE( v.size() == 10 ); 40 | REQUIRE( v.capacity() >= 10 ); 41 | REQUIRE( v.capacity_back() >= 10 ); 42 | 43 | v.resize_front( 15 ); 44 | 45 | REQUIRE( v.size() == 15 ); 46 | REQUIRE( v.capacity() >= 15 ); 47 | REQUIRE( v.capacity_front() >= 15 ); 48 | 49 | if constexpr( std::is_same_v< typename TestType::allocator_type, CountingAllocator > ) 50 | { 51 | REQUIRE( v.get_allocator().counter == 15 ); 52 | } 53 | } 54 | SECTION( "resizing smaller changes size but not capacity" ) 55 | { 56 | v.resize( 0 ); 57 | 58 | REQUIRE( v.size() == 0 ); 59 | REQUIRE( v.capacity() >= 5 ); 60 | 61 | v.resize( 5 ); 62 | 63 | SECTION( "We can use the 'swap trick' to reset the capacity" ) 64 | { 65 | TestType( v ).swap( v ); 66 | 67 | REQUIRE( v.capacity() == 5 ); 68 | if constexpr( is_using_counting_allocator ) 69 | { 70 | REQUIRE( v.get_allocator().counter == 5 ); 71 | } 72 | } 73 | SECTION( "Or we can use shrink_to_fit()" ) 74 | { 75 | REQUIRE( v.size() == 5 ); 76 | 77 | v.shrink_to_fit(); 78 | 79 | CHECK( v.size() == 5 ); 80 | CHECK( v.capacity() == 5 ); 81 | if constexpr( is_using_counting_allocator ) 82 | { 83 | REQUIRE( v.get_allocator().counter == 5 ); 84 | } 85 | } 86 | } 87 | SECTION( "reserving smaller does not change size or capacity" ) 88 | { 89 | v.reserve( 0 ); 90 | 91 | CHECK( v.size() == 5 ); 92 | CHECK( v.capacity() >= 5 ); 93 | } 94 | SECTION( "clearing" ) 95 | { 96 | v.clear(); 97 | 98 | CHECK( v.size() == 0 ); 99 | CHECK( v.empty() ); 100 | } 101 | SECTION( "reserve_front" ) 102 | { 103 | v.reserve_front( 20 ); 104 | CHECK( v.capacity_front() >= 20 ); 105 | } 106 | SECTION( "reserve_back" ) 107 | { 108 | v.reserve_back( 20 ); 109 | CHECK( v.capacity_back() >= 20 ); 110 | } 111 | SECTION( "reserve" ) 112 | { 113 | v.reserve( 20 ); 114 | CHECK( v.capacity_front() >= 20 ); 115 | CHECK( v.capacity_back() >= 20 ); 116 | } 117 | SECTION( "reserve less" ) 118 | { 119 | auto old_capacity_front = v.capacity_front(); 120 | auto old_capacity_back = v.capacity_back(); 121 | 122 | v.reserve( 0 ); 123 | 124 | CHECK( v.capacity_front() == old_capacity_front ); 125 | CHECK( v.capacity_back() == old_capacity_back ); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /test/test_strong_exception_guarantee.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * veque::veque test suite. 4 | * 5 | * Additionally, valgrind claims there is no bad behavior throughout this usage. 6 | * 7 | * Copyright (C) 2019 Drew Dormann 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "catch.hpp" 17 | #include "test_types.h" 18 | 19 | TEMPLATE_PRODUCT_TEST_CASE( "veque::veques can be modified at either end with strong exception guarantee", "[veque::veque][template]", (StdVeque, GrumpyVeque, PropogatingGrumpyVeque, AllocCountingVeque), ( 20 | (int,veque::fast_resize_traits), (int,veque::std_vector_traits), (int,veque::no_reserve_traits), (int,front_vector_traits), 21 | (std::string,veque::fast_resize_traits), (std::string,veque::std_vector_traits), (std::string,veque::no_reserve_traits), (std::string,front_vector_traits), 22 | (double,veque::fast_resize_traits), (double,veque::std_vector_traits), (double,veque::no_reserve_traits), (double,front_vector_traits), 23 | (LargeTrivialObject,veque::fast_resize_traits), (LargeTrivialObject,veque::std_vector_traits), (LargeTrivialObject,veque::no_reserve_traits), (LargeTrivialObject,front_vector_traits), 24 | (NonTrivialObject,veque::fast_resize_traits), (NonTrivialObject,veque::std_vector_traits), (NonTrivialObject,veque::no_reserve_traits), (NonTrivialObject,front_vector_traits), 25 | (ThrowingMoveConstructObject,veque::fast_resize_traits), (ThrowingMoveConstructObject,veque::std_vector_traits), (ThrowingMoveConstructObject,veque::no_reserve_traits), (ThrowingMoveConstructObject,front_vector_traits), 26 | (ThrowingMoveAssignObject,veque::fast_resize_traits), (ThrowingMoveAssignObject,veque::std_vector_traits), (ThrowingMoveAssignObject,veque::no_reserve_traits), (ThrowingMoveAssignObject,front_vector_traits), 27 | (ThrowingMoveObject,veque::fast_resize_traits), (ThrowingMoveObject,veque::std_vector_traits), (ThrowingMoveObject,veque::no_reserve_traits), (ThrowingMoveObject,front_vector_traits) 28 | ) ) 29 | { 30 | TestType v( 5 ); 31 | 32 | REQUIRE( v.size() == 5 ); 33 | REQUIRE( v.capacity() >= 5 ); 34 | 35 | SECTION( "push_rvalue_back" ) 36 | { 37 | auto size = v.size(); 38 | for ( auto i = 0; i != 100; ++ i ) 39 | { 40 | auto v_before = v; 41 | try 42 | { 43 | v.push_back( {} ); 44 | REQUIRE( ++size == v.size() ); 45 | } 46 | catch(...) 47 | { 48 | REQUIRE( size == v.size() ); 49 | REQUIRE( v == v_before ); 50 | } 51 | } 52 | } 53 | SECTION( "push_lvalue_back" ) 54 | { 55 | auto size = v.size(); 56 | for ( auto i = 0; i != 100; ++ i ) 57 | { 58 | auto v_before = v; 59 | try 60 | { 61 | typename TestType::value_type val{}; 62 | v.push_back( val ); 63 | REQUIRE( ++size == v.size() ); 64 | } 65 | catch(...) 66 | { 67 | REQUIRE( size == v.size() ); 68 | REQUIRE( v == v_before ); 69 | } 70 | } 71 | } 72 | SECTION( "emplace_back" ) 73 | { 74 | auto size = v.size(); 75 | for ( auto i = 0; i != 100; ++ i ) 76 | { 77 | auto v_before = v; 78 | try 79 | { 80 | v.emplace_back(); 81 | REQUIRE( ++size == v.size() ); 82 | } 83 | catch(...) 84 | { 85 | REQUIRE( size == v.size() ); 86 | REQUIRE( v == v_before ); 87 | } 88 | } 89 | } 90 | SECTION( "push_lvalue_front" ) 91 | { 92 | auto size = v.size(); 93 | for ( auto i = 0; i != 100; ++ i ) 94 | { 95 | auto v_before = v; 96 | try 97 | { 98 | typename TestType::value_type val{}; 99 | v.push_front( val ); 100 | REQUIRE( ++size == v.size() ); 101 | } 102 | catch(...) 103 | { 104 | REQUIRE( size == v.size() ); 105 | REQUIRE( v == v_before ); 106 | } 107 | } 108 | } 109 | SECTION( "push_rvalue_front" ) 110 | { 111 | auto size = v.size(); 112 | for ( auto i = 0; i != 100; ++ i ) 113 | { 114 | auto v_before = v; 115 | try 116 | { 117 | v.push_front( {} ); 118 | REQUIRE( ++size == v.size() ); 119 | } 120 | catch(...) 121 | { 122 | REQUIRE( size == v.size() ); 123 | REQUIRE( v == v_before ); 124 | } 125 | } 126 | } 127 | SECTION( "emplace_front" ) 128 | { 129 | auto size = v.size(); 130 | for ( auto i = 0; i != 100; ++ i ) 131 | { 132 | auto v_before = v; 133 | try 134 | { 135 | v.emplace_front(); 136 | REQUIRE( ++size == v.size() ); 137 | } 138 | catch(...) 139 | { 140 | REQUIRE( size == v.size() ); 141 | REQUIRE( v == v_before ); 142 | } 143 | } 144 | } 145 | } 146 | 147 | -------------------------------------------------------------------------------- /test/test_types.h: -------------------------------------------------------------------------------- 1 | #ifndef VEQUE_TEST_TYPES_H 2 | #define VEQUE_TEST_TYPES_H 3 | 4 | /* 5 | * 6 | * veque::veque test suite. 7 | * 8 | * Additionally, valgrind claims there is no bad behavior throughout this usage. 9 | * 10 | * Copyright (C) 2019 Drew Dormann 11 | * 12 | */ 13 | 14 | #include 15 | #include 16 | #include "veque.hpp" 17 | 18 | // An allocator that is stateful, unlikely to be equal to another, and aware of being mismatched 19 | template 20 | struct StatefulAllocator 21 | { 22 | using value_type = T; 23 | using propagate_on_container_copy_assignment = std::false_type; 24 | using propagate_on_container_move_assignment = std::false_type; 25 | using propagate_on_container_swap = std::false_type; 26 | using is_always_equal = std::false_type; 27 | 28 | static constexpr auto barrier_size = 64; 29 | std::uint32_t barrier = 0xDEADBEEF + rand(); 30 | 31 | StatefulAllocator() = default; 32 | constexpr StatefulAllocator(const StatefulAllocator &) = default; 33 | constexpr StatefulAllocator(StatefulAllocator &&) = default; 34 | template< class U > 35 | StatefulAllocator( const StatefulAllocator& other ) noexcept 36 | : barrier{ other.barrier } 37 | { 38 | } 39 | 40 | constexpr StatefulAllocator& operator=(const StatefulAllocator &) = default; 41 | constexpr StatefulAllocator& operator=(StatefulAllocator &&) = default; 42 | 43 | T* allocate( std::size_t n ) 44 | { 45 | std::byte * ptr = reinterpret_cast( std::malloc( n * sizeof(T) + 2 * barrier_size ) ); 46 | *reinterpret_cast(ptr) = barrier; 47 | *reinterpret_cast(ptr + n * sizeof(T) + barrier_size) = barrier; 48 | return reinterpret_cast(ptr + barrier_size); 49 | } 50 | 51 | void deallocate( T* p, std::size_t n ) 52 | { 53 | if ( p ) 54 | { 55 | std::byte * ptr = reinterpret_cast(p) - barrier_size; 56 | if ( *reinterpret_cast(ptr) != barrier ) 57 | { 58 | throw std::runtime_error{"StatefulAllocator mismatch"}; 59 | } 60 | if ( *reinterpret_cast(ptr + n * sizeof(T) + barrier_size) != barrier ) 61 | { 62 | throw std::runtime_error{"StatefulAllocator mismatch"}; 63 | } 64 | std::free( ptr ); 65 | } 66 | } 67 | }; 68 | 69 | // An allocator that expects to propagate to other containers 70 | template 71 | struct PropagatingStatefulAllocator : StatefulAllocator 72 | { 73 | using propagate_on_container_copy_assignment = std::true_type; 74 | using propagate_on_container_move_assignment = std::true_type; 75 | using propagate_on_container_swap = std::true_type; 76 | 77 | PropagatingStatefulAllocator() = default; 78 | constexpr PropagatingStatefulAllocator(const PropagatingStatefulAllocator &) = default; 79 | constexpr PropagatingStatefulAllocator(PropagatingStatefulAllocator &&) = default; 80 | template< class U > 81 | PropagatingStatefulAllocator( const PropagatingStatefulAllocator& other ) noexcept : StatefulAllocator{other} {} 82 | constexpr PropagatingStatefulAllocator& operator=(const PropagatingStatefulAllocator &o) = default; 83 | constexpr PropagatingStatefulAllocator& operator=(PropagatingStatefulAllocator &&o) = default; 84 | }; 85 | 86 | // An allocator with construct/destroy members that must always be called. 87 | template 88 | struct CountingAllocator 89 | { 90 | using value_type = T; 91 | using is_always_equal = std::true_type; 92 | static inline size_t counter = 0; 93 | 94 | template< class U, class... Args > 95 | void construct( U* p, Args&&... args ) 96 | { 97 | ++counter; 98 | ::new((void *)p) U(std::forward(args)...); 99 | } 100 | 101 | template< class U > 102 | void destroy( U* p ) 103 | { 104 | --counter; 105 | p->~U(); 106 | } 107 | 108 | T* allocate( std::size_t n ) 109 | { 110 | return reinterpret_cast( std::malloc( n * sizeof(T) ) ); 111 | } 112 | 113 | void deallocate( T* p, std::size_t ) 114 | { 115 | std::free(p); 116 | } 117 | 118 | CountingAllocator() = default; 119 | constexpr CountingAllocator(const CountingAllocator &) = default; 120 | constexpr CountingAllocator(CountingAllocator &&) = default; 121 | template< class U > 122 | CountingAllocator( const CountingAllocator& ) noexcept {} 123 | constexpr CountingAllocator& operator=(const CountingAllocator &o) = default; 124 | constexpr CountingAllocator& operator=(CountingAllocator &&o) = default; 125 | }; 126 | 127 | template< class T1, class T2 > 128 | constexpr bool operator==( const StatefulAllocator& lhs, const StatefulAllocator& rhs ) noexcept 129 | { 130 | return lhs.barrier == rhs.barrier; 131 | } 132 | 133 | template< class T1, class T2 > 134 | constexpr bool operator!=( const StatefulAllocator& lhs, const StatefulAllocator& rhs ) noexcept 135 | { 136 | return lhs.barrier != rhs.barrier; 137 | } 138 | 139 | template< class T1, class T2 > 140 | constexpr bool operator==( const CountingAllocator&, const CountingAllocator& ) noexcept 141 | { 142 | return true; 143 | } 144 | 145 | template< class T1, class T2 > 146 | constexpr bool operator!=( const CountingAllocator&, const CountingAllocator& ) noexcept 147 | { 148 | return false; 149 | } 150 | 151 | // A trivial object should benefit from supporting memcpy/memmove 152 | // and no destruction 153 | struct LargeTrivialObject 154 | { 155 | bool operator== (const LargeTrivialObject & other) const 156 | { 157 | return std::equal( std::begin(data), std::end(data), other.data ); 158 | } 159 | int data[1024]; 160 | }; 161 | 162 | // A non-trivial object requires construction/destruction and potentially 163 | // benefits from move operations 164 | struct NonTrivialObject 165 | { 166 | std::string data = std::string( 1024, 'W' ); 167 | bool operator== (const NonTrivialObject & other) const 168 | { 169 | return data == other.data; 170 | } 171 | }; 172 | 173 | // An object with move operations that aren't noexcept should use 174 | // copy operations to allow strong exception guarantee 175 | struct ThrowingMoveConstructObject 176 | { 177 | ThrowingMoveConstructObject() = default; 178 | ThrowingMoveConstructObject( const ThrowingMoveConstructObject& ) = default; 179 | ThrowingMoveConstructObject( ThrowingMoveConstructObject&& ) noexcept(false) 180 | { 181 | throw std::runtime_error("Failed move construction"); 182 | } 183 | 184 | ThrowingMoveConstructObject& operator=( const ThrowingMoveConstructObject& ) = default; 185 | ThrowingMoveConstructObject& operator=( ThrowingMoveConstructObject&& ) = default; 186 | bool operator== (const ThrowingMoveConstructObject & other) const 187 | { 188 | return data == other.data; 189 | } 190 | std::string data = std::string( 1024, 'X' ); 191 | }; 192 | 193 | struct ThrowingMoveAssignObject 194 | { 195 | ThrowingMoveAssignObject() = default; 196 | ThrowingMoveAssignObject( const ThrowingMoveAssignObject& ) = default; 197 | ThrowingMoveAssignObject( ThrowingMoveAssignObject&& ) = default; 198 | 199 | ThrowingMoveAssignObject& operator=( const ThrowingMoveAssignObject& ) = default; 200 | ThrowingMoveAssignObject& operator=( ThrowingMoveAssignObject&& ) noexcept(false) 201 | { 202 | throw std::runtime_error("Failed move assignment"); 203 | } 204 | bool operator== (const ThrowingMoveAssignObject & other) const 205 | { 206 | return data == other.data; 207 | } 208 | std::string data = std::string( 1024, 'Y' ); 209 | }; 210 | 211 | struct ThrowingMoveObject 212 | { 213 | ThrowingMoveObject() = default; 214 | ThrowingMoveObject( const ThrowingMoveObject& ) = default; 215 | ThrowingMoveObject( ThrowingMoveObject&& ) noexcept(false) 216 | { 217 | throw std::runtime_error("Failed move construction"); 218 | } 219 | 220 | ThrowingMoveObject& operator=( const ThrowingMoveObject& ) = default; 221 | ThrowingMoveObject& operator=( ThrowingMoveObject&& ) noexcept(false) 222 | { 223 | throw std::runtime_error("Failed move assignment"); 224 | } 225 | bool operator== (const ThrowingMoveObject & other) const 226 | { 227 | return data == other.data; 228 | } 229 | std::string data = std::string( 1024, 'Z' ); 230 | }; 231 | 232 | // Resizing behavior identical to std::vector, but front-allocating 233 | struct front_vector_traits 234 | { 235 | // Reserve storage only at back, like std::vector 236 | using allocation_before_front = std::ratio<1>; 237 | using allocation_after_back = std::ratio<0>; 238 | 239 | // Same iterator invalidation rules as std::vector 240 | static constexpr auto resize_from_closest_side = false; 241 | }; 242 | 243 | 244 | template 245 | using StdVeque = veque::veque; 246 | 247 | template 248 | using GrumpyVeque = veque::veque>; 249 | 250 | template 251 | using PropogatingGrumpyVeque = veque::veque>; 252 | 253 | template 254 | using AllocCountingVeque = veque::veque>; 255 | 256 | template 257 | constexpr bool is_using_counting_allocator = std::is_same_v< typename Container::allocator_type, CountingAllocator >; 258 | 259 | 260 | // Sample data, each in increasing comparison order 261 | template const T val; 262 | template<> const int val = 0; 263 | template<> const int val = 1; 264 | template<> const int val = 2; 265 | template<> const int val = 3; 266 | template<> const int val = 4; 267 | template<> const int val = 5; 268 | template<> const std::string val = std::string(100, 'A'); 269 | template<> const std::string val = std::string(200, 'B'); 270 | template<> const std::string val = std::string(300, 'C'); 271 | template<> const std::string val = std::string(400, 'D'); 272 | template<> const std::string val = std::string(500, 'E'); 273 | template<> const std::string val = std::string(600, 'F'); 274 | template<> const double val = 00.0; 275 | template<> const double val = 11.0; 276 | template<> const double val = 22.0; 277 | template<> const double val = 33.0; 278 | template<> const double val = 44.0; 279 | template<> const double val = 55.0; 280 | template<> const std::vector val,0> = { 0, 1, 2 }; 281 | template<> const std::vector val,1> = { 1, 2, 3 }; 282 | template<> const std::vector val,2> = { 2, 3, 4 }; 283 | template<> const std::vector val,3> = { 3, 4, 5 }; 284 | template<> const std::vector val,4> = { 4, 5, 6 }; 285 | template<> const std::vector val,5> = { 6, 7, 8 }; 286 | 287 | 288 | #endif // VEQUE_TEST_TYPES_H 289 | -------------------------------------------------------------------------------- /test/test_vector_parity.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * veque::veque test suite. 4 | * 5 | * Additionally, valgrind claims there is no bad behavior throughout this usage. 6 | * 7 | * Copyright (C) 2019 Drew Dormann 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "catch.hpp" 17 | #include "test_types.h" 18 | 19 | TEMPLATE_PRODUCT_TEST_CASE( "std::vector interface parity", "[veque::veque][template]", (StdVeque, GrumpyVeque, PropogatingGrumpyVeque), ( 20 | (int,veque::fast_resize_traits), (int,veque::std_vector_traits), (int,veque::no_reserve_traits), (int,front_vector_traits), 21 | (std::string,veque::fast_resize_traits), (std::string,veque::std_vector_traits), (std::string,veque::no_reserve_traits), (std::string,front_vector_traits), 22 | (double,veque::fast_resize_traits), (double,veque::std_vector_traits), (double,veque::no_reserve_traits), (double,front_vector_traits), 23 | (std::vector,veque::fast_resize_traits), (std::vector,veque::std_vector_traits), (std::vector,veque::no_reserve_traits), (std::vector,front_vector_traits) 24 | ) ) 25 | { 26 | using VectorType = std::vector; 27 | 28 | TestType veq; 29 | VectorType vec; 30 | 31 | srand(time(NULL)); 32 | 33 | REQUIRE( veq.max_size() >= 20'000 ); 34 | 35 | auto tests = std::vector>{ 36 | [&] 37 | { 38 | INFO( "data" ); 39 | CHECK( veq == TestType( veq.data(), veq.data() + veq.size() ) ); 40 | CHECK( vec == VectorType( vec.data(), vec.data() + vec.size() ) ); 41 | }, 42 | [&] 43 | { 44 | INFO( "const data" ); 45 | const auto & veq2 = veq; 46 | const auto & vec2 = vec; 47 | CHECK( veq2 == TestType( veq2.data(), veq2.data() + veq2.size() ) ); 48 | CHECK( vec2 == VectorType( vec2.data(), vec2.data() + vec2.size() ) ); 49 | }, 50 | [&] 51 | { 52 | INFO( "il assign" ); 53 | veq = { val, val, val }; 54 | vec = { val, val, val }; 55 | }, 56 | [&] 57 | { 58 | INFO( "rvalue assign" ); 59 | veq = TestType{ val, val, val }; 60 | vec = VectorType{ val, val, val }; 61 | }, 62 | [&] 63 | { 64 | INFO( "resize" ); 65 | auto new_size = rand() % 10'000; 66 | veq.resize(new_size); 67 | vec.resize(new_size); 68 | }, 69 | [&] 70 | { 71 | INFO( "resize 2" ); 72 | auto new_size = rand() % 10'000; 73 | veq.resize(new_size, val); 74 | vec.resize(new_size, val); 75 | }, 76 | [&] 77 | { 78 | INFO( "at" ); 79 | if ( veq.size() ) 80 | { 81 | auto index = rand() % veq.size(); 82 | CHECK( veq.at(index) == vec.at(index) ); 83 | } 84 | }, 85 | [&] 86 | { 87 | INFO( "[]" ); 88 | if ( veq.ssize() ) 89 | { 90 | auto index = rand() % veq.ssize(); 91 | CHECK( veq[index] == vec[index] ); 92 | } 93 | }, 94 | [&] 95 | { 96 | INFO( "front" ); 97 | if ( veq.size() ) 98 | { 99 | CHECK( veq.front() == vec.front() ); 100 | } 101 | }, 102 | [&] 103 | { 104 | INFO( "back" ); 105 | if ( veq.size() ) 106 | { 107 | CHECK( veq.back() == vec.back() ); 108 | } 109 | }, 110 | [&] 111 | { 112 | INFO( "push_back0" ); 113 | auto item = val; 114 | veq.push_back( item ); 115 | vec.push_back( item ); 116 | }, 117 | [&] 118 | { 119 | INFO( "push_back1" ); 120 | auto item = val; 121 | veq.push_back( typename TestType::value_type{item} ); 122 | vec.push_back( typename TestType::value_type{item} ); 123 | }, 124 | [&] 125 | { 126 | INFO( "emplace_back" ); 127 | auto item = val; 128 | veq.emplace_back( typename TestType::value_type{item} ); 129 | vec.emplace_back( typename TestType::value_type{item} ); 130 | }, 131 | [&] 132 | { 133 | INFO( "insert2" ); 134 | auto item = val; 135 | auto index = veq.size() ? rand() % veq.size() : 0; 136 | veq.insert( veq.begin() + index, item ); 137 | vec.insert( vec.begin() + index, item ); 138 | }, 139 | [&] 140 | { 141 | INFO( "insert3" ); 142 | auto item = val; 143 | auto index = veq.size() ? rand() % veq.size() : 0; 144 | veq.insert( veq.begin() + index, typename TestType::value_type{item} ); 145 | vec.insert( vec.begin() + index, typename TestType::value_type{item}); 146 | }, 147 | [&] 148 | { 149 | INFO( "emplace" ); 150 | auto index = veq.size() ? rand() % veq.size() : 0; 151 | veq.emplace( veq.begin() + index ); 152 | vec.emplace( vec.begin() + index ); 153 | }, 154 | [&] 155 | { 156 | INFO( "begin" ); 157 | if ( veq.size() ) 158 | { 159 | CHECK( *veq.begin() == *vec.begin() ); 160 | } 161 | }, 162 | [&] 163 | { 164 | INFO( "rbegin" ); 165 | if ( veq.size() ) 166 | { 167 | CHECK( *veq.rbegin() == *vec.rbegin() ); 168 | } 169 | }, 170 | [&] 171 | { 172 | INFO( "clear" ); 173 | veq.clear(); 174 | vec.clear(); 175 | }, 176 | [&] 177 | { 178 | INFO( "reserve" ); 179 | auto new_size = rand() % 10'000; 180 | veq.reserve(new_size); 181 | vec.reserve(new_size); 182 | }, 183 | [&] 184 | { 185 | INFO( "reserve" ); 186 | auto new_size = rand() % 10'000; 187 | veq.reserve(new_size); 188 | vec.reserve(new_size); 189 | }, 190 | [&] 191 | { 192 | INFO( "swap 1" ); 193 | if ( vec.size() > 2 ) 194 | { 195 | auto veq2 = TestType( veq.begin() + 1, veq.end() - 1 ); 196 | auto vec2 = VectorType( vec.begin() + 1, vec.end() - 1 ); 197 | if ( veq.get_allocator() == veq2.get_allocator() && vec.get_allocator() == vec2.get_allocator() ) 198 | { 199 | // UB, otherwise. 200 | veq.swap( veq2 ); 201 | vec.swap( vec2 ); 202 | } 203 | } 204 | }, 205 | [&] 206 | { 207 | INFO( "swap 2" ); 208 | if ( vec.size() > 2 ) 209 | { 210 | auto veq2 = TestType( veq.begin() + 1, veq.end() - 1 ); 211 | auto vec2 = VectorType( vec.begin() + 1, vec.end() - 1 ); 212 | if ( veq.get_allocator() == veq2.get_allocator() && vec.get_allocator() == vec2.get_allocator() ) 213 | { 214 | // UB, otherwise. 215 | using std::swap; 216 | swap( veq, veq2 ); 217 | swap( vec, vec2 ); 218 | } 219 | } 220 | }, 221 | [&] 222 | { 223 | INFO( "swap 3" ); 224 | if ( vec.size() > 2 ) 225 | { 226 | auto veq2 = TestType( veq.begin() + 1, veq.end() - 1 ); 227 | auto veq3 = TestType( std::move(veq2), veq.get_allocator() ); 228 | auto vec2 = VectorType( vec.begin() + 1, vec.end() - 1 ); 229 | auto vec3 = VectorType( std::move(vec2), vec.get_allocator() ); 230 | 231 | // UB, otherwise. 232 | if ( veq.get_allocator() == veq2.get_allocator() && vec.get_allocator() == vec2.get_allocator() ) 233 | { 234 | using std::swap; 235 | swap( veq, veq2 ); 236 | swap( vec, vec2 ); 237 | } 238 | } 239 | } 240 | }; 241 | 242 | for ( auto test_counter = 0; test_counter != 20'000; ++test_counter ) 243 | { 244 | tests[ rand() % tests.size() ](); 245 | REQUIRE( std::equal( veq.begin(), veq.end(), vec.begin(), vec.end() ) ); 246 | } 247 | } 248 | --------------------------------------------------------------------------------