Lagrange
SmallVector.h
1// Source: https://github.com/KonanM/small_vector
2// SPDX-License-Identifier: Unlicense
3//
4// This file has been modified by Adobe.
5//
6// All modifications are Copyright 2022 Adobe.
7
8#pragma once
9
10#include <initializer_list>
11#include <memory>
12#include <type_traits>
13#include <vector>
14
15namespace lagrange {
16
18
25template <typename T, std::size_t MaxSize = 8, typename NonReboundT = T>
26struct SmallBufferAllocator
27{
28 alignas(alignof(T)) char m_small_buffer[MaxSize * sizeof(T)];
29 std::allocator<T> m_alloc{};
30 bool m_small_buffer_used = false;
31
32 using value_type = T;
33 // we have to set these three values, as they are responsible for the correct handling of the
34 // move assignment operator
35 using propagate_on_container_move_assignment = std::false_type;
36 using propagate_on_container_swap = std::false_type;
37 using is_always_equal = std::false_type;
38
39 template <class U>
40 struct rebind
41 {
42 typedef SmallBufferAllocator<U, MaxSize, NonReboundT> other;
43 };
44
45 constexpr SmallBufferAllocator() noexcept = default;
46 template <class U>
47 constexpr SmallBufferAllocator(const SmallBufferAllocator<U, MaxSize, NonReboundT>&) noexcept
48 {}
49 // don't copy the small buffer for the copy/move constructors, as the copying is done through
50 // the vector
51 constexpr SmallBufferAllocator(const SmallBufferAllocator& other) noexcept
52 : m_small_buffer_used(other.m_small_buffer_used)
53 {}
54 constexpr SmallBufferAllocator& operator=(const SmallBufferAllocator& other) noexcept
55 {
56 m_small_buffer_used = other.m_small_buffer_used;
57 return *this;
58 }
59 constexpr SmallBufferAllocator(SmallBufferAllocator&&) noexcept {}
60 constexpr SmallBufferAllocator& operator=(const SmallBufferAllocator&&) noexcept
61 {
62 return *this;
63 }
64
65 constexpr T* allocate(const std::size_t n)
66 {
67 // when the allocator was rebound we don't want to use the small buffer
68 if constexpr (std::is_same<T, NonReboundT>::value) {
69 if (n <= MaxSize) {
70 m_small_buffer_used = true;
71 // as long as we use less memory than the small buffer, we return a pointer to it
72 return reinterpret_cast<T*>(&m_small_buffer);
73 }
74 }
75 m_small_buffer_used = false;
76 // otherwise use the default allocator
77 return m_alloc.allocate(n);
78 }
79 constexpr void deallocate(void* p, const std::size_t n)
80 {
81 // we don't deallocate anything if the memory was allocated in small buffer
82 if (&m_small_buffer != p) m_alloc.deallocate(static_cast<T*>(p), n);
83 m_small_buffer_used = false;
84 }
85 // according to the C++ standard when propagate_on_container_move_assignment is set to false,
86 // the comparison operators are used to check if two allocators are equal. When they are not, an
87 // element wise move is done instead of just taking over the memory. For our implementation this
88 // means the comparision has to return false, when the small buffer is active
89 friend constexpr bool operator==(
90 const SmallBufferAllocator& lhs,
91 const SmallBufferAllocator& rhs)
92 {
93 return !lhs.m_small_buffer_used && !rhs.m_small_buffer_used;
94 }
95 friend constexpr bool operator!=(
96 const SmallBufferAllocator& lhs,
97 const SmallBufferAllocator& rhs)
98 {
99 return !(lhs == rhs);
100 }
101};
102
104
110
122template <typename T, std::size_t N = 8>
123class SmallVector : public std::vector<T, SmallBufferAllocator<T, N>>
124{
125private:
126 using vectorT = std::vector<T, SmallBufferAllocator<T, N>>;
127
128public:
129 // default initialize with the small buffer size
130 constexpr SmallVector() noexcept { vectorT::reserve(N); }
131 SmallVector(const SmallVector&) = default;
132 SmallVector& operator=(const SmallVector&) = default;
133 SmallVector(SmallVector&& other) noexcept(std::is_nothrow_move_constructible<T>::value)
134 {
135 if (other.size() <= N) vectorT::reserve(N);
136 vectorT::operator=(std::move(other));
137 }
138 SmallVector& operator=(SmallVector&& other) noexcept(
139 std::is_nothrow_move_constructible<T>::value)
140 {
141 if (other.size() <= N) vectorT::reserve(N);
142 vectorT::operator=(std::move(other));
143 return *this;
144 }
145 // use the default constructor first to reserve then construct the values
146 explicit SmallVector(std::size_t count)
147 : SmallVector()
148 {
149 vectorT::resize(count);
150 }
151 SmallVector(std::size_t count, const T& value)
152 : SmallVector()
153 {
154 vectorT::assign(count, value);
155 }
156 template <class InputIt>
157 SmallVector(InputIt first, InputIt last)
158 : SmallVector()
159 {
160 vectorT::insert(vectorT::begin(), first, last);
161 }
162 SmallVector(std::initializer_list<T> init)
163 : SmallVector()
164 {
165 vectorT::insert(vectorT::begin(), init);
166 }
167 friend void swap(SmallVector& a, SmallVector& b) noexcept
168 {
169 using std::swap;
170 swap(static_cast<vectorT&>(a), static_cast<vectorT&>(b));
171 }
172};
173
175
176} // namespace lagrange
Hybrid vector that uses the stack upto a maximum size, and the heap beyond that.
Definition: SmallVector.h:124
constexpr void swap(function_ref< R(Args...)> &lhs, function_ref< R(Args...)> &rhs) noexcept
Swaps the referred callables of lhs and rhs.
Definition: function_ref.h:114
bool operator==(const shared_ptr< T > &sp1, const shared_ptr< U > &sp2)
Operator == overloading.
Definition: shared_ptr.h:344
bool operator!=(const shared_ptr< T > &sp1, const shared_ptr< U > &sp2)
Operator != overloading.
Definition: shared_ptr.h:363
Main namespace for Lagrange.
Definition: AABBIGL.h:30