File indexing completed on 2025-02-21 09:58:13
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027 #pragma once
0028
0029 #include <iterator>
0030 #include <memory>
0031
0032 namespace dfe {
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046 template<typename T, std::size_t N, typename Allocator = std::allocator<T>>
0047 class SmallVector {
0048 public:
0049 using value_type = T;
0050 using size_type = std::size_t;
0051 using iterator = T*;
0052 using const_iterator = const T*;
0053
0054 SmallVector() = default;
0055 ~SmallVector() { clear(); }
0056
0057 value_type& operator[](size_type idx);
0058 const value_type& operator[](size_type idx) const;
0059
0060 iterator begin();
0061 iterator end() { return begin() + m_size; }
0062 const_iterator begin() const;
0063 const_iterator end() const { return begin() + m_size; }
0064
0065
0066 bool empty() const { return (m_size == 0); }
0067
0068 size_type size() const { return m_size; }
0069
0070 size_type capacity() const { return (m_size <= N) ? N : m_onheap.capacity; }
0071
0072
0073
0074
0075
0076 void clear();
0077
0078 template<typename... Args>
0079 iterator emplace(const_iterator pos, Args&&... args);
0080
0081 template<typename... Args>
0082 T& emplace_back(Args&&... args);
0083
0084 private:
0085 struct AllocatedStorage {
0086 size_type capacity;
0087 T* data;
0088 };
0089
0090 AllocatedStorage allocate_storage(size_type capacity);
0091 void destruct_inplace();
0092 void destruct_deallocate_onheap();
0093
0094 size_type m_size = 0;
0095 union {
0096 AllocatedStorage m_onheap;
0097
0098 alignas(T) char m_inplace[N * sizeof(T)];
0099 };
0100 Allocator m_alloc;
0101 };
0102
0103
0104
0105 template<typename T, std::size_t N, typename Allocator>
0106 inline typename SmallVector<T, N, Allocator>::AllocatedStorage
0107 SmallVector<T, N, Allocator>::allocate_storage(size_type capacity) {
0108 AllocatedStorage s;
0109 s.capacity = capacity;
0110 s.data = std::allocator_traits<Allocator>::allocate(m_alloc, capacity);
0111 return s;
0112 }
0113
0114
0115 template<typename T, std::size_t N, typename Allocator>
0116 inline void
0117 SmallVector<T, N, Allocator>::destruct_inplace() {
0118 T* ptr = reinterpret_cast<T*>(m_inplace);
0119 for (T* end = ptr + m_size; ptr != end; ++ptr) {
0120 ptr->~T();
0121 }
0122 }
0123
0124
0125 template<typename T, std::size_t N, typename Allocator>
0126 inline void
0127 SmallVector<T, N, Allocator>::destruct_deallocate_onheap() {
0128 T* ptr = m_onheap.data;
0129 for (T* end = ptr + m_size; ptr != end; ++ptr) {
0130 std::allocator_traits<Allocator>::destroy(m_alloc, ptr);
0131 }
0132 std::allocator_traits<Allocator>::deallocate(
0133 m_alloc, m_onheap.data, m_onheap.capacity);
0134 m_onheap.capacity = 0;
0135 m_onheap.data = nullptr;
0136 }
0137
0138 template<typename T, std::size_t N, typename Allocator>
0139 inline T& SmallVector<T, N, Allocator>::operator[](size_type idx) {
0140 if (m_size <= N) {
0141 return *(reinterpret_cast<T*>(m_inplace) + idx);
0142 } else {
0143 return m_onheap.data[idx];
0144 }
0145 }
0146
0147 template<typename T, std::size_t N, typename Allocator>
0148 inline const T& SmallVector<T, N, Allocator>::operator[](size_type idx) const {
0149 if (m_size <= N) {
0150 return *(reinterpret_cast<const T*>(m_inplace) + idx);
0151 } else {
0152 return m_onheap.data[idx];
0153 }
0154 }
0155
0156 template<typename T, std::size_t N, typename Allocator>
0157 inline typename SmallVector<T, N, Allocator>::iterator
0158 SmallVector<T, N, Allocator>::begin() {
0159 return (m_size <= N) ? reinterpret_cast<T*>(m_inplace) : m_onheap.data;
0160 }
0161
0162 template<typename T, std::size_t N, typename Allocator>
0163 inline typename SmallVector<T, N, Allocator>::const_iterator
0164 SmallVector<T, N, Allocator>::begin() const {
0165 return (m_size <= N) ? reinterpret_cast<const T*>(m_inplace) : m_onheap.data;
0166 }
0167
0168 template<typename T, std::size_t N, typename Allocator>
0169 inline void
0170 SmallVector<T, N, Allocator>::clear() {
0171 if (m_size <= N) {
0172 destruct_inplace();
0173 } else {
0174 destruct_deallocate_onheap();
0175 }
0176 m_size = 0;
0177 }
0178
0179 template<typename T, std::size_t N, typename Allocator>
0180 template<typename... Args>
0181 typename SmallVector<T, N, Allocator>::iterator
0182 SmallVector<T, N, Allocator>::emplace(const_iterator pos, Args&&... args) {
0183 using AllocatorTraits = std::allocator_traits<Allocator>;
0184
0185
0186
0187
0188
0189
0190 if (size() < capacity()) {
0191 T* i = const_cast<T*>(pos);
0192 T* e = end();
0193
0194
0195
0196
0197 (void)new (e) T();
0198 std::move_backward(i, e, std::next(e));
0199
0200
0201 *i = T(std::forward<Args>(args)...);
0202
0203 m_size += 1;
0204 return i;
0205 }
0206
0207
0208 auto storage = allocate_storage(1.3f * (m_size + 1));
0209 T* source = begin();
0210 T* target = storage.data;
0211
0212
0213 for (T* e = const_cast<T*>(pos); source != e; ++source, ++target) {
0214 AllocatorTraits::construct(m_alloc, target, std::move(*source));
0215 }
0216
0217 T* insert = target++;
0218 AllocatorTraits::construct(m_alloc, insert, std::forward<Args>(args)...);
0219
0220 for (T* e = end(); source != e; ++source, ++target) {
0221 AllocatorTraits::construct(m_alloc, target, std::move(*source));
0222 }
0223
0224
0225 if (m_size == N) {
0226 destruct_inplace();
0227 } else {
0228 destruct_deallocate_onheap();
0229 }
0230 m_onheap = storage;
0231
0232 m_size += 1;
0233 return insert;
0234 }
0235
0236 template<typename T, std::size_t N, typename Allocator>
0237 template<typename... Args>
0238 inline typename SmallVector<T, N, Allocator>::value_type&
0239 SmallVector<T, N, Allocator>::emplace_back(Args&&... args) {
0240 return *emplace(end(), std::forward<Args>(args)...);
0241 }
0242
0243 }