// ultra_fast_vector4.h #pragma once #include #include #include #include // malloc, realloc, free #include // std::bad_alloc //? Note: The vector is very inconsistant, but is on average %19 faster // GCC/Clang helpers for inlining and cold paths #if defined(__GNUC__) #define UFV_ALWAYS_INLINE inline __attribute__((always_inline)) #define UFV_COLD __attribute__((cold)) #else #define UFV_ALWAYS_INLINE inline #define UFV_COLD #endif template> class Vector { static_assert(!std::is_const_v, "Vector cannot hold const T"); // Select POD fast path only for trivially copyable T with default allocator static constexpr bool POD_PATH = std::is_trivially_copyable_v && std::is_same_v>; using alloc_traits = std::allocator_traits; public: using value_type = T; using allocator_type = Alloc; using size_type = size_t; using reference = T&; using const_reference = const T&; using iterator = T*; using const_iterator = const T*; Vector() noexcept(std::is_nothrow_default_constructible_v) : _b(nullptr), _e(nullptr), _cap(nullptr), _alloc() {} explicit Vector(const Alloc& a) noexcept : _b(nullptr), _e(nullptr), _cap(nullptr), _alloc(a) {} ~Vector() noexcept { if constexpr (POD_PATH) { std::free(_b); } else { clear(); if (_b) alloc_traits::deallocate(_alloc, _b, capacity()); } } UFV_ALWAYS_INLINE size_type size() const noexcept { return _e - _b; } UFV_ALWAYS_INLINE size_type capacity() const noexcept { return _cap - _b; } UFV_ALWAYS_INLINE bool empty() const noexcept { return _b == _e; } UFV_ALWAYS_INLINE iterator begin() noexcept { return _b; } UFV_ALWAYS_INLINE const_iterator begin() const noexcept { return _b; } UFV_ALWAYS_INLINE iterator end() noexcept { return _e; } UFV_ALWAYS_INLINE const_iterator end() const noexcept { return _e; } UFV_ALWAYS_INLINE reference operator[](size_type i) noexcept { return _b[i]; } UFV_ALWAYS_INLINE const_reference operator[](size_type i) const noexcept { return _b[i]; } UFV_ALWAYS_INLINE reference at(size_type i) { if (i >= size()) throw std::out_of_range("Vector::at"); return _b[i]; } UFV_ALWAYS_INLINE const_reference at(size_type i) const { if (i >= size()) throw std::out_of_range("Vector::at"); return _b[i]; } UFV_ALWAYS_INLINE void clear() noexcept { if constexpr (!std::is_trivially_destructible_v) { while (_e != _b) alloc_traits::destroy(_alloc, --_e); } else { _e = _b; } } // Cold path: only taken on growth UFV_COLD void reserve_grow(size_type minNeeded) { size_type oldSize = size(); if constexpr (POD_PATH) { size_type newCap = capacity() ? capacity() * 2 : 1; while (newCap < minNeeded) newCap <<= 1; void* blk = _b ? std::realloc(_b, newCap * sizeof(T)) : std::malloc (newCap * sizeof(T)); if (!blk) throw std::bad_alloc(); _b = static_cast(blk); _e = _b + oldSize; _cap = _b + newCap; } else { size_type newCap = capacity() ? capacity() * 2 : 1; while (newCap < minNeeded) newCap <<= 1; T* newB = alloc_traits::allocate(_alloc, newCap); if constexpr (std::is_nothrow_move_constructible_v) { std::uninitialized_move(_b, _e, newB); } else { std::uninitialized_copy(_b, _e, newB); } clear(); if (_b) alloc_traits::deallocate(_alloc, _b, capacity()); _b = newB; _e = newB + oldSize; _cap = newB + newCap; } } UFV_ALWAYS_INLINE void reserve(size_type n) { if (n > capacity()) reserve_grow(n); } UFV_ALWAYS_INLINE void shrink_to_fit() { reserve(size()); } UFV_ALWAYS_INLINE void push_back(const T& v) { if (__builtin_expect(_e == _cap, 0)) reserve_grow(size() + 1); if constexpr (POD_PATH) { *_e = v; } else { alloc_traits::construct(_alloc, _e, v); } ++_e; } UFV_ALWAYS_INLINE void push_back(T&& v) { if (__builtin_expect(_e == _cap, 0)) reserve_grow(size() + 1); if constexpr (POD_PATH) { *_e = static_cast(v); } else { alloc_traits::construct(_alloc, _e, std::move(v)); } ++_e; } template UFV_ALWAYS_INLINE reference emplace_back(Args&&... args) { if (__builtin_expect(_e == _cap, 0)) reserve_grow(size() + 1); if constexpr (POD_PATH) { *_e = T(std::forward(args)...); } else { alloc_traits::construct(_alloc, _e, std::forward(args)...); } return *(_e++); } UFV_ALWAYS_INLINE void pop_back() noexcept { assert(_e > _b); --_e; if constexpr (!std::is_trivially_destructible_v) alloc_traits::destroy(_alloc, _e); } private: T* _b; T* _e; T* _cap; Alloc _alloc; };