| // basisu_containers.h | |
| #pragma once | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <stdint.h> | |
| #include <assert.h> | |
| #include <algorithm> | |
| #if defined(__linux__) && !defined(ANDROID) | |
| // Only for malloc_usable_size() in basisu_containers_impl.h | |
| #include <malloc.h> | |
| #define HAS_MALLOC_USABLE_SIZE 1 | |
| #endif | |
| // Set to 1 to always check vector operator[], front(), and back() even in release. | |
| #define BASISU_VECTOR_FORCE_CHECKING 0 | |
| // If 1, the vector container will not query the CRT to get the size of resized memory blocks. | |
| #define BASISU_VECTOR_DETERMINISTIC 1 | |
| #ifdef _MSC_VER | |
| #define BASISU_FORCE_INLINE __forceinline | |
| #else | |
| #define BASISU_FORCE_INLINE inline | |
| #endif | |
| #define BASISU_HASHMAP_TEST 0 | |
| namespace basisu | |
| { | |
| enum { cInvalidIndex = -1 }; | |
| template <typename S> inline S clamp(S value, S low, S high) { return (value < low) ? low : ((value > high) ? high : value); } | |
| template <typename S> inline S maximum(S a, S b) { return (a > b) ? a : b; } | |
| template <typename S> inline S maximum(S a, S b, S c) { return maximum(maximum(a, b), c); } | |
| template <typename S> inline S maximum(S a, S b, S c, S d) { return maximum(maximum(maximum(a, b), c), d); } | |
| template <typename S> inline S minimum(S a, S b) { return (a < b) ? a : b; } | |
| template <typename S> inline S minimum(S a, S b, S c) { return minimum(minimum(a, b), c); } | |
| template <typename S> inline S minimum(S a, S b, S c, S d) { return minimum(minimum(minimum(a, b), c), d); } | |
| #ifdef _MSC_VER | |
| __declspec(noreturn) | |
| #else | |
| [[noreturn]] | |
| #endif | |
| void container_abort(const char* pMsg, ...); | |
| namespace helpers | |
| { | |
| inline bool is_power_of_2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); } | |
| inline bool is_power_of_2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); } | |
| template<class T> const T& minimum(const T& a, const T& b) { return (b < a) ? b : a; } | |
| template<class T> const T& maximum(const T& a, const T& b) { return (a < b) ? b : a; } | |
| inline uint32_t floor_log2i(uint32_t v) | |
| { | |
| uint32_t l = 0; | |
| while (v > 1U) | |
| { | |
| v >>= 1; | |
| l++; | |
| } | |
| return l; | |
| } | |
| inline uint32_t floor_log2i(uint64_t v) | |
| { | |
| uint32_t l = 0; | |
| while (v > 1U) | |
| { | |
| v >>= 1; | |
| l++; | |
| } | |
| return l; | |
| } | |
| inline uint32_t next_pow2(uint32_t val) | |
| { | |
| val--; | |
| val |= val >> 16; | |
| val |= val >> 8; | |
| val |= val >> 4; | |
| val |= val >> 2; | |
| val |= val >> 1; | |
| return val + 1; | |
| } | |
| inline uint64_t next_pow2(uint64_t val) | |
| { | |
| val--; | |
| val |= val >> 32; | |
| val |= val >> 16; | |
| val |= val >> 8; | |
| val |= val >> 4; | |
| val |= val >> 2; | |
| val |= val >> 1; | |
| return val + 1; | |
| } | |
| } // namespace helpers | |
| template <typename T> | |
| inline T* construct(T* p) | |
| { | |
| return new (static_cast<void*>(p)) T; | |
| } | |
| template <typename T, typename U> | |
| inline T* construct(T* p, const U& init) | |
| { | |
| return new (static_cast<void*>(p)) T(init); | |
| } | |
| template <typename T> | |
| inline void construct_array(T* p, size_t n) | |
| { | |
| T* q = p + n; | |
| for (; p != q; ++p) | |
| new (static_cast<void*>(p)) T; | |
| } | |
| template <typename T, typename U> | |
| inline void construct_array(T* p, size_t n, const U& init) | |
| { | |
| T* q = p + n; | |
| for (; p != q; ++p) | |
| new (static_cast<void*>(p)) T(init); | |
| } | |
| template <typename T> | |
| inline void destruct(T* p) | |
| { | |
| p->~T(); | |
| } | |
| template <typename T> inline void destruct_array(T* p, size_t n) | |
| { | |
| T* q = p + n; | |
| for (; p != q; ++p) | |
| p->~T(); | |
| } | |
| template<typename T> | |
| struct scalar_type | |
| { | |
| enum { cFlag = false }; | |
| static inline void construct(T* p) { basisu::construct(p); } | |
| static inline void construct(T* p, const T& init) { basisu::construct(p, init); } | |
| static inline void construct_array(T* p, size_t n) { basisu::construct_array(p, n); } | |
| static inline void destruct(T* p) { basisu::destruct(p); } | |
| static inline void destruct_array(T* p, size_t n) { basisu::destruct_array(p, n); } | |
| }; | |
| template<typename T> struct scalar_type<T*> | |
| { | |
| enum { cFlag = true }; | |
| static inline void construct(T** p) { memset(p, 0, sizeof(T*)); } | |
| static inline void construct(T** p, T* init) { *p = init; } | |
| static inline void construct_array(T** p, size_t n) { memset(p, 0, sizeof(T*) * n); } | |
| static inline void destruct(T** p) { (void)p; } | |
| static inline void destruct_array(T** p, size_t n) { (void)p, (void)n; } | |
| }; | |
| #define BASISU_DEFINE_BUILT_IN_TYPE(X) \ | |
| template<> struct scalar_type<X> { \ | |
| enum { cFlag = true }; \ | |
| static inline void construct(X* p) { memset(p, 0, sizeof(X)); } \ | |
| static inline void construct(X* p, const X& init) { memcpy(p, &init, sizeof(X)); } \ | |
| static inline void construct_array(X* p, size_t n) { memset(p, 0, sizeof(X) * n); } \ | |
| static inline void destruct(X* p) { (void)p; } \ | |
| static inline void destruct_array(X* p, size_t n) { (void)p, (void)n; } }; | |
| BASISU_DEFINE_BUILT_IN_TYPE(bool) | |
| BASISU_DEFINE_BUILT_IN_TYPE(char) | |
| BASISU_DEFINE_BUILT_IN_TYPE(unsigned char) | |
| BASISU_DEFINE_BUILT_IN_TYPE(short) | |
| BASISU_DEFINE_BUILT_IN_TYPE(unsigned short) | |
| BASISU_DEFINE_BUILT_IN_TYPE(int) | |
| BASISU_DEFINE_BUILT_IN_TYPE(unsigned int) | |
| BASISU_DEFINE_BUILT_IN_TYPE(long) | |
| BASISU_DEFINE_BUILT_IN_TYPE(unsigned long) | |
| #ifdef __GNUC__ | |
| BASISU_DEFINE_BUILT_IN_TYPE(long long) | |
| BASISU_DEFINE_BUILT_IN_TYPE(unsigned long long) | |
| #else | |
| BASISU_DEFINE_BUILT_IN_TYPE(__int64) | |
| BASISU_DEFINE_BUILT_IN_TYPE(unsigned __int64) | |
| #endif | |
| BASISU_DEFINE_BUILT_IN_TYPE(float) | |
| BASISU_DEFINE_BUILT_IN_TYPE(double) | |
| BASISU_DEFINE_BUILT_IN_TYPE(long double) | |
| #undef BASISU_DEFINE_BUILT_IN_TYPE | |
| template<typename T> | |
| struct bitwise_movable { enum { cFlag = false }; }; | |
| #define BASISU_DEFINE_BITWISE_MOVABLE(Q) template<> struct bitwise_movable<Q> { enum { cFlag = true }; }; | |
| template<typename T> | |
| struct bitwise_copyable { enum { cFlag = false }; }; | |
| #define BASISU_DEFINE_BITWISE_COPYABLE(Q) template<> struct bitwise_copyable<Q> { enum { cFlag = true }; }; | |
| #define BASISU_IS_POD(T) __is_pod(T) | |
| #define BASISU_IS_SCALAR_TYPE(T) (scalar_type<T>::cFlag) | |
| #if !defined(BASISU_HAVE_STD_TRIVIALLY_COPYABLE) && defined(__GNUC__) && (__GNUC__ < 5) | |
| #define BASISU_IS_TRIVIALLY_COPYABLE(...) __is_trivially_copyable(__VA_ARGS__) | |
| #else | |
| #define BASISU_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value | |
| #endif | |
| // TODO: clean this up, it's still confusing (copying vs. movable). | |
| #define BASISU_IS_BITWISE_COPYABLE(T) (BASISU_IS_SCALAR_TYPE(T) || BASISU_IS_POD(T) || BASISU_IS_TRIVIALLY_COPYABLE(T) || std::is_trivial<T>::value || (bitwise_copyable<T>::cFlag)) | |
| #define BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(T) (BASISU_IS_BITWISE_COPYABLE(T) || (bitwise_movable<T>::cFlag)) | |
| #define BASISU_HAS_DESTRUCTOR(T) ((!scalar_type<T>::cFlag) && (!__is_pod(T)) && (!std::is_trivially_destructible<T>::value)) | |
| typedef char(&yes_t)[1]; | |
| typedef char(&no_t)[2]; | |
| template <class U> yes_t class_test(int U::*); | |
| template <class U> no_t class_test(...); | |
| template <class T> struct is_class | |
| { | |
| enum { value = (sizeof(class_test<T>(0)) == sizeof(yes_t)) }; | |
| }; | |
| template <typename T> struct is_pointer | |
| { | |
| enum { value = false }; | |
| }; | |
| template <typename T> struct is_pointer<T*> | |
| { | |
| enum { value = true }; | |
| }; | |
| struct empty_type { }; | |
| BASISU_DEFINE_BITWISE_COPYABLE(empty_type); | |
| BASISU_DEFINE_BITWISE_MOVABLE(empty_type); | |
| template<typename T> struct rel_ops | |
| { | |
| friend bool operator!=(const T& x, const T& y) { return (!(x == y)); } | |
| friend bool operator> (const T& x, const T& y) { return (y < x); } | |
| friend bool operator<=(const T& x, const T& y) { return (!(y < x)); } | |
| friend bool operator>=(const T& x, const T& y) { return (!(x < y)); } | |
| }; | |
| struct elemental_vector | |
| { | |
| void* m_p; | |
| size_t m_size; | |
| size_t m_capacity; | |
| typedef void (*object_mover)(void* pDst, void* pSrc, size_t num); | |
| bool increase_capacity(size_t min_new_capacity, bool grow_hint, size_t element_size, object_mover pRelocate, bool nofail); | |
| }; | |
| // Returns true if a+b would overflow a size_t. | |
| inline bool add_overflow_check(size_t a, size_t b) | |
| { | |
| size_t c = a + b; | |
| return c < a; | |
| } | |
| // Returns false on overflow, true if OK. | |
| template<typename T> | |
| inline bool can_fit_into_size_t(T val) | |
| { | |
| static_assert(std::is_integral<T>::value, "T must be an integral type"); | |
| return (val >= 0) && (static_cast<size_t>(val) == val); | |
| } | |
| // Returns true if a*b would overflow a size_t. | |
| inline bool mul_overflow_check(size_t a, size_t b) | |
| { | |
| // Avoid the division on 32-bit platforms | |
| if (sizeof(size_t) == sizeof(uint32_t)) | |
| return !can_fit_into_size_t(static_cast<uint64_t>(a) * b); | |
| else | |
| return b && (a > (SIZE_MAX / b)); | |
| } | |
| template<typename T> | |
| class writable_span; | |
| template<typename T> | |
| class readable_span | |
| { | |
| public: | |
| using value_type = T; | |
| using size_type = size_t; | |
| using const_pointer = const T*; | |
| using const_reference = const T&; | |
| using const_iterator = const T*; | |
| inline readable_span() : | |
| m_p(nullptr), | |
| m_size(0) | |
| { | |
| } | |
| inline readable_span(const writable_span<T>& other); | |
| inline readable_span& operator= (const writable_span<T>& rhs); | |
| inline readable_span(const_pointer p, size_t n) | |
| { | |
| set(p, n); | |
| } | |
| inline readable_span(const_pointer s, const_pointer e) | |
| { | |
| set(s, e); | |
| } | |
| inline readable_span(const readable_span& other) : | |
| m_p(other.m_p), | |
| m_size(other.m_size) | |
| { | |
| assert(!m_size || m_p); | |
| } | |
| inline readable_span(readable_span&& other) : | |
| m_p(other.m_p), | |
| m_size(other.m_size) | |
| { | |
| assert(!m_size || m_p); | |
| other.m_p = nullptr; | |
| other.m_size = 0; | |
| } | |
| template <size_t N> | |
| inline readable_span(const T(&arr)[N]) : | |
| m_p(arr), | |
| m_size(N) | |
| { | |
| } | |
| template <size_t N> | |
| inline readable_span& set(const T(&arr)[N]) | |
| { | |
| m_p = arr; | |
| m_size = N; | |
| return *this; | |
| } | |
| inline readable_span& set(const_pointer p, size_t n) | |
| { | |
| if (!p && n) | |
| { | |
| assert(0); | |
| m_p = nullptr; | |
| m_size = 0; | |
| } | |
| else | |
| { | |
| m_p = p; | |
| m_size = n; | |
| } | |
| return *this; | |
| } | |
| inline readable_span& set(const_pointer s, const_pointer e) | |
| { | |
| if ((e < s) || (!s && e)) | |
| { | |
| assert(0); | |
| m_p = nullptr; | |
| m_size = 0; | |
| } | |
| else | |
| { | |
| m_p = s; | |
| m_size = e - s; | |
| } | |
| return *this; | |
| } | |
| inline bool operator== (const readable_span& rhs) const | |
| { | |
| return (m_p == rhs.m_p) && (m_size == rhs.m_size); | |
| } | |
| inline bool operator!= (const readable_span& rhs) const | |
| { | |
| return (m_p != rhs.m_p) || (m_size != rhs.m_size); | |
| } | |
| // only true if the region is totally inside the span | |
| inline bool is_inside_ptr(const_pointer p, size_t n) const | |
| { | |
| if (!is_valid()) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if (!p) | |
| { | |
| assert(!n); | |
| return false; | |
| } | |
| return (p >= m_p) && ((p + n) <= end()); | |
| } | |
| inline bool is_inside(size_t ofs, size_t size) const | |
| { | |
| if (add_overflow_check(ofs, size)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if (!is_valid()) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if ((ofs + size) > m_size) | |
| return false; | |
| return true; | |
| } | |
| inline readable_span subspan(size_t ofs, size_t n) const | |
| { | |
| if (!is_valid()) | |
| { | |
| assert(0); | |
| return readable_span((const_pointer)nullptr, (size_t)0); | |
| } | |
| if (add_overflow_check(ofs, n)) | |
| { | |
| assert(0); | |
| return readable_span((const_pointer)nullptr, (size_t)0); | |
| } | |
| if ((ofs + n) > m_size) | |
| { | |
| assert(0); | |
| return readable_span((const_pointer)nullptr, (size_t)0); | |
| } | |
| return readable_span(m_p + ofs, n); | |
| } | |
| void clear() | |
| { | |
| m_p = nullptr; | |
| m_size = 0; | |
| } | |
| inline bool empty() const { return !m_size; } | |
| // true if the span is non-nullptr and is not empty | |
| inline bool is_valid() const { return m_p && m_size; } | |
| inline bool is_nullptr() const { return m_p == nullptr; } | |
| inline size_t size() const { return m_size; } | |
| inline size_t size_in_bytes() const { assert(can_fit_into_size_t((uint64_t)m_size * sizeof(T))); return m_size * sizeof(T); } | |
| inline const_pointer get_ptr() const { return m_p; } | |
| inline const_iterator begin() const { return m_p; } | |
| inline const_iterator end() const { assert(m_p || !m_size); return m_p + m_size; } | |
| inline const_iterator cbegin() const { return m_p; } | |
| inline const_iterator cend() const { assert(m_p || !m_size); return m_p + m_size; } | |
| inline const_reference front() const | |
| { | |
| if (!(m_p && m_size)) | |
| container_abort("readable_span invalid\n"); | |
| return m_p[0]; | |
| } | |
| inline const_reference back() const | |
| { | |
| if (!(m_p && m_size)) | |
| container_abort("readable_span invalid\n"); | |
| return m_p[m_size - 1]; | |
| } | |
| inline readable_span& operator= (const readable_span& rhs) | |
| { | |
| m_p = rhs.m_p; | |
| m_size = rhs.m_size; | |
| return *this; | |
| } | |
| inline readable_span& operator= (readable_span&& rhs) | |
| { | |
| if (this != &rhs) | |
| { | |
| m_p = rhs.m_p; | |
| m_size = rhs.m_size; | |
| rhs.m_p = nullptr; | |
| rhs.m_size = 0; | |
| } | |
| return *this; | |
| } | |
| inline const_reference operator* () const | |
| { | |
| if (!(m_p && m_size)) | |
| container_abort("readable_span invalid\n"); | |
| return *m_p; | |
| } | |
| inline const_pointer operator-> () const | |
| { | |
| if (!(m_p && m_size)) | |
| container_abort("readable_span invalid\n"); | |
| return m_p; | |
| } | |
| inline readable_span& remove_prefix(size_t n) | |
| { | |
| if ((!m_p) || (n > m_size)) | |
| { | |
| assert(0); | |
| return *this; | |
| } | |
| m_p += n; | |
| m_size -= n; | |
| return *this; | |
| } | |
| inline readable_span& remove_suffix(size_t n) | |
| { | |
| if ((!m_p) || (n > m_size)) | |
| { | |
| assert(0); | |
| return *this; | |
| } | |
| m_size -= n; | |
| return *this; | |
| } | |
| inline readable_span& enlarge(size_t n) | |
| { | |
| if (!m_p) | |
| { | |
| assert(0); | |
| return *this; | |
| } | |
| if (add_overflow_check(m_size, n)) | |
| { | |
| assert(0); | |
| return *this; | |
| } | |
| m_size += n; | |
| return *this; | |
| } | |
| bool copy_from(size_t src_ofs, size_t src_size, T* pDst, size_t dst_ofs) const | |
| { | |
| if (!src_size) | |
| return true; | |
| if (!pDst) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if (!is_inside(src_ofs, src_size)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| const_pointer pS = m_p + src_ofs; | |
| if (BASISU_IS_BITWISE_COPYABLE(T)) | |
| { | |
| const uint64_t num_bytes = (uint64_t)src_size * sizeof(T); | |
| if (!can_fit_into_size_t(num_bytes)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| memcpy(pDst, pS, (size_t)num_bytes); | |
| } | |
| else | |
| { | |
| T* pD = pDst + dst_ofs; | |
| T* pDst_end = pD + src_size; | |
| while (pD != pDst_end) | |
| *pD++ = *pS++; | |
| } | |
| return true; | |
| } | |
| inline const_reference operator[] (size_t idx) const | |
| { | |
| if ((!is_valid()) || (idx >= m_size)) | |
| container_abort("readable_span: invalid span or index\n"); | |
| return m_p[idx]; | |
| } | |
| inline uint16_t read_le16(size_t ofs) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(uint16_t))) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| const uint8_t a = (uint8_t)m_p[ofs]; | |
| const uint8_t b = (uint8_t)m_p[ofs + 1]; | |
| return a | (b << 8u); | |
| } | |
| template<typename R> | |
| inline R read_val(size_t ofs) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(R))) | |
| { | |
| assert(0); | |
| return (R)0; | |
| } | |
| return *reinterpret_cast<const R*>(&m_p[ofs]); | |
| } | |
| inline uint16_t read_be16(size_t ofs) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(uint16_t))) | |
| { | |
| assert(0); | |
| return 0; | |
| } | |
| const uint8_t b = (uint8_t)m_p[ofs]; | |
| const uint8_t a = (uint8_t)m_p[ofs + 1]; | |
| return a | (b << 8u); | |
| } | |
| inline uint32_t read_le32(size_t ofs) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(uint32_t))) | |
| { | |
| assert(0); | |
| return 0; | |
| } | |
| const uint8_t a = (uint8_t)m_p[ofs]; | |
| const uint8_t b = (uint8_t)m_p[ofs + 1]; | |
| const uint8_t c = (uint8_t)m_p[ofs + 2]; | |
| const uint8_t d = (uint8_t)m_p[ofs + 3]; | |
| return a | (b << 8u) | (c << 16u) | (d << 24u); | |
| } | |
| inline uint32_t read_be32(size_t ofs) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(uint32_t))) | |
| { | |
| assert(0); | |
| return 0; | |
| } | |
| const uint8_t d = (uint8_t)m_p[ofs]; | |
| const uint8_t c = (uint8_t)m_p[ofs + 1]; | |
| const uint8_t b = (uint8_t)m_p[ofs + 2]; | |
| const uint8_t a = (uint8_t)m_p[ofs + 3]; | |
| return a | (b << 8u) | (c << 16u) | (d << 24u); | |
| } | |
| inline uint64_t read_le64(size_t ofs) const | |
| { | |
| if (!add_overflow_check(ofs, sizeof(uint64_t))) | |
| { | |
| assert(0); | |
| return 0; | |
| } | |
| const uint64_t l = read_le32(ofs); | |
| const uint64_t h = read_le32(ofs + sizeof(uint32_t)); | |
| return l | (h << 32u); | |
| } | |
| inline uint64_t read_be64(size_t ofs) const | |
| { | |
| if (!add_overflow_check(ofs, sizeof(uint64_t))) | |
| { | |
| assert(0); | |
| return 0; | |
| } | |
| const uint64_t h = read_be32(ofs); | |
| const uint64_t l = read_be32(ofs + sizeof(uint32_t)); | |
| return l | (h << 32u); | |
| } | |
| private: | |
| const_pointer m_p; | |
| size_t m_size; | |
| }; | |
| template<typename T> | |
| class writable_span | |
| { | |
| friend readable_span<T>; | |
| public: | |
| using value_type = T; | |
| using size_type = size_t; | |
| using const_pointer = const T*; | |
| using const_reference = const T&; | |
| using const_iterator = const T*; | |
| using pointer = T*; | |
| using reference = T&; | |
| using iterator = T*; | |
| inline writable_span() : | |
| m_p(nullptr), | |
| m_size(0) | |
| { | |
| } | |
| inline writable_span(T* p, size_t n) | |
| { | |
| set(p, n); | |
| } | |
| inline writable_span(T* s, T* e) | |
| { | |
| set(s, e); | |
| } | |
| inline writable_span(const writable_span& other) : | |
| m_p(other.m_p), | |
| m_size(other.m_size) | |
| { | |
| assert(!m_size || m_p); | |
| } | |
| inline writable_span(writable_span&& other) : | |
| m_p(other.m_p), | |
| m_size(other.m_size) | |
| { | |
| assert(!m_size || m_p); | |
| other.m_p = nullptr; | |
| other.m_size = 0; | |
| } | |
| template <size_t N> | |
| inline writable_span(T(&arr)[N]) : | |
| m_p(arr), | |
| m_size(N) | |
| { | |
| } | |
| readable_span<T> get_readable_span() const | |
| { | |
| return readable_span<T>(m_p, m_size); | |
| } | |
| template <size_t N> | |
| inline writable_span& set(T(&arr)[N]) | |
| { | |
| m_p = arr; | |
| m_size = N; | |
| return *this; | |
| } | |
| inline writable_span& set(T* p, size_t n) | |
| { | |
| if (!p && n) | |
| { | |
| assert(0); | |
| m_p = nullptr; | |
| m_size = 0; | |
| } | |
| else | |
| { | |
| m_p = p; | |
| m_size = n; | |
| } | |
| return *this; | |
| } | |
| inline writable_span& set(T* s, T* e) | |
| { | |
| if ((e < s) || (!s && e)) | |
| { | |
| assert(0); | |
| m_p = nullptr; | |
| m_size = 0; | |
| } | |
| else | |
| { | |
| m_p = s; | |
| m_size = e - s; | |
| } | |
| return *this; | |
| } | |
| inline bool operator== (const writable_span& rhs) const | |
| { | |
| return (m_p == rhs.m_p) && (m_size == rhs.m_size); | |
| } | |
| inline bool operator== (const readable_span<T>& rhs) const | |
| { | |
| return (m_p == rhs.m_p) && (m_size == rhs.m_size); | |
| } | |
| inline bool operator!= (const writable_span& rhs) const | |
| { | |
| return (m_p != rhs.m_p) || (m_size != rhs.m_size); | |
| } | |
| inline bool operator!= (const readable_span<T>& rhs) const | |
| { | |
| return (m_p != rhs.m_p) || (m_size != rhs.m_size); | |
| } | |
| // only true if the region is totally inside the span | |
| inline bool is_inside_ptr(const_pointer p, size_t n) const | |
| { | |
| if (!is_valid()) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if (!p) | |
| { | |
| assert(!n); | |
| return false; | |
| } | |
| return (p >= m_p) && ((p + n) <= end()); | |
| } | |
| inline bool is_inside(size_t ofs, size_t size) const | |
| { | |
| if (add_overflow_check(ofs, size)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if (!is_valid()) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if ((ofs + size) > m_size) | |
| return false; | |
| return true; | |
| } | |
| inline writable_span subspan(size_t ofs, size_t n) const | |
| { | |
| if (!is_valid()) | |
| { | |
| assert(0); | |
| return writable_span((T*)nullptr, (size_t)0); | |
| } | |
| if (add_overflow_check(ofs, n)) | |
| { | |
| assert(0); | |
| return writable_span((T*)nullptr, (size_t)0); | |
| } | |
| if ((ofs + n) > m_size) | |
| { | |
| assert(0); | |
| return writable_span((T*)nullptr, (size_t)0); | |
| } | |
| return writable_span(m_p + ofs, n); | |
| } | |
| void clear() | |
| { | |
| m_p = nullptr; | |
| m_size = 0; | |
| } | |
| inline bool empty() const { return !m_size; } | |
| // true if the span is non-nullptr and is not empty | |
| inline bool is_valid() const { return m_p && m_size; } | |
| inline bool is_nullptr() const { return m_p == nullptr; } | |
| inline size_t size() const { return m_size; } | |
| inline size_t size_in_bytes() const { assert(can_fit_into_size_t((uint64_t)m_size * sizeof(T))); return m_size * sizeof(T); } | |
| inline T* get_ptr() const { return m_p; } | |
| inline iterator begin() const { return m_p; } | |
| inline iterator end() const { assert(m_p || !m_size); return m_p + m_size; } | |
| inline const_iterator cbegin() const { return m_p; } | |
| inline const_iterator cend() const { assert(m_p || !m_size); return m_p + m_size; } | |
| inline T& front() const | |
| { | |
| if (!(m_p && m_size)) | |
| container_abort("writable_span invalid\n"); | |
| return m_p[0]; | |
| } | |
| inline T& back() const | |
| { | |
| if (!(m_p && m_size)) | |
| container_abort("writable_span invalid\n"); | |
| return m_p[m_size - 1]; | |
| } | |
| inline writable_span& operator= (const writable_span& rhs) | |
| { | |
| m_p = rhs.m_p; | |
| m_size = rhs.m_size; | |
| return *this; | |
| } | |
| inline writable_span& operator= (writable_span&& rhs) | |
| { | |
| if (this != &rhs) | |
| { | |
| m_p = rhs.m_p; | |
| m_size = rhs.m_size; | |
| rhs.m_p = nullptr; | |
| rhs.m_size = 0; | |
| } | |
| return *this; | |
| } | |
| inline T& operator* () const | |
| { | |
| if (!(m_p && m_size)) | |
| container_abort("writable_span invalid\n"); | |
| return *m_p; | |
| } | |
| inline T* operator-> () const | |
| { | |
| if (!(m_p && m_size)) | |
| container_abort("writable_span invalid\n"); | |
| return m_p; | |
| } | |
| inline bool set_all(size_t ofs, size_t size, const_reference val) | |
| { | |
| if (!size) | |
| return true; | |
| if (!is_inside(ofs, size)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| T* pDst = m_p + ofs; | |
| if ((sizeof(T) == sizeof(uint8_t)) && (BASISU_IS_BITWISE_COPYABLE(T))) | |
| { | |
| memset(pDst, (int)((uint8_t)val), size); | |
| } | |
| else | |
| { | |
| T* pDst_end = pDst + size; | |
| while (pDst != pDst_end) | |
| *pDst++ = val; | |
| } | |
| return true; | |
| } | |
| inline bool set_all(const_reference val) | |
| { | |
| return set_all(0, m_size, val); | |
| } | |
| inline writable_span& remove_prefix(size_t n) | |
| { | |
| if ((!m_p) || (n > m_size)) | |
| { | |
| assert(0); | |
| return *this; | |
| } | |
| m_p += n; | |
| m_size -= n; | |
| return *this; | |
| } | |
| inline writable_span& remove_suffix(size_t n) | |
| { | |
| if ((!m_p) || (n > m_size)) | |
| { | |
| assert(0); | |
| return *this; | |
| } | |
| m_size -= n; | |
| return *this; | |
| } | |
| inline writable_span& enlarge(size_t n) | |
| { | |
| if (!m_p) | |
| { | |
| assert(0); | |
| return *this; | |
| } | |
| if (add_overflow_check(m_size, n)) | |
| { | |
| assert(0); | |
| return *this; | |
| } | |
| m_size += n; | |
| return *this; | |
| } | |
| // copy from this span to the destination ptr | |
| bool copy_from(size_t src_ofs, size_t src_size, T* pDst, size_t dst_ofs) const | |
| { | |
| if (!src_size) | |
| return true; | |
| if (!pDst) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if (!is_inside(src_ofs, src_size)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| const_pointer pS = m_p + src_ofs; | |
| if (BASISU_IS_BITWISE_COPYABLE(T)) | |
| { | |
| const uint64_t num_bytes = (uint64_t)src_size * sizeof(T); | |
| if (!can_fit_into_size_t(num_bytes)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| memcpy(pDst, pS, (size_t)num_bytes); | |
| } | |
| else | |
| { | |
| T* pD = pDst + dst_ofs; | |
| T* pDst_end = pD + src_size; | |
| while (pD != pDst_end) | |
| *pD++ = *pS++; | |
| } | |
| return true; | |
| } | |
| // copy from the source ptr into this span | |
| bool copy_into(const_pointer pSrc, size_t src_ofs, size_t src_size, size_t dst_ofs) const | |
| { | |
| if (!src_size) | |
| return true; | |
| if (!pSrc) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if (add_overflow_check(src_ofs, src_size) || add_overflow_check(dst_ofs, src_size)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if (!is_valid()) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if (!is_inside(dst_ofs, src_size)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| const_pointer pS = pSrc + src_ofs; | |
| T* pD = m_p + dst_ofs; | |
| if (BASISU_IS_BITWISE_COPYABLE(T)) | |
| { | |
| const uint64_t num_bytes = (uint64_t)src_size * sizeof(T); | |
| if (!can_fit_into_size_t(num_bytes)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| memcpy(pD, pS, (size_t)num_bytes); | |
| } | |
| else | |
| { | |
| T* pDst_end = pD + src_size; | |
| while (pD != pDst_end) | |
| *pD++ = *pS++; | |
| } | |
| return true; | |
| } | |
| // copy from a source span into this span | |
| bool copy_into(const readable_span<T>& src, size_t src_ofs, size_t src_size, size_t dst_ofs) const | |
| { | |
| if (!src.is_inside(src_ofs, src_size)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| return copy_into(src.get_ptr(), src_ofs, src_size, dst_ofs); | |
| } | |
| // copy from a source span into this span | |
| bool copy_into(const writable_span& src, size_t src_ofs, size_t src_size, size_t dst_ofs) const | |
| { | |
| if (!src.is_inside(src_ofs, src_size)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| return copy_into(src.get_ptr(), src_ofs, src_size, dst_ofs); | |
| } | |
| inline T& operator[] (size_t idx) const | |
| { | |
| if ((!is_valid()) || (idx >= m_size)) | |
| container_abort("writable_span: invalid span or index\n"); | |
| return m_p[idx]; | |
| } | |
| template<typename R> | |
| inline R read_val(size_t ofs) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(R))) | |
| { | |
| assert(0); | |
| return (R)0; | |
| } | |
| return *reinterpret_cast<const R*>(&m_p[ofs]); | |
| } | |
| template<typename R> | |
| inline bool write_val(size_t ofs, R val) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(R))) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| *reinterpret_cast<R*>(&m_p[ofs]) = val; | |
| return true; | |
| } | |
| inline bool write_le16(size_t ofs, uint16_t val) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(uint16_t))) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| m_p[ofs] = (uint8_t)val; | |
| m_p[ofs + 1] = (uint8_t)(val >> 8u); | |
| return true; | |
| } | |
| inline bool write_be16(size_t ofs, uint16_t val) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(uint16_t))) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| m_p[ofs + 1] = (uint8_t)val; | |
| m_p[ofs] = (uint8_t)(val >> 8u); | |
| return true; | |
| } | |
| inline bool write_le32(size_t ofs, uint32_t val) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(uint32_t))) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| m_p[ofs] = (uint8_t)val; | |
| m_p[ofs + 1] = (uint8_t)(val >> 8u); | |
| m_p[ofs + 2] = (uint8_t)(val >> 16u); | |
| m_p[ofs + 3] = (uint8_t)(val >> 24u); | |
| return true; | |
| } | |
| inline bool write_be32(size_t ofs, uint32_t val) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(uint32_t))) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| m_p[ofs + 3] = (uint8_t)val; | |
| m_p[ofs + 2] = (uint8_t)(val >> 8u); | |
| m_p[ofs + 1] = (uint8_t)(val >> 16u); | |
| m_p[ofs] = (uint8_t)(val >> 24u); | |
| return true; | |
| } | |
| inline bool write_le64(size_t ofs, uint64_t val) const | |
| { | |
| if (!add_overflow_check(ofs, sizeof(uint64_t))) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| return write_le32(ofs, (uint32_t)val) && write_le32(ofs + sizeof(uint32_t), (uint32_t)(val >> 32u)); | |
| } | |
| inline bool write_be64(size_t ofs, uint64_t val) const | |
| { | |
| if (!add_overflow_check(ofs, sizeof(uint64_t))) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| return write_be32(ofs + sizeof(uint32_t), (uint32_t)val) && write_be32(ofs, (uint32_t)(val >> 32u)); | |
| } | |
| inline uint16_t read_le16(size_t ofs) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(uint16_t))) | |
| { | |
| assert(0); | |
| return 0; | |
| } | |
| const uint8_t a = (uint8_t)m_p[ofs]; | |
| const uint8_t b = (uint8_t)m_p[ofs + 1]; | |
| return a | (b << 8u); | |
| } | |
| inline uint16_t read_be16(size_t ofs) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(uint16_t))) | |
| { | |
| assert(0); | |
| return 0; | |
| } | |
| const uint8_t b = (uint8_t)m_p[ofs]; | |
| const uint8_t a = (uint8_t)m_p[ofs + 1]; | |
| return a | (b << 8u); | |
| } | |
| inline uint32_t read_le32(size_t ofs) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(uint32_t))) | |
| { | |
| assert(0); | |
| return 0; | |
| } | |
| const uint8_t a = (uint8_t)m_p[ofs]; | |
| const uint8_t b = (uint8_t)m_p[ofs + 1]; | |
| const uint8_t c = (uint8_t)m_p[ofs + 2]; | |
| const uint8_t d = (uint8_t)m_p[ofs + 3]; | |
| return a | (b << 8u) | (c << 16u) | (d << 24u); | |
| } | |
| inline uint32_t read_be32(size_t ofs) const | |
| { | |
| static_assert(sizeof(T) == 1, "T must be byte size"); | |
| if (!is_inside(ofs, sizeof(uint32_t))) | |
| { | |
| assert(0); | |
| return 0; | |
| } | |
| const uint8_t d = (uint8_t)m_p[ofs]; | |
| const uint8_t c = (uint8_t)m_p[ofs + 1]; | |
| const uint8_t b = (uint8_t)m_p[ofs + 2]; | |
| const uint8_t a = (uint8_t)m_p[ofs + 3]; | |
| return a | (b << 8u) | (c << 16u) | (d << 24u); | |
| } | |
| inline uint64_t read_le64(size_t ofs) const | |
| { | |
| if (!add_overflow_check(ofs, sizeof(uint64_t))) | |
| { | |
| assert(0); | |
| return 0; | |
| } | |
| const uint64_t l = read_le32(ofs); | |
| const uint64_t h = read_le32(ofs + sizeof(uint32_t)); | |
| return l | (h << 32u); | |
| } | |
| inline uint64_t read_be64(size_t ofs) const | |
| { | |
| if (!add_overflow_check(ofs, sizeof(uint64_t))) | |
| { | |
| assert(0); | |
| return 0; | |
| } | |
| const uint64_t h = read_be32(ofs); | |
| const uint64_t l = read_be32(ofs + sizeof(uint32_t)); | |
| return l | (h << 32u); | |
| } | |
| private: | |
| T* m_p; | |
| size_t m_size; | |
| }; | |
| template<typename T> | |
| inline readable_span<T>::readable_span(const writable_span<T>& other) : | |
| m_p(other.m_p), | |
| m_size(other.m_size) | |
| { | |
| } | |
| template<typename T> | |
| inline readable_span<T>& readable_span<T>::operator= (const writable_span<T>& rhs) | |
| { | |
| m_p = rhs.m_p; | |
| m_size = rhs.m_size; | |
| return *this; | |
| } | |
| template<typename T> | |
| inline bool span_copy(const writable_span<T>& dst, const readable_span<T>& src) | |
| { | |
| return dst.copy_into(src, 0, src.size(), 0); | |
| } | |
| template<typename T> | |
| inline bool span_copy(const writable_span<T>& dst, const writable_span<T>& src) | |
| { | |
| return dst.copy_into(src, 0, src.size(), 0); | |
| } | |
| template<typename T> | |
| inline bool span_copy(const writable_span<T>& dst, size_t dst_ofs, const writable_span<T>& src, size_t src_ofs, size_t len) | |
| { | |
| return dst.copy_into(src, src_ofs, len, dst_ofs); | |
| } | |
| template<typename T> | |
| inline bool span_copy(const writable_span<T>& dst, size_t dst_ofs, const readable_span<T>& src, size_t src_ofs, size_t len) | |
| { | |
| return dst.copy_into(src, src_ofs, len, dst_ofs); | |
| } | |
| template<typename T> | |
| class vector : public rel_ops< vector<T> > | |
| { | |
| public: | |
| typedef T* iterator; | |
| typedef const T* const_iterator; | |
| typedef T value_type; | |
| typedef T& reference; | |
| typedef const T& const_reference; | |
| typedef T* pointer; | |
| typedef const T* const_pointer; | |
| inline vector() : | |
| m_p(nullptr), | |
| m_size(0), | |
| m_capacity(0) | |
| { | |
| } | |
| inline vector(size_t n, const T& init) : | |
| m_p(nullptr), | |
| m_size(0), | |
| m_capacity(0) | |
| { | |
| increase_capacity(n, false); | |
| construct_array(m_p, n, init); | |
| m_size = n; | |
| } | |
| inline vector(vector&& other) : | |
| m_p(other.m_p), | |
| m_size(other.m_size), | |
| m_capacity(other.m_capacity) | |
| { | |
| other.m_p = nullptr; | |
| other.m_size = 0; | |
| other.m_capacity = 0; | |
| } | |
| inline vector(const vector& other) : | |
| m_p(nullptr), | |
| m_size(0), | |
| m_capacity(0) | |
| { | |
| increase_capacity(other.m_size, false); | |
| m_size = other.m_size; | |
| if (BASISU_IS_BITWISE_COPYABLE(T)) | |
| { | |
| #if defined(__GNUC__) && !defined(__clang__) | |
| #pragma GCC diagnostic push | |
| #pragma GCC diagnostic ignored "-Wclass-memaccess" | |
| #endif | |
| if ((m_p) && (other.m_p)) | |
| { | |
| memcpy((void *)m_p, other.m_p, m_size * sizeof(T)); | |
| } | |
| #if defined(__GNUC__) && !defined(__clang__) | |
| #pragma GCC diagnostic pop | |
| #endif | |
| } | |
| else | |
| { | |
| T* pDst = m_p; | |
| const T* pSrc = other.m_p; | |
| for (size_t i = m_size; i > 0; i--) | |
| construct(pDst++, *pSrc++); | |
| } | |
| } | |
| inline explicit vector(size_t size) : | |
| m_p(nullptr), | |
| m_size(0), | |
| m_capacity(0) | |
| { | |
| resize(size); | |
| } | |
| inline explicit vector(std::initializer_list<T> init_list) : | |
| m_p(nullptr), | |
| m_size(0), | |
| m_capacity(0) | |
| { | |
| resize(init_list.size()); | |
| size_t idx = 0; | |
| for (const T& elem : init_list) | |
| m_p[idx++] = elem; | |
| assert(idx == m_size); | |
| } | |
| inline vector(const readable_span<T>& rs) : | |
| m_p(nullptr), | |
| m_size(0), | |
| m_capacity(0) | |
| { | |
| set(rs); | |
| } | |
| inline vector(const writable_span<T>& ws) : | |
| m_p(nullptr), | |
| m_size(0), | |
| m_capacity(0) | |
| { | |
| set(ws); | |
| } | |
| // mostly to ease porting from std::vector, not particularly optimized | |
| inline void assign(size_t new_size, const T& init) | |
| { | |
| assert(!m_p || (&init < m_p) || (&init >= (m_p + m_size))); | |
| // Blow away existing contents | |
| resize(0); | |
| if (new_size) | |
| { | |
| resize(new_size); | |
| for (size_t i = 0; i < new_size; ++i) | |
| m_p[i] = init; | |
| } | |
| } | |
| // mostly to ease porting from std::vector, not particularly optimized | |
| template<typename R> | |
| inline void assign(const R* pBegin, const R* pEnd) | |
| { | |
| assert(!m_p || | |
| (reinterpret_cast<const uint8_t *>(pEnd) <= reinterpret_cast<const uint8_t*>(m_p)) || | |
| (reinterpret_cast<const uint8_t *>(pBegin) >= reinterpret_cast<const uint8_t*>(m_p + m_size)) | |
| ); | |
| // Blow away existing contents | |
| resize(0); | |
| if ((!pBegin) || (!pEnd) || (pEnd <= pBegin)) | |
| { | |
| assert(0); | |
| return; | |
| } | |
| const size_t new_size = static_cast<size_t>(static_cast<ptrdiff_t>(pEnd - pBegin)); | |
| if (new_size) | |
| { | |
| resize(new_size); | |
| for (size_t i = 0; i < new_size; ++i) | |
| m_p[i] = static_cast<T>(*pBegin++); | |
| } | |
| } | |
| // Set contents of vector to contents of the readable span | |
| bool set(const readable_span<T>& rs) | |
| { | |
| if (!rs.is_valid()) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| const size_t new_size = rs.size(); | |
| // Could call resize(), but it'll redundantly construct trivial types. | |
| if (m_size != new_size) | |
| { | |
| if (new_size < m_size) | |
| { | |
| if (BASISU_HAS_DESTRUCTOR(T)) | |
| { | |
| scalar_type<T>::destruct_array(m_p + new_size, m_size - new_size); | |
| } | |
| } | |
| else | |
| { | |
| if (new_size > m_capacity) | |
| { | |
| if (!increase_capacity(new_size, false, true)) | |
| return false; | |
| } | |
| } | |
| // Don't bother constructing trivial types, because we're going to memcpy() over them anyway. | |
| if (!BASISU_IS_BITWISE_COPYABLE(T)) | |
| { | |
| scalar_type<T>::construct_array(m_p + m_size, new_size - m_size); | |
| } | |
| m_size = new_size; | |
| } | |
| if (!rs.copy_from(0, rs.size(), m_p, 0)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| return true; | |
| } | |
| // Set contents of vector to contents of the writable span | |
| inline bool set(const writable_span<T>& ws) | |
| { | |
| return set(ws.get_readable_span()); | |
| } | |
| inline ~vector() | |
| { | |
| if (m_p) | |
| { | |
| if (BASISU_HAS_DESTRUCTOR(T)) | |
| { | |
| scalar_type<T>::destruct_array(m_p, m_size); | |
| } | |
| free(m_p); | |
| } | |
| } | |
| inline vector& operator= (const vector& other) | |
| { | |
| if (this == &other) | |
| return *this; | |
| if (m_capacity >= other.m_size) | |
| resize(0); | |
| else | |
| { | |
| clear(); | |
| increase_capacity(other.m_size, false); | |
| } | |
| if (BASISU_IS_BITWISE_COPYABLE(T)) | |
| { | |
| #if defined(__GNUC__) && !defined(__clang__) | |
| #pragma GCC diagnostic push | |
| #pragma GCC diagnostic ignored "-Wclass-memaccess" | |
| #endif | |
| if ((m_p) && (other.m_p)) | |
| memcpy((void *)m_p, other.m_p, other.m_size * sizeof(T)); | |
| #if defined(__GNUC__) && !defined(__clang__) | |
| #pragma GCC diagnostic pop | |
| #endif | |
| } | |
| else | |
| { | |
| T* pDst = m_p; | |
| const T* pSrc = other.m_p; | |
| for (size_t i = other.m_size; i > 0; i--) | |
| construct(pDst++, *pSrc++); | |
| } | |
| m_size = other.m_size; | |
| return *this; | |
| } | |
| inline vector& operator= (vector&& rhs) | |
| { | |
| if (this != &rhs) | |
| { | |
| clear(); | |
| m_p = rhs.m_p; | |
| m_size = rhs.m_size; | |
| m_capacity = rhs.m_capacity; | |
| rhs.m_p = nullptr; | |
| rhs.m_size = 0; | |
| rhs.m_capacity = 0; | |
| } | |
| return *this; | |
| } | |
| BASISU_FORCE_INLINE const T* begin() const { return m_p; } | |
| BASISU_FORCE_INLINE T* begin() { return m_p; } | |
| BASISU_FORCE_INLINE const T* end() const { return m_p + m_size; } | |
| BASISU_FORCE_INLINE T* end() { return m_p + m_size; } | |
| BASISU_FORCE_INLINE bool empty() const { return !m_size; } | |
| BASISU_FORCE_INLINE size_t size() const { return m_size; } | |
| BASISU_FORCE_INLINE uint32_t size_u32() const { assert(m_size <= UINT32_MAX); return static_cast<uint32_t>(m_size); } | |
| BASISU_FORCE_INLINE size_t size_in_bytes() const { return m_size * sizeof(T); } | |
| BASISU_FORCE_INLINE uint32_t size_in_bytes_u32() const { assert((m_size * sizeof(T)) <= UINT32_MAX); return static_cast<uint32_t>(m_size * sizeof(T)); } | |
| BASISU_FORCE_INLINE size_t capacity() const { return m_capacity; } | |
| #if !BASISU_VECTOR_FORCE_CHECKING | |
| BASISU_FORCE_INLINE const T& operator[] (size_t i) const { assert(i < m_size); return m_p[i]; } | |
| BASISU_FORCE_INLINE T& operator[] (size_t i) { assert(i < m_size); return m_p[i]; } | |
| #else | |
| BASISU_FORCE_INLINE const T& operator[] (size_t i) const | |
| { | |
| if (i >= m_size) | |
| container_abort("vector::operator[] invalid index: %zu, max entries %u, type size %zu\n", i, m_size, sizeof(T)); | |
| return m_p[i]; | |
| } | |
| BASISU_FORCE_INLINE T& operator[] (size_t i) | |
| { | |
| if (i >= m_size) | |
| container_abort("vector::operator[] invalid index: %zu, max entries %u, type size %zu\n", i, m_size, sizeof(T)); | |
| return m_p[i]; | |
| } | |
| #endif | |
| // at() always includes range checking, even in final builds, unlike operator []. | |
| BASISU_FORCE_INLINE const T& at(size_t i) const | |
| { | |
| if (i >= m_size) | |
| container_abort("vector::at() invalid index: %zu, max entries %u, type size %zu\n", i, m_size, sizeof(T)); | |
| return m_p[i]; | |
| } | |
| BASISU_FORCE_INLINE T& at(size_t i) | |
| { | |
| if (i >= m_size) | |
| container_abort("vector::at() invalid index: %zu, max entries %u, type size %zu\n", i, m_size, sizeof(T)); | |
| return m_p[i]; | |
| } | |
| #if !BASISU_VECTOR_FORCE_CHECKING | |
| BASISU_FORCE_INLINE const T& front() const { assert(m_size); return m_p[0]; } | |
| BASISU_FORCE_INLINE T& front() { assert(m_size); return m_p[0]; } | |
| BASISU_FORCE_INLINE const T& back() const { assert(m_size); return m_p[m_size - 1]; } | |
| BASISU_FORCE_INLINE T& back() { assert(m_size); return m_p[m_size - 1]; } | |
| #else | |
| BASISU_FORCE_INLINE const T& front() const | |
| { | |
| if (!m_size) | |
| container_abort("front: vector is empty, type size %zu\n", sizeof(T)); | |
| return m_p[0]; | |
| } | |
| BASISU_FORCE_INLINE T& front() | |
| { | |
| if (!m_size) | |
| container_abort("front: vector is empty, type size %zu\n", sizeof(T)); | |
| return m_p[0]; | |
| } | |
| BASISU_FORCE_INLINE const T& back() const | |
| { | |
| if (!m_size) | |
| container_abort("back: vector is empty, type size %zu\n", sizeof(T)); | |
| return m_p[m_size - 1]; | |
| } | |
| BASISU_FORCE_INLINE T& back() | |
| { | |
| if (!m_size) | |
| container_abort("back: vector is empty, type size %zu\n", sizeof(T)); | |
| return m_p[m_size - 1]; | |
| } | |
| #endif | |
| BASISU_FORCE_INLINE const T* get_ptr() const { return m_p; } | |
| BASISU_FORCE_INLINE T* get_ptr() { return m_p; } | |
| BASISU_FORCE_INLINE const T* data() const { return m_p; } | |
| BASISU_FORCE_INLINE T* data() { return m_p; } | |
| // clear() sets the container to empty, then frees the allocated block. | |
| inline void clear() | |
| { | |
| if (m_p) | |
| { | |
| if (BASISU_HAS_DESTRUCTOR(T)) | |
| { | |
| scalar_type<T>::destruct_array(m_p, m_size); | |
| } | |
| free(m_p); | |
| m_p = nullptr; | |
| m_size = 0; | |
| m_capacity = 0; | |
| } | |
| } | |
| inline void clear_no_destruction() | |
| { | |
| if (m_p) | |
| { | |
| free(m_p); | |
| m_p = nullptr; | |
| m_size = 0; | |
| m_capacity = 0; | |
| } | |
| } | |
| inline void reserve(size_t new_capacity) | |
| { | |
| if (!try_reserve(new_capacity)) | |
| container_abort("vector:reserve: try_reserve failed!\n"); | |
| } | |
| inline bool try_reserve(size_t new_capacity) | |
| { | |
| if (new_capacity > m_capacity) | |
| { | |
| if (!increase_capacity(new_capacity, false, true)) | |
| return false; | |
| } | |
| else if (new_capacity < m_capacity) | |
| { | |
| // Must work around the lack of a "decrease_capacity()" method. | |
| // This case is rare enough in practice that it's probably not worth implementing an optimized in-place resize. | |
| vector tmp; | |
| if (!tmp.increase_capacity(helpers::maximum(m_size, new_capacity), false, true)) | |
| return false; | |
| tmp = *this; | |
| swap(tmp); | |
| } | |
| return true; | |
| } | |
| // try_resize(0) sets the container to empty, but does not free the allocated block. | |
| inline bool try_resize(size_t new_size, bool grow_hint = false) | |
| { | |
| if (m_size != new_size) | |
| { | |
| if (new_size < m_size) | |
| { | |
| if (BASISU_HAS_DESTRUCTOR(T)) | |
| { | |
| scalar_type<T>::destruct_array(m_p + new_size, m_size - new_size); | |
| } | |
| } | |
| else | |
| { | |
| if (new_size > m_capacity) | |
| { | |
| if (!increase_capacity(new_size, (new_size == (m_size + 1)) || grow_hint, true)) | |
| return false; | |
| } | |
| scalar_type<T>::construct_array(m_p + m_size, new_size - m_size); | |
| } | |
| m_size = new_size; | |
| } | |
| return true; | |
| } | |
| // resize(0) sets the container to empty, but does not free the allocated block. | |
| inline void resize(size_t new_size, bool grow_hint = false) | |
| { | |
| if (!try_resize(new_size, grow_hint)) | |
| container_abort("vector::resize failed, new size %zu\n", new_size); | |
| } | |
| // If size >= capacity/2, reset() sets the container's size to 0 but doesn't free the allocated block (because the container may be similarly loaded in the future). | |
| // Otherwise it blows away the allocated block. See http://www.codercorner.com/blog/?p=494 | |
| inline void reset() | |
| { | |
| if (m_size >= (m_capacity >> 1)) | |
| resize(0); | |
| else | |
| clear(); | |
| } | |
| inline T* try_enlarge(size_t i) | |
| { | |
| size_t cur_size = m_size; | |
| if (add_overflow_check(cur_size, i)) | |
| return nullptr; | |
| if (!try_resize(cur_size + i, true)) | |
| return nullptr; | |
| return get_ptr() + cur_size; | |
| } | |
| inline T* enlarge(size_t i) | |
| { | |
| T* p = try_enlarge(i); | |
| if (!p) | |
| container_abort("vector::enlarge failed, amount %zu!\n", i); | |
| return p; | |
| } | |
| BASISU_FORCE_INLINE void push_back(const T& obj) | |
| { | |
| assert(!m_p || (&obj < m_p) || (&obj >= (m_p + m_size))); | |
| if (m_size >= m_capacity) | |
| { | |
| if (add_overflow_check(m_size, 1)) | |
| container_abort("vector::push_back: vector too large\n"); | |
| increase_capacity(m_size + 1, true); | |
| } | |
| scalar_type<T>::construct(m_p + m_size, obj); | |
| m_size++; | |
| } | |
| BASISU_FORCE_INLINE void push_back_value(T&& obj) | |
| { | |
| assert(!m_p || (&obj < m_p) || (&obj >= (m_p + m_size))); | |
| if (m_size >= m_capacity) | |
| { | |
| if (add_overflow_check(m_size, 1)) | |
| container_abort("vector::push_back_value: vector too large\n"); | |
| increase_capacity(m_size + 1, true); | |
| } | |
| new ((void*)(m_p + m_size)) T(std::move(obj)); | |
| m_size++; | |
| } | |
| inline bool try_push_back(const T& obj) | |
| { | |
| assert(!m_p || (&obj < m_p) || (&obj >= (m_p + m_size))); | |
| if (m_size >= m_capacity) | |
| { | |
| if (add_overflow_check(m_size, 1)) | |
| return false; | |
| if (!increase_capacity(m_size + 1, true, true)) | |
| return false; | |
| } | |
| scalar_type<T>::construct(m_p + m_size, obj); | |
| m_size++; | |
| return true; | |
| } | |
| inline bool try_push_back(T&& obj) | |
| { | |
| assert(!m_p || (&obj < m_p) || (&obj >= (m_p + m_size))); | |
| if (m_size >= m_capacity) | |
| { | |
| if (add_overflow_check(m_size, 1)) | |
| return false; | |
| if (!increase_capacity(m_size + 1, true, true)) | |
| return false; | |
| } | |
| new ((void*)(m_p + m_size)) T(std::move(obj)); | |
| m_size++; | |
| return true; | |
| } | |
| // obj is explictly passed in by value, not ref | |
| inline void push_back_value(T obj) | |
| { | |
| if (m_size >= m_capacity) | |
| { | |
| if (add_overflow_check(m_size, 1)) | |
| container_abort("vector::push_back_value: vector too large\n"); | |
| increase_capacity(m_size + 1, true); | |
| } | |
| scalar_type<T>::construct(m_p + m_size, obj); | |
| m_size++; | |
| } | |
| // obj is explictly passed in by value, not ref | |
| inline bool try_push_back_value(T obj) | |
| { | |
| if (m_size >= m_capacity) | |
| { | |
| if (add_overflow_check(m_size, 1)) | |
| return false; | |
| if (!increase_capacity(m_size + 1, true, true)) | |
| return false; | |
| } | |
| scalar_type<T>::construct(m_p + m_size, obj); | |
| m_size++; | |
| return true; | |
| } | |
| template<typename... Args> | |
| BASISU_FORCE_INLINE void emplace_back(Args&&... args) | |
| { | |
| if (m_size >= m_capacity) | |
| { | |
| if (add_overflow_check(m_size, 1)) | |
| container_abort("vector::enlarge: vector too large\n"); | |
| increase_capacity(m_size + 1, true); | |
| } | |
| new ((void*)(m_p + m_size)) T(std::forward<Args>(args)...); // perfect forwarding | |
| m_size++; | |
| } | |
| template<typename... Args> | |
| BASISU_FORCE_INLINE bool try_emplace_back(Args&&... args) | |
| { | |
| if (m_size >= m_capacity) | |
| { | |
| if (add_overflow_check(m_size, 1)) | |
| return false; | |
| if (!increase_capacity(m_size + 1, true, true)) | |
| return false; | |
| } | |
| new ((void*)(m_p + m_size)) T(std::forward<Args>(args)...); // perfect forwarding | |
| m_size++; | |
| return true; | |
| } | |
| inline void pop_back() | |
| { | |
| assert(m_size); | |
| if (m_size) | |
| { | |
| m_size--; | |
| scalar_type<T>::destruct(&m_p[m_size]); | |
| } | |
| } | |
| inline bool try_insert(size_t index, const T* p, size_t n) | |
| { | |
| assert(index <= m_size); | |
| if (index > m_size) | |
| return false; | |
| if (!n) | |
| return true; | |
| const size_t orig_size = m_size; | |
| if (add_overflow_check(m_size, n)) | |
| return false; | |
| if (!try_resize(m_size + n, true)) | |
| return false; | |
| const size_t num_to_move = orig_size - index; | |
| if (BASISU_IS_BITWISE_COPYABLE(T)) | |
| { | |
| // This overwrites the destination object bits, but bitwise copyable means we don't need to worry about destruction. | |
| memmove((void *)(m_p + index + n), m_p + index, sizeof(T) * num_to_move); | |
| } | |
| else | |
| { | |
| const T* pSrc = m_p + orig_size - 1; | |
| T* pDst = const_cast<T*>(pSrc) + n; | |
| for (size_t i = 0; i < num_to_move; i++) | |
| { | |
| assert((uint64_t)(pDst - m_p) < (uint64_t)m_size); | |
| *pDst = std::move(*pSrc); | |
| pDst--; | |
| pSrc--; | |
| } | |
| } | |
| T* pDst = m_p + index; | |
| if (BASISU_IS_BITWISE_COPYABLE(T)) | |
| { | |
| // This copies in the new bits, overwriting the existing objects, which is OK for copyable types that don't need destruction. | |
| memcpy((void *)pDst, p, sizeof(T) * n); | |
| } | |
| else | |
| { | |
| for (size_t i = 0; i < n; i++) | |
| { | |
| assert((uint64_t)(pDst - m_p) < (uint64_t)m_size); | |
| *pDst++ = *p++; | |
| } | |
| } | |
| return true; | |
| } | |
| inline void insert(size_t index, const T* p, size_t n) | |
| { | |
| if (!try_insert(index, p, n)) | |
| container_abort("vector::insert() failed!\n"); | |
| } | |
| inline bool try_insert(T* p, const T& obj) | |
| { | |
| if (p < begin()) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| uint64_t ofs = p - begin(); | |
| if (ofs > m_size) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if ((size_t)ofs != ofs) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| return try_insert((size_t)ofs, &obj, 1); | |
| } | |
| inline void insert(T* p, const T& obj) | |
| { | |
| if (!try_insert(p, obj)) | |
| container_abort("vector::insert() failed!\n"); | |
| } | |
| // push_front() isn't going to be very fast - it's only here for usability. | |
| inline void push_front(const T& obj) | |
| { | |
| insert(0, &obj, 1); | |
| } | |
| inline bool try_push_front(const T& obj) | |
| { | |
| return try_insert(0, &obj, 1); | |
| } | |
| vector& append(const vector& other) | |
| { | |
| if (other.m_size) | |
| insert(m_size, &other[0], other.m_size); | |
| return *this; | |
| } | |
| bool try_append(const vector& other) | |
| { | |
| if (other.m_size) | |
| return try_insert(m_size, &other[0], other.m_size); | |
| return true; | |
| } | |
| vector& append(const T* p, size_t n) | |
| { | |
| if (n) | |
| insert(m_size, p, n); | |
| return *this; | |
| } | |
| bool try_append(const T* p, size_t n) | |
| { | |
| if (n) | |
| return try_insert(m_size, p, n); | |
| return true; | |
| } | |
| inline bool erase(size_t start, size_t n) | |
| { | |
| if (add_overflow_check(start, n)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| assert((start + n) <= m_size); | |
| if ((start + n) > m_size) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if (!n) | |
| return true; | |
| const size_t num_to_move = m_size - (start + n); | |
| T* pDst = m_p + start; | |
| const T* pSrc = m_p + start + n; | |
| if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(T)) | |
| { | |
| // This test is overly cautious. | |
| if ((!BASISU_IS_BITWISE_COPYABLE(T)) || (BASISU_HAS_DESTRUCTOR(T))) | |
| { | |
| // Type has been marked explictly as bitwise movable, which means we can move them around but they may need to be destructed. | |
| // First destroy the erased objects. | |
| scalar_type<T>::destruct_array(pDst, n); | |
| } | |
| // Copy "down" the objects to preserve, filling in the empty slots. | |
| #if defined(__GNUC__) && !defined(__clang__) | |
| #pragma GCC diagnostic push | |
| #pragma GCC diagnostic ignored "-Wclass-memaccess" | |
| #endif | |
| memmove((void *)pDst, pSrc, num_to_move * sizeof(T)); | |
| #if defined(__GNUC__) && !defined(__clang__) | |
| #pragma GCC diagnostic pop | |
| #endif | |
| } | |
| else | |
| { | |
| // Type is not bitwise copyable or movable. | |
| // Move them down one at a time by using the equals operator, and destroying anything that's left over at the end. | |
| T* pDst_end = pDst + num_to_move; | |
| while (pDst != pDst_end) | |
| { | |
| *pDst = std::move(*pSrc); | |
| ++pDst; | |
| ++pSrc; | |
| } | |
| scalar_type<T>::destruct_array(pDst_end, n); | |
| } | |
| m_size -= n; | |
| return true; | |
| } | |
| inline bool erase_index(size_t index) | |
| { | |
| return erase(index, 1); | |
| } | |
| inline bool erase(T* p) | |
| { | |
| assert((p >= m_p) && (p < (m_p + m_size))); | |
| if (p < m_p) | |
| return false; | |
| return erase_index(static_cast<size_t>(p - m_p)); | |
| } | |
| inline bool erase(T* pFirst, T* pEnd) | |
| { | |
| assert(pFirst <= pEnd); | |
| assert(pFirst >= begin() && pFirst <= end()); | |
| assert(pEnd >= begin() && pEnd <= end()); | |
| if ((pFirst < begin()) || (pEnd < pFirst)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| uint64_t ofs = pFirst - begin(); | |
| if ((size_t)ofs != ofs) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| uint64_t n = pEnd - pFirst; | |
| if ((size_t)n != n) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| return erase((size_t)ofs, (size_t)n); | |
| } | |
| bool erase_unordered(size_t index) | |
| { | |
| if (index >= m_size) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if ((index + 1) < m_size) | |
| { | |
| (*this)[index] = std::move(back()); | |
| } | |
| pop_back(); | |
| return true; | |
| } | |
| inline bool operator== (const vector& rhs) const | |
| { | |
| if (m_size != rhs.m_size) | |
| return false; | |
| else if (m_size) | |
| { | |
| if (scalar_type<T>::cFlag) | |
| return memcmp(m_p, rhs.m_p, sizeof(T) * m_size) == 0; | |
| else | |
| { | |
| const T* pSrc = m_p; | |
| const T* pDst = rhs.m_p; | |
| for (size_t i = m_size; i; i--) | |
| if (!(*pSrc++ == *pDst++)) | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| inline bool operator< (const vector& rhs) const | |
| { | |
| const size_t min_size = helpers::minimum(m_size, rhs.m_size); | |
| const T* pSrc = m_p; | |
| const T* pSrc_end = m_p + min_size; | |
| const T* pDst = rhs.m_p; | |
| while ((pSrc < pSrc_end) && (*pSrc == *pDst)) | |
| { | |
| pSrc++; | |
| pDst++; | |
| } | |
| if (pSrc < pSrc_end) | |
| return *pSrc < *pDst; | |
| return m_size < rhs.m_size; | |
| } | |
| inline void swap(vector& other) | |
| { | |
| std::swap(m_p, other.m_p); | |
| std::swap(m_size, other.m_size); | |
| std::swap(m_capacity, other.m_capacity); | |
| } | |
| inline void sort() | |
| { | |
| std::sort(begin(), end()); | |
| } | |
| inline void unique() | |
| { | |
| if (!empty()) | |
| { | |
| sort(); | |
| resize(std::unique(begin(), end()) - begin()); | |
| } | |
| } | |
| inline void reverse() | |
| { | |
| const size_t j = m_size >> 1; | |
| for (size_t i = 0; i < j; i++) | |
| std::swap(m_p[i], m_p[m_size - 1 - i]); | |
| } | |
| inline bool find(const T& key, size_t &idx) const | |
| { | |
| idx = 0; | |
| const T* p = m_p; | |
| const T* p_end = m_p + m_size; | |
| size_t index = 0; | |
| while (p != p_end) | |
| { | |
| if (key == *p) | |
| { | |
| idx = index; | |
| return true; | |
| } | |
| p++; | |
| index++; | |
| } | |
| return false; | |
| } | |
| inline bool find_sorted(const T& key, size_t& idx) const | |
| { | |
| idx = 0; | |
| if (!m_size) | |
| return false; | |
| // Inclusive range | |
| size_t low = 0, high = m_size - 1; | |
| while (low <= high) | |
| { | |
| size_t mid = (size_t)(((uint64_t)low + (uint64_t)high) >> 1); | |
| const T* pTrial_key = m_p + mid; | |
| // Sanity check comparison operator | |
| assert(!((*pTrial_key < key) && (key < *pTrial_key))); | |
| if (*pTrial_key < key) | |
| { | |
| if (add_overflow_check(mid, 1)) | |
| break; | |
| low = mid + 1; | |
| } | |
| else if (key < *pTrial_key) | |
| { | |
| if (!mid) | |
| break; | |
| high = mid - 1; | |
| } | |
| else | |
| { | |
| idx = mid; | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| inline size_t count_occurences(const T& key) const | |
| { | |
| size_t c = 0; | |
| const T* p = m_p; | |
| const T* p_end = m_p + m_size; | |
| while (p != p_end) | |
| { | |
| if (key == *p) | |
| c++; | |
| p++; | |
| } | |
| return c; | |
| } | |
| inline void set_all(const T& o) | |
| { | |
| if ((sizeof(T) == 1) && (scalar_type<T>::cFlag)) | |
| { | |
| #if defined(__GNUC__) && !defined(__clang__) | |
| #pragma GCC diagnostic push | |
| #pragma GCC diagnostic ignored "-Wclass-memaccess" | |
| #endif | |
| memset(m_p, *reinterpret_cast<const uint8_t*>(&o), m_size); | |
| #if defined(__GNUC__) && !defined(__clang__) | |
| #pragma GCC diagnostic pop | |
| #endif | |
| } | |
| else | |
| { | |
| T* pDst = m_p; | |
| T* pDst_end = pDst + m_size; | |
| while (pDst != pDst_end) | |
| *pDst++ = o; | |
| } | |
| } | |
| // Caller assumes ownership of the heap block associated with the container. Container is cleared. | |
| // Caller must use free() on the returned pointer. | |
| inline void* assume_ownership() | |
| { | |
| T* p = m_p; | |
| m_p = nullptr; | |
| m_size = 0; | |
| m_capacity = 0; | |
| return p; | |
| } | |
| // Caller is granting ownership of the indicated heap block. | |
| // Block must have size constructed elements, and have enough room for capacity elements. | |
| // The block must have been allocated using malloc(). | |
| // Important: This method is used in Basis Universal. If you change how this container allocates memory, you'll need to change any users of this method. | |
| inline bool grant_ownership(T* p, size_t size, size_t capacity) | |
| { | |
| // To prevent the caller from obviously shooting themselves in the foot. | |
| if (((p + capacity) > m_p) && (p < (m_p + m_capacity))) | |
| { | |
| // Can grant ownership of a block inside the container itself! | |
| assert(0); | |
| return false; | |
| } | |
| if (size > capacity) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if (!p) | |
| { | |
| if (capacity) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| } | |
| else if (!capacity) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| clear(); | |
| m_p = p; | |
| m_size = size; | |
| m_capacity = capacity; | |
| return true; | |
| } | |
| readable_span<T> get_readable_span() const | |
| { | |
| return readable_span<T>(m_p, m_size); | |
| } | |
| writable_span<T> get_writable_span() | |
| { | |
| return writable_span<T>(m_p, m_size); | |
| } | |
| private: | |
| T* m_p; | |
| size_t m_size; // the number of constructed objects | |
| size_t m_capacity; // the size of the allocation | |
| template<typename Q> struct is_vector { enum { cFlag = false }; }; | |
| template<typename Q> struct is_vector< vector<Q> > { enum { cFlag = true }; }; | |
| static void object_mover(void* pDst_void, void* pSrc_void, size_t num) | |
| { | |
| T* pSrc = static_cast<T*>(pSrc_void); | |
| T* const pSrc_end = pSrc + num; | |
| T* pDst = static_cast<T*>(pDst_void); | |
| while (pSrc != pSrc_end) | |
| { | |
| new ((void*)(pDst)) T(std::move(*pSrc)); | |
| scalar_type<T>::destruct(pSrc); | |
| ++pSrc; | |
| ++pDst; | |
| } | |
| } | |
| inline bool increase_capacity(size_t min_new_capacity, bool grow_hint, bool nofail = false) | |
| { | |
| return reinterpret_cast<elemental_vector*>(this)->increase_capacity( | |
| min_new_capacity, grow_hint, sizeof(T), | |
| (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(T) || (is_vector<T>::cFlag)) ? nullptr : object_mover, nofail); | |
| } | |
| }; | |
| template<typename T> struct bitwise_movable< vector<T> > { enum { cFlag = true }; }; | |
| // Hash map | |
| // rg TODO 9/8/2024: I've upgraded this class to support 64-bit size_t, and it needs a lot more testing. | |
| const uint32_t SIZE_T_BITS = sizeof(size_t) * 8U; | |
| inline uint32_t safe_shift_left(uint32_t v, uint32_t l) | |
| { | |
| return (l < 32U) ? (v << l) : 0; | |
| } | |
| inline uint64_t safe_shift_left(uint64_t v, uint32_t l) | |
| { | |
| return (l < 64U) ? (v << l) : 0; | |
| } | |
| template <typename T> | |
| struct hasher | |
| { | |
| inline size_t operator() (const T& key) const { return static_cast<size_t>(key); } | |
| }; | |
| template <typename T> | |
| struct equal_to | |
| { | |
| inline bool operator()(const T& a, const T& b) const { return a == b; } | |
| }; | |
| // Important: The Hasher and Equals objects must be bitwise movable! | |
| template<typename Key, typename Value = empty_type, typename Hasher = hasher<Key>, typename Equals = equal_to<Key> > | |
| class hash_map | |
| { | |
| public: | |
| class iterator; | |
| class const_iterator; | |
| private: | |
| friend class iterator; | |
| friend class const_iterator; | |
| enum state | |
| { | |
| cStateInvalid = 0, | |
| cStateValid = 1 | |
| }; | |
| enum | |
| { | |
| cMinHashSize = 4U | |
| }; | |
| public: | |
| typedef hash_map<Key, Value, Hasher, Equals> hash_map_type; | |
| typedef std::pair<Key, Value> value_type; | |
| typedef Key key_type; | |
| typedef Value referent_type; | |
| typedef Hasher hasher_type; | |
| typedef Equals equals_type; | |
| hash_map() : | |
| m_num_valid(0), | |
| m_grow_threshold(0), | |
| m_hash_shift(SIZE_T_BITS) | |
| { | |
| static_assert((SIZE_T_BITS == 32) || (SIZE_T_BITS == 64), "SIZE_T_BITS must be 32 or 64"); | |
| } | |
| hash_map(const hash_map& other) : | |
| m_values(other.m_values), | |
| m_num_valid(other.m_num_valid), | |
| m_grow_threshold(other.m_grow_threshold), | |
| m_hash_shift(other.m_hash_shift), | |
| m_hasher(other.m_hasher), | |
| m_equals(other.m_equals) | |
| { | |
| static_assert((SIZE_T_BITS == 32) || (SIZE_T_BITS == 64), "SIZE_T_BITS must be 32 or 64"); | |
| } | |
| hash_map(hash_map&& other) : | |
| m_values(std::move(other.m_values)), | |
| m_num_valid(other.m_num_valid), | |
| m_grow_threshold(other.m_grow_threshold), | |
| m_hash_shift(other.m_hash_shift), | |
| m_hasher(std::move(other.m_hasher)), | |
| m_equals(std::move(other.m_equals)) | |
| { | |
| static_assert((SIZE_T_BITS == 32) || (SIZE_T_BITS == 64), "SIZE_T_BITS must be 32 or 64"); | |
| other.m_hash_shift = SIZE_T_BITS; | |
| other.m_num_valid = 0; | |
| other.m_grow_threshold = 0; | |
| } | |
| hash_map& operator= (const hash_map& other) | |
| { | |
| if (this == &other) | |
| return *this; | |
| clear(); | |
| m_values = other.m_values; | |
| m_hash_shift = other.m_hash_shift; | |
| m_num_valid = other.m_num_valid; | |
| m_grow_threshold = other.m_grow_threshold; | |
| m_hasher = other.m_hasher; | |
| m_equals = other.m_equals; | |
| return *this; | |
| } | |
| hash_map& operator= (hash_map&& other) | |
| { | |
| if (this == &other) | |
| return *this; | |
| clear(); | |
| m_values = std::move(other.m_values); | |
| m_hash_shift = other.m_hash_shift; | |
| m_num_valid = other.m_num_valid; | |
| m_grow_threshold = other.m_grow_threshold; | |
| m_hasher = std::move(other.m_hasher); | |
| m_equals = std::move(other.m_equals); | |
| other.m_hash_shift = SIZE_T_BITS; | |
| other.m_num_valid = 0; | |
| other.m_grow_threshold = 0; | |
| return *this; | |
| } | |
| inline ~hash_map() | |
| { | |
| clear(); | |
| } | |
| inline const Equals& get_equals() const { return m_equals; } | |
| inline Equals& get_equals() { return m_equals; } | |
| inline void set_equals(const Equals& equals) { m_equals = equals; } | |
| inline const Hasher& get_hasher() const { return m_hasher; } | |
| inline Hasher& get_hasher() { return m_hasher; } | |
| inline void set_hasher(const Hasher& hasher) { m_hasher = hasher; } | |
| inline void clear() | |
| { | |
| if (m_values.empty()) | |
| return; | |
| if (BASISU_HAS_DESTRUCTOR(Key) || BASISU_HAS_DESTRUCTOR(Value)) | |
| { | |
| node* p = &get_node(0); | |
| node* p_end = p + m_values.size(); | |
| size_t num_remaining = m_num_valid; | |
| while (p != p_end) | |
| { | |
| if (p->state) | |
| { | |
| destruct_value_type(p); | |
| num_remaining--; | |
| if (!num_remaining) | |
| break; | |
| } | |
| p++; | |
| } | |
| } | |
| m_values.clear_no_destruction(); | |
| m_hash_shift = SIZE_T_BITS; | |
| m_num_valid = 0; | |
| m_grow_threshold = 0; | |
| } | |
| // Destroys elements/empties container but doesn't free memory. | |
| inline void reset() | |
| { | |
| if (!m_num_valid) | |
| return; | |
| if (BASISU_HAS_DESTRUCTOR(Key) || BASISU_HAS_DESTRUCTOR(Value)) | |
| { | |
| node* p = &get_node(0); | |
| node* p_end = p + m_values.size(); | |
| size_t num_remaining = m_num_valid; | |
| while (p != p_end) | |
| { | |
| if (p->state) | |
| { | |
| destruct_value_type(p); | |
| p->state = cStateInvalid; | |
| num_remaining--; | |
| if (!num_remaining) | |
| break; | |
| } | |
| p++; | |
| } | |
| } | |
| else if (sizeof(node) <= 16) | |
| { | |
| memset((void *)&m_values[0], 0, m_values.size_in_bytes()); | |
| } | |
| else | |
| { | |
| node* p = &get_node(0); | |
| node* p_end = p + m_values.size(); | |
| size_t num_remaining = m_num_valid; | |
| while (p != p_end) | |
| { | |
| if (p->state) | |
| { | |
| p->state = cStateInvalid; | |
| num_remaining--; | |
| if (!num_remaining) | |
| break; | |
| } | |
| p++; | |
| } | |
| } | |
| m_num_valid = 0; | |
| } | |
| inline size_t size() | |
| { | |
| return m_num_valid; | |
| } | |
| inline uint32_t size_u32() | |
| { | |
| return static_cast<uint32_t>(m_num_valid); | |
| } | |
| inline size_t get_table_size() | |
| { | |
| return m_values.size(); | |
| } | |
| inline bool empty() | |
| { | |
| return !m_num_valid; | |
| } | |
| inline bool reserve(size_t new_capacity) | |
| { | |
| if (!new_capacity) | |
| return true; | |
| uint64_t new_hash_size = new_capacity; | |
| new_hash_size = new_hash_size * 2ULL; | |
| if (!helpers::is_power_of_2(new_hash_size)) | |
| new_hash_size = helpers::next_pow2(new_hash_size); | |
| new_hash_size = helpers::maximum<uint64_t>(cMinHashSize, new_hash_size); | |
| if (!can_fit_into_size_t(new_hash_size)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| assert(new_hash_size >= new_capacity); | |
| if (new_hash_size <= m_values.size()) | |
| return true; | |
| return rehash((size_t)new_hash_size); | |
| } | |
| class iterator | |
| { | |
| friend class hash_map<Key, Value, Hasher, Equals>; | |
| friend class hash_map<Key, Value, Hasher, Equals>::const_iterator; | |
| public: | |
| inline iterator() : m_pTable(nullptr), m_index(0) { } | |
| inline iterator(hash_map_type& table, size_t index) : m_pTable(&table), m_index(index) { } | |
| inline iterator(const iterator& other) : m_pTable(other.m_pTable), m_index(other.m_index) { } | |
| inline iterator& operator= (const iterator& other) | |
| { | |
| m_pTable = other.m_pTable; | |
| m_index = other.m_index; | |
| return *this; | |
| } | |
| // post-increment | |
| inline iterator operator++(int) | |
| { | |
| iterator result(*this); | |
| ++*this; | |
| return result; | |
| } | |
| // pre-increment | |
| inline iterator& operator++() | |
| { | |
| probe(); | |
| return *this; | |
| } | |
| inline value_type& operator*() const { return *get_cur(); } | |
| inline value_type* operator->() const { return get_cur(); } | |
| inline bool operator == (const iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); } | |
| inline bool operator != (const iterator& b) const { return !(*this == b); } | |
| inline bool operator == (const const_iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); } | |
| inline bool operator != (const const_iterator& b) const { return !(*this == b); } | |
| private: | |
| hash_map_type* m_pTable; | |
| size_t m_index; | |
| inline value_type* get_cur() const | |
| { | |
| assert(m_pTable && (m_index < m_pTable->m_values.size())); | |
| assert(m_pTable->get_node_state(m_index) == cStateValid); | |
| return &m_pTable->get_node(m_index); | |
| } | |
| inline void probe() | |
| { | |
| assert(m_pTable); | |
| m_index = m_pTable->find_next(m_index); | |
| } | |
| }; | |
| class const_iterator | |
| { | |
| friend class hash_map<Key, Value, Hasher, Equals>; | |
| friend class hash_map<Key, Value, Hasher, Equals>::iterator; | |
| public: | |
| inline const_iterator() : m_pTable(nullptr), m_index(0) { } | |
| inline const_iterator(const hash_map_type& table, size_t index) : m_pTable(&table), m_index(index) { } | |
| inline const_iterator(const iterator& other) : m_pTable(other.m_pTable), m_index(other.m_index) { } | |
| inline const_iterator(const const_iterator& other) : m_pTable(other.m_pTable), m_index(other.m_index) { } | |
| inline const_iterator& operator= (const const_iterator& other) | |
| { | |
| m_pTable = other.m_pTable; | |
| m_index = other.m_index; | |
| return *this; | |
| } | |
| inline const_iterator& operator= (const iterator& other) | |
| { | |
| m_pTable = other.m_pTable; | |
| m_index = other.m_index; | |
| return *this; | |
| } | |
| // post-increment | |
| inline const_iterator operator++(int) | |
| { | |
| const_iterator result(*this); | |
| ++*this; | |
| return result; | |
| } | |
| // pre-increment | |
| inline const_iterator& operator++() | |
| { | |
| probe(); | |
| return *this; | |
| } | |
| inline const value_type& operator*() const { return *get_cur(); } | |
| inline const value_type* operator->() const { return get_cur(); } | |
| inline bool operator == (const const_iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); } | |
| inline bool operator != (const const_iterator& b) const { return !(*this == b); } | |
| inline bool operator == (const iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); } | |
| inline bool operator != (const iterator& b) const { return !(*this == b); } | |
| private: | |
| const hash_map_type* m_pTable; | |
| size_t m_index; | |
| inline const value_type* get_cur() const | |
| { | |
| assert(m_pTable && (m_index < m_pTable->m_values.size())); | |
| assert(m_pTable->get_node_state(m_index) == cStateValid); | |
| return &m_pTable->get_node(m_index); | |
| } | |
| inline void probe() | |
| { | |
| assert(m_pTable); | |
| m_index = m_pTable->find_next(m_index); | |
| } | |
| }; | |
| inline const_iterator begin() const | |
| { | |
| if (!m_num_valid) | |
| return end(); | |
| return const_iterator(*this, find_next(std::numeric_limits<size_t>::max())); | |
| } | |
| inline const_iterator end() const | |
| { | |
| return const_iterator(*this, m_values.size()); | |
| } | |
| inline iterator begin() | |
| { | |
| if (!m_num_valid) | |
| return end(); | |
| return iterator(*this, find_next(std::numeric_limits<size_t>::max())); | |
| } | |
| inline iterator end() | |
| { | |
| return iterator(*this, m_values.size()); | |
| } | |
| // insert_result.first will always point to inserted key/value (or the already existing key/value). | |
| // insert_result.second will be true if a new key/value was inserted, or false if the key already existed (in which case first will point to the already existing value). | |
| typedef std::pair<iterator, bool> insert_result; | |
| inline insert_result insert(const Key& k, const Value& v = Value()) | |
| { | |
| insert_result result; | |
| if (!insert_no_grow(result, k, v)) | |
| { | |
| if (!try_grow()) | |
| container_abort("hash_map::try_grow() failed"); | |
| // This must succeed. | |
| if (!insert_no_grow(result, k, v)) | |
| container_abort("hash_map::insert() failed"); | |
| } | |
| return result; | |
| } | |
| inline bool try_insert(insert_result& result, const Key& k, const Value& v = Value()) | |
| { | |
| if (!insert_no_grow(result, k, v)) | |
| { | |
| if (!try_grow()) | |
| return false; | |
| if (!insert_no_grow(result, k, v)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| inline insert_result insert(Key&& k, Value&& v = Value()) | |
| { | |
| insert_result result; | |
| if (!insert_no_grow_move(result, std::move(k), std::move(v))) | |
| { | |
| if (!try_grow()) | |
| container_abort("hash_map::try_grow() failed"); | |
| // This must succeed. | |
| if (!insert_no_grow_move(result, std::move(k), std::move(v))) | |
| container_abort("hash_map::insert() failed"); | |
| } | |
| return result; | |
| } | |
| inline bool try_insert(insert_result& result, Key&& k, Value&& v = Value()) | |
| { | |
| if (!insert_no_grow_move(result, std::move(k), std::move(v))) | |
| { | |
| if (!try_grow()) | |
| return false; | |
| if (!insert_no_grow_move(result, std::move(k), std::move(v))) | |
| return false; | |
| } | |
| return true; | |
| } | |
| inline insert_result insert(const value_type& v) | |
| { | |
| return insert(v.first, v.second); | |
| } | |
| inline bool try_insert(insert_result& result, const value_type& v) | |
| { | |
| return try_insert(result, v.first, v.second); | |
| } | |
| inline insert_result insert(value_type&& v) | |
| { | |
| return insert(std::move(v.first), std::move(v.second)); | |
| } | |
| inline bool try_insert(insert_result& result, value_type&& v) | |
| { | |
| return try_insert(result, std::move(v.first), std::move(v.second)); | |
| } | |
| inline const_iterator find(const Key& k) const | |
| { | |
| return const_iterator(*this, find_index(k)); | |
| } | |
| inline iterator find(const Key& k) | |
| { | |
| return iterator(*this, find_index(k)); | |
| } | |
| inline bool contains(const Key& k) const | |
| { | |
| const size_t idx = find_index(k); | |
| return idx != m_values.size(); | |
| } | |
| inline bool erase(const Key& k) | |
| { | |
| size_t i = find_index(k); | |
| if (i >= m_values.size()) | |
| return false; | |
| node* pDst = &get_node(i); | |
| destruct_value_type(pDst); | |
| pDst->state = cStateInvalid; | |
| m_num_valid--; | |
| for (; ; ) | |
| { | |
| size_t r, j = i; | |
| node* pSrc = pDst; | |
| do | |
| { | |
| if (!i) | |
| { | |
| i = m_values.size() - 1; | |
| pSrc = &get_node(i); | |
| } | |
| else | |
| { | |
| i--; | |
| pSrc--; | |
| } | |
| if (!pSrc->state) | |
| return true; | |
| r = hash_key(pSrc->first); | |
| } while ((i <= r && r < j) || (r < j && j < i) || (j < i && i <= r)); | |
| move_node(pDst, pSrc); | |
| pDst = pSrc; | |
| } | |
| } | |
| inline void swap(hash_map_type& other) | |
| { | |
| m_values.swap(other.m_values); | |
| std::swap(m_hash_shift, other.m_hash_shift); | |
| std::swap(m_num_valid, other.m_num_valid); | |
| std::swap(m_grow_threshold, other.m_grow_threshold); | |
| std::swap(m_hasher, other.m_hasher); | |
| std::swap(m_equals, other.m_equals); | |
| } | |
| private: | |
| struct node : public value_type | |
| { | |
| uint8_t state; | |
| }; | |
| static inline void construct_value_type(value_type* pDst, const Key& k, const Value& v) | |
| { | |
| if (BASISU_IS_BITWISE_COPYABLE(Key)) | |
| memcpy((void *)&pDst->first, &k, sizeof(Key)); | |
| else | |
| scalar_type<Key>::construct(&pDst->first, k); | |
| if (BASISU_IS_BITWISE_COPYABLE(Value)) | |
| memcpy((void *)&pDst->second, &v, sizeof(Value)); | |
| else | |
| scalar_type<Value>::construct(&pDst->second, v); | |
| } | |
| static inline void construct_value_type(value_type* pDst, const value_type* pSrc) | |
| { | |
| if ((BASISU_IS_BITWISE_COPYABLE(Key)) && (BASISU_IS_BITWISE_COPYABLE(Value))) | |
| { | |
| memcpy((void *)pDst, pSrc, sizeof(value_type)); | |
| } | |
| else | |
| { | |
| if (BASISU_IS_BITWISE_COPYABLE(Key)) | |
| memcpy((void *)&pDst->first, &pSrc->first, sizeof(Key)); | |
| else | |
| scalar_type<Key>::construct(&pDst->first, pSrc->first); | |
| if (BASISU_IS_BITWISE_COPYABLE(Value)) | |
| memcpy((void *)&pDst->second, &pSrc->second, sizeof(Value)); | |
| else | |
| scalar_type<Value>::construct(&pDst->second, pSrc->second); | |
| } | |
| } | |
| static inline void destruct_value_type(value_type* p) | |
| { | |
| scalar_type<Key>::destruct(&p->first); | |
| scalar_type<Value>::destruct(&p->second); | |
| } | |
| // Moves nodes *pSrc to *pDst efficiently from one hashmap to another. | |
| // pDst should NOT be constructed on entry. | |
| static inline void move_node(node* pDst, node* pSrc, bool update_src_state = true) | |
| { | |
| assert(!pDst->state); | |
| if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Key) && BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Value)) | |
| { | |
| memcpy((void *)pDst, pSrc, sizeof(node)); | |
| assert(pDst->state == cStateValid); | |
| } | |
| else | |
| { | |
| if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Key)) | |
| memcpy((void*)&pDst->first, &pSrc->first, sizeof(Key)); | |
| else | |
| { | |
| new ((void*)&pDst->first) Key(std::move(pSrc->first)); | |
| scalar_type<Key>::destruct(&pSrc->first); | |
| } | |
| if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Value)) | |
| memcpy((void*)&pDst->second, &pSrc->second, sizeof(Value)); | |
| else | |
| { | |
| new ((void*)&pDst->second) Value(std::move(pSrc->second)); | |
| scalar_type<Value>::destruct(&pSrc->second); | |
| } | |
| pDst->state = cStateValid; | |
| } | |
| if (update_src_state) | |
| pSrc->state = cStateInvalid; | |
| } | |
| struct raw_node | |
| { | |
| inline raw_node() | |
| { | |
| node* p = reinterpret_cast<node*>(this); | |
| p->state = cStateInvalid; | |
| } | |
| // In practice, this should never be called (right?). We manage destruction ourselves. | |
| inline ~raw_node() | |
| { | |
| node* p = reinterpret_cast<node*>(this); | |
| if (p->state) | |
| hash_map_type::destruct_value_type(p); | |
| } | |
| inline raw_node(const raw_node& other) | |
| { | |
| node* pDst = reinterpret_cast<node*>(this); | |
| const node* pSrc = reinterpret_cast<const node*>(&other); | |
| if (pSrc->state) | |
| { | |
| hash_map_type::construct_value_type(pDst, pSrc); | |
| pDst->state = cStateValid; | |
| } | |
| else | |
| pDst->state = cStateInvalid; | |
| } | |
| inline raw_node& operator= (const raw_node& rhs) | |
| { | |
| if (this == &rhs) | |
| return *this; | |
| node* pDst = reinterpret_cast<node*>(this); | |
| const node* pSrc = reinterpret_cast<const node*>(&rhs); | |
| if (pSrc->state) | |
| { | |
| if (pDst->state) | |
| { | |
| pDst->first = pSrc->first; | |
| pDst->second = pSrc->second; | |
| } | |
| else | |
| { | |
| hash_map_type::construct_value_type(pDst, pSrc); | |
| pDst->state = cStateValid; | |
| } | |
| } | |
| else if (pDst->state) | |
| { | |
| hash_map_type::destruct_value_type(pDst); | |
| pDst->state = cStateInvalid; | |
| } | |
| return *this; | |
| } | |
| uint8_t m_bits[sizeof(node)]; | |
| }; | |
| typedef basisu::vector<raw_node> node_vector; | |
| node_vector m_values; | |
| size_t m_num_valid; | |
| size_t m_grow_threshold; | |
| uint32_t m_hash_shift; | |
| Hasher m_hasher; | |
| Equals m_equals; | |
| inline size_t hash_key(const Key& k) const | |
| { | |
| assert((safe_shift_left(static_cast<uint64_t>(1), (SIZE_T_BITS - m_hash_shift))) == m_values.size()); | |
| // Fibonacci hashing | |
| if (SIZE_T_BITS == 32) | |
| { | |
| assert(m_hash_shift != 32); | |
| uint32_t hash = static_cast<uint32_t>(m_hasher(k)); | |
| hash = (2654435769U * hash) >> m_hash_shift; | |
| assert(hash < m_values.size()); | |
| return (size_t)hash; | |
| } | |
| else | |
| { | |
| assert(m_hash_shift != 64); | |
| uint64_t hash = static_cast<uint64_t>(m_hasher(k)); | |
| hash = (0x9E3779B97F4A7C15ULL * hash) >> m_hash_shift; | |
| assert(hash < m_values.size()); | |
| return (size_t)hash; | |
| } | |
| } | |
| inline const node& get_node(size_t index) const | |
| { | |
| return *reinterpret_cast<const node*>(&m_values[index]); | |
| } | |
| inline node& get_node(size_t index) | |
| { | |
| return *reinterpret_cast<node*>(&m_values[index]); | |
| } | |
| inline state get_node_state(size_t index) const | |
| { | |
| return static_cast<state>(get_node(index).state); | |
| } | |
| inline void set_node_state(size_t index, bool valid) | |
| { | |
| get_node(index).state = valid; | |
| } | |
| inline bool try_grow() | |
| { | |
| uint64_t n = m_values.size() * 2ULL; | |
| if (!helpers::is_power_of_2(n)) | |
| n = helpers::next_pow2(n); | |
| if (!can_fit_into_size_t(n)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| return rehash(helpers::maximum<size_t>(cMinHashSize, (size_t)n)); | |
| } | |
| // new_hash_size must be a power of 2. | |
| inline bool rehash(size_t new_hash_size) | |
| { | |
| if (!helpers::is_power_of_2((uint64_t)new_hash_size)) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if (new_hash_size < m_num_valid) | |
| { | |
| assert(0); | |
| return false; | |
| } | |
| if (new_hash_size == m_values.size()) | |
| return true; | |
| hash_map new_map; | |
| if (!new_map.m_values.try_resize(new_hash_size)) | |
| return false; | |
| new_map.m_hash_shift = SIZE_T_BITS - helpers::floor_log2i((uint64_t)new_hash_size); | |
| assert(new_hash_size == safe_shift_left(static_cast<uint64_t>(1), SIZE_T_BITS - new_map.m_hash_shift)); | |
| new_map.m_grow_threshold = std::numeric_limits<size_t>::max(); | |
| node* pNode = reinterpret_cast<node*>(m_values.begin()); | |
| node* pNode_end = pNode + m_values.size(); | |
| while (pNode != pNode_end) | |
| { | |
| if (pNode->state) | |
| { | |
| new_map.move_into(pNode); | |
| if (new_map.m_num_valid == m_num_valid) | |
| break; | |
| } | |
| pNode++; | |
| } | |
| new_map.m_grow_threshold = new_hash_size >> 1U; | |
| if (new_hash_size & 1) | |
| new_map.m_grow_threshold++; | |
| m_values.clear_no_destruction(); | |
| m_hash_shift = SIZE_T_BITS; | |
| swap(new_map); | |
| return true; | |
| } | |
| inline size_t find_next(size_t index) const | |
| { | |
| index++; | |
| if (index >= m_values.size()) | |
| return index; | |
| const node* pNode = &get_node(index); | |
| for (; ; ) | |
| { | |
| if (pNode->state) | |
| break; | |
| if (++index >= m_values.size()) | |
| break; | |
| pNode++; | |
| } | |
| return index; | |
| } | |
| inline size_t find_index(const Key& k) const | |
| { | |
| if (m_num_valid) | |
| { | |
| size_t index = hash_key(k); | |
| const node* pNode = &get_node(index); | |
| if (pNode->state) | |
| { | |
| if (m_equals(pNode->first, k)) | |
| return index; | |
| const size_t orig_index = index; | |
| for (; ; ) | |
| { | |
| if (!index) | |
| { | |
| index = m_values.size() - 1; | |
| pNode = &get_node(index); | |
| } | |
| else | |
| { | |
| index--; | |
| pNode--; | |
| } | |
| if (index == orig_index) | |
| break; | |
| if (!pNode->state) | |
| break; | |
| if (m_equals(pNode->first, k)) | |
| return index; | |
| } | |
| } | |
| } | |
| return m_values.size(); | |
| } | |
| inline bool insert_no_grow(insert_result& result, const Key& k, const Value& v) | |
| { | |
| if (!m_values.size()) | |
| return false; | |
| size_t index = hash_key(k); | |
| node* pNode = &get_node(index); | |
| if (pNode->state) | |
| { | |
| if (m_equals(pNode->first, k)) | |
| { | |
| result.first = iterator(*this, index); | |
| result.second = false; | |
| return true; | |
| } | |
| const size_t orig_index = index; | |
| for (; ; ) | |
| { | |
| if (!index) | |
| { | |
| index = m_values.size() - 1; | |
| pNode = &get_node(index); | |
| } | |
| else | |
| { | |
| index--; | |
| pNode--; | |
| } | |
| if (orig_index == index) | |
| return false; | |
| if (!pNode->state) | |
| break; | |
| if (m_equals(pNode->first, k)) | |
| { | |
| result.first = iterator(*this, index); | |
| result.second = false; | |
| return true; | |
| } | |
| } | |
| } | |
| if (m_num_valid >= m_grow_threshold) | |
| return false; | |
| construct_value_type(pNode, k, v); | |
| pNode->state = cStateValid; | |
| m_num_valid++; | |
| assert(m_num_valid <= m_values.size()); | |
| result.first = iterator(*this, index); | |
| result.second = true; | |
| return true; | |
| } | |
| // Move user supplied key/value into a node. | |
| static inline void move_value_type(value_type* pDst, Key&& k, Value&& v) | |
| { | |
| // Not checking for is MOVABLE because the caller could later destruct k and/or v (what state do we set them to?) | |
| if (BASISU_IS_BITWISE_COPYABLE(Key)) | |
| { | |
| memcpy((void *)&pDst->first, (const void *)&k, sizeof(Key)); | |
| } | |
| else | |
| { | |
| new ((void*)&pDst->first) Key(std::move(k)); | |
| // No destruction - user will do that (we don't own k). | |
| } | |
| if (BASISU_IS_BITWISE_COPYABLE(Value)) | |
| { | |
| memcpy((void *)&pDst->second, (const void*)&v, sizeof(Value)); | |
| } | |
| else | |
| { | |
| new ((void*)&pDst->second) Value(std::move(v)); | |
| // No destruction - user will do that (we don't own v). | |
| } | |
| } | |
| // Insert user provided k/v, by moving, into the current hash table | |
| inline bool insert_no_grow_move(insert_result& result, Key&& k, Value&& v) | |
| { | |
| if (!m_values.size()) | |
| return false; | |
| size_t index = hash_key(k); | |
| node* pNode = &get_node(index); | |
| if (pNode->state) | |
| { | |
| if (m_equals(pNode->first, k)) | |
| { | |
| result.first = iterator(*this, index); | |
| result.second = false; | |
| return true; | |
| } | |
| const size_t orig_index = index; | |
| for (; ; ) | |
| { | |
| if (!index) | |
| { | |
| index = m_values.size() - 1; | |
| pNode = &get_node(index); | |
| } | |
| else | |
| { | |
| index--; | |
| pNode--; | |
| } | |
| if (orig_index == index) | |
| return false; | |
| if (!pNode->state) | |
| break; | |
| if (m_equals(pNode->first, k)) | |
| { | |
| result.first = iterator(*this, index); | |
| result.second = false; | |
| return true; | |
| } | |
| } | |
| } | |
| if (m_num_valid >= m_grow_threshold) | |
| return false; | |
| move_value_type(pNode, std::move(k), std::move(v)); | |
| pNode->state = cStateValid; | |
| m_num_valid++; | |
| assert(m_num_valid <= m_values.size()); | |
| result.first = iterator(*this, index); | |
| result.second = true; | |
| return true; | |
| } | |
| // Insert pNode by moving into the current hash table | |
| inline void move_into(node* pNode) | |
| { | |
| size_t index = hash_key(pNode->first); | |
| node* pDst_node = &get_node(index); | |
| if (pDst_node->state) | |
| { | |
| const size_t orig_index = index; | |
| for (; ; ) | |
| { | |
| if (!index) | |
| { | |
| index = m_values.size() - 1; | |
| pDst_node = &get_node(index); | |
| } | |
| else | |
| { | |
| index--; | |
| pDst_node--; | |
| } | |
| if (index == orig_index) | |
| { | |
| assert(false); | |
| return; | |
| } | |
| if (!pDst_node->state) | |
| break; | |
| } | |
| } | |
| // No need to update the source node's state (it's going away) | |
| move_node(pDst_node, pNode, false); | |
| m_num_valid++; | |
| } | |
| }; | |
| template<typename Key, typename Value, typename Hasher, typename Equals> | |
| struct bitwise_movable< hash_map<Key, Value, Hasher, Equals> > { enum { cFlag = true }; }; | |
| #if BASISU_HASHMAP_TEST | |
| extern void hash_map_test(); | |
| #endif | |
| // String formatting | |
| inline std::string string_format(const char* pFmt, ...) | |
| { | |
| char buf[2048]; | |
| va_list args; | |
| va_start(args, pFmt); | |
| #ifdef _WIN32 | |
| vsprintf_s(buf, sizeof(buf), pFmt, args); | |
| #else | |
| vsnprintf(buf, sizeof(buf), pFmt, args); | |
| #endif | |
| va_end(args); | |
| return std::string(buf); | |
| } | |
| enum class variant_type | |
| { | |
| cInvalid, | |
| cI32, cU32, | |
| cI64, cU64, | |
| cFlt, cDbl, cBool, | |
| cStrPtr, cStdStr | |
| }; | |
| struct fmt_variant | |
| { | |
| union | |
| { | |
| int32_t m_i32; | |
| uint32_t m_u32; | |
| int64_t m_i64; | |
| uint64_t m_u64; | |
| float m_flt; | |
| double m_dbl; | |
| bool m_bool; | |
| const char* m_pStr; | |
| }; | |
| std::string m_str; | |
| variant_type m_type; | |
| inline fmt_variant() : | |
| m_u64(0), | |
| m_type(variant_type::cInvalid) | |
| { | |
| } | |
| inline fmt_variant(const fmt_variant& other) : | |
| m_u64(other.m_u64), | |
| m_str(other.m_str), | |
| m_type(other.m_type) | |
| { | |
| } | |
| inline fmt_variant(fmt_variant&& other) : | |
| m_u64(other.m_u64), | |
| m_str(std::move(other.m_str)), | |
| m_type(other.m_type) | |
| { | |
| other.m_type = variant_type::cInvalid; | |
| other.m_u64 = 0; | |
| } | |
| inline fmt_variant& operator= (fmt_variant&& other) | |
| { | |
| if (this == &other) | |
| return *this; | |
| m_type = other.m_type; | |
| m_u64 = other.m_u64; | |
| m_str = std::move(other.m_str); | |
| other.m_type = variant_type::cInvalid; | |
| other.m_u64 = 0; | |
| return *this; | |
| } | |
| inline fmt_variant& operator= (const fmt_variant& rhs) | |
| { | |
| if (this == &rhs) | |
| return *this; | |
| m_u64 = rhs.m_u64; | |
| m_type = rhs.m_type; | |
| m_str = rhs.m_str; | |
| return *this; | |
| } | |
| inline fmt_variant(int32_t v) : m_i32(v), m_type(variant_type::cI32) { } | |
| inline fmt_variant(uint32_t v) : m_u32(v), m_type(variant_type::cU32) { } | |
| inline fmt_variant(int64_t v) : m_i64(v), m_type(variant_type::cI64) { } | |
| inline fmt_variant(uint64_t v) : m_u64(v), m_type(variant_type::cU64) { } | |
| #ifdef _MSC_VER | |
| inline fmt_variant(unsigned long v) : m_u64(v), m_type(variant_type::cU64) {} | |
| inline fmt_variant(long v) : m_i64(v), m_type(variant_type::cI64) {} | |
| #endif | |
| inline fmt_variant(float v) : m_flt(v), m_type(variant_type::cFlt) { } | |
| inline fmt_variant(double v) : m_dbl(v), m_type(variant_type::cDbl) { } | |
| inline fmt_variant(const char* pStr) : m_pStr(pStr), m_type(variant_type::cStrPtr) { } | |
| inline fmt_variant(const std::string& str) : m_u64(0), m_str(str), m_type(variant_type::cStdStr) { } | |
| inline fmt_variant(bool val) : m_bool(val), m_type(variant_type::cBool) { } | |
| bool to_string(std::string& res, std::string& fmt) const; | |
| }; | |
| typedef basisu::vector<fmt_variant> fmt_variant_vec; | |
| bool fmt_variants(std::string& res, const char* pFmt, const fmt_variant_vec& variants); | |
| template <typename... Args> | |
| inline bool fmt_string(std::string& res, const char* pFmt, Args&&... args) | |
| { | |
| return fmt_variants(res, pFmt, fmt_variant_vec{ fmt_variant(std::forward<Args>(args))... }); | |
| } | |
| template <typename... Args> | |
| inline std::string fmt_string(const char* pFmt, Args&&... args) | |
| { | |
| std::string res; | |
| fmt_variants(res, pFmt, fmt_variant_vec{ fmt_variant(std::forward<Args>(args))... }); | |
| return res; | |
| } | |
| template <typename... Args> | |
| inline int fmt_printf(const char* pFmt, Args&&... args) | |
| { | |
| std::string res; | |
| if (!fmt_variants(res, pFmt, fmt_variant_vec{ fmt_variant(std::forward<Args>(args))... })) | |
| return EOF; | |
| return fputs(res.c_str(), stdout); | |
| } | |
| template <typename... Args> | |
| inline int fmt_fprintf(FILE* pFile, const char* pFmt, Args&&... args) | |
| { | |
| std::string res; | |
| if (!fmt_variants(res, pFmt, fmt_variant_vec{ fmt_variant(std::forward<Args>(args))... })) | |
| return EOF; | |
| return fputs(res.c_str(), pFile); | |
| } | |
| // fixed_array - zero initialized by default, operator[] is always bounds checked. | |
| template <std::size_t N, typename T> | |
| class fixed_array | |
| { | |
| static_assert(N >= 1, "fixed_array size must be at least 1"); | |
| public: | |
| using value_type = T; | |
| using size_type = std::size_t; | |
| using difference_type = std::ptrdiff_t; | |
| using reference = T&; | |
| using const_reference = const T&; | |
| using pointer = T*; | |
| using const_pointer = const T*; | |
| using iterator = T*; | |
| using const_iterator = const T*; | |
| T m_data[N]; | |
| BASISU_FORCE_INLINE fixed_array() | |
| { | |
| initialize_array(); | |
| } | |
| BASISU_FORCE_INLINE fixed_array(std::initializer_list<T> list) | |
| { | |
| assert(list.size() <= N); | |
| std::size_t copy_size = std::min(list.size(), N); | |
| std::copy_n(list.begin(), copy_size, m_data); // Copy up to min(list.size(), N) | |
| if (list.size() < N) | |
| { | |
| // Initialize the rest of the array | |
| std::fill(m_data + copy_size, m_data + N, T{}); | |
| } | |
| } | |
| BASISU_FORCE_INLINE T& operator[](std::size_t index) | |
| { | |
| if (index >= N) | |
| container_abort("fixed_array: Index out of bounds."); | |
| return m_data[index]; | |
| } | |
| BASISU_FORCE_INLINE const T& operator[](std::size_t index) const | |
| { | |
| if (index >= N) | |
| container_abort("fixed_array: Index out of bounds."); | |
| return m_data[index]; | |
| } | |
| BASISU_FORCE_INLINE T* begin() { return m_data; } | |
| BASISU_FORCE_INLINE const T* begin() const { return m_data; } | |
| BASISU_FORCE_INLINE T* end() { return m_data + N; } | |
| BASISU_FORCE_INLINE const T* end() const { return m_data + N; } | |
| BASISU_FORCE_INLINE const T* data() const { return m_data; } | |
| BASISU_FORCE_INLINE T* data() { return m_data; } | |
| BASISU_FORCE_INLINE const T& front() const { return m_data[0]; } | |
| BASISU_FORCE_INLINE T& front() { return m_data[0]; } | |
| BASISU_FORCE_INLINE const T& back() const { return m_data[N - 1]; } | |
| BASISU_FORCE_INLINE T& back() { return m_data[N - 1]; } | |
| BASISU_FORCE_INLINE constexpr std::size_t size() const { return N; } | |
| BASISU_FORCE_INLINE void clear() | |
| { | |
| initialize_array(); // Reinitialize the array | |
| } | |
| BASISU_FORCE_INLINE void set_all(const T& value) | |
| { | |
| std::fill(m_data, m_data + N, value); | |
| } | |
| BASISU_FORCE_INLINE readable_span<T> get_readable_span() const | |
| { | |
| return readable_span<T>(m_data, N); | |
| } | |
| BASISU_FORCE_INLINE writable_span<T> get_writable_span() | |
| { | |
| return writable_span<T>(m_data, N); | |
| } | |
| private: | |
| BASISU_FORCE_INLINE void initialize_array() | |
| { | |
| if constexpr (std::is_integral<T>::value || std::is_floating_point<T>::value) | |
| memset(m_data, 0, sizeof(m_data)); | |
| else | |
| std::fill(m_data, m_data + N, T{}); | |
| } | |
| BASISU_FORCE_INLINE T& access_element(std::size_t index) | |
| { | |
| if (index >= N) | |
| container_abort("fixed_array: Index out of bounds."); | |
| return m_data[index]; | |
| } | |
| BASISU_FORCE_INLINE const T& access_element(std::size_t index) const | |
| { | |
| if (index >= N) | |
| container_abort("fixed_array: Index out of bounds."); | |
| return m_data[index]; | |
| } | |
| }; | |
| // 2D array | |
| template<typename T> | |
| class vector2D | |
| { | |
| typedef basisu::vector<T> vec_type; | |
| uint32_t m_width, m_height; | |
| vec_type m_values; | |
| public: | |
| vector2D() : | |
| m_width(0), | |
| m_height(0) | |
| { | |
| } | |
| vector2D(uint32_t w, uint32_t h) : | |
| m_width(0), | |
| m_height(0) | |
| { | |
| resize(w, h); | |
| } | |
| vector2D(const vector2D& other) | |
| { | |
| *this = other; | |
| } | |
| vector2D(vector2D&& other) : | |
| m_width(0), | |
| m_height(0) | |
| { | |
| *this = std::move(other); | |
| } | |
| vector2D& operator= (const vector2D& other) | |
| { | |
| if (this != &other) | |
| { | |
| m_width = other.m_width; | |
| m_height = other.m_height; | |
| m_values = other.m_values; | |
| } | |
| return *this; | |
| } | |
| vector2D& operator= (vector2D&& other) | |
| { | |
| if (this != &other) | |
| { | |
| m_width = other.m_width; | |
| m_height = other.m_height; | |
| m_values = std::move(other.m_values); | |
| other.m_width = 0; | |
| other.m_height = 0; | |
| } | |
| return *this; | |
| } | |
| inline bool operator== (const vector2D& rhs) const | |
| { | |
| return (m_width == rhs.m_width) && (m_height == rhs.m_height) && (m_values == rhs.m_values); | |
| } | |
| inline size_t size_in_bytes() const { return m_values.size_in_bytes(); } | |
| inline uint32_t get_width() const { return m_width; } | |
| inline uint32_t get_height() const { return m_height; } | |
| inline uint32_t get_cols() const { return m_width; } | |
| inline uint32_t get_rows() const { return m_height; } | |
| inline const T& operator() (uint32_t x, uint32_t y) const { assert(x < m_width && y < m_height); return m_values[x + y * m_width]; } | |
| inline T& operator() (uint32_t x, uint32_t y) { assert(x < m_width && y < m_height); return m_values[x + y * m_width]; } | |
| inline size_t size() const { return m_values.size(); } | |
| inline const T& operator[] (uint32_t i) const { return m_values[i]; } | |
| inline T& operator[] (uint32_t i) { return m_values[i]; } | |
| inline const T& at(int x, int y) const { return (*this)((uint32_t)x, (uint32_t)y); } | |
| inline T& at(int x, int y) { return (*this)((uint32_t)x, (uint32_t)y); } | |
| inline const T& get_clamped(int x, int y) const { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); } | |
| inline T& get_clamped(int x, int y) { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); } | |
| inline const T& at_clamped(int x, int y) const { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); } | |
| inline T& at_clamped(int x, int y) { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); } | |
| inline const T& at_row_col(int y, int x) const { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); } | |
| inline T& at_row_col(int y, int x) { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); } | |
| inline void set_clipped(int x, int y, const T& val) | |
| { | |
| if ( ((uint32_t)x >= m_width) || ((uint32_t)y >= m_height) ) | |
| return; | |
| m_values[x + y * m_width] = val; | |
| } | |
| void clear() | |
| { | |
| m_width = 0; | |
| m_height = 0; | |
| m_values.clear(); | |
| } | |
| void set_all(const T& val) | |
| { | |
| vector_set_all(m_values, val); | |
| } | |
| inline const T* get_ptr() const { return m_values.data(); } | |
| inline T* get_ptr() { return m_values.data(); } | |
| vector2D& resize(uint32_t new_width, uint32_t new_height) | |
| { | |
| if ((m_width == new_width) && (m_height == new_height)) | |
| return *this; | |
| const uint64_t total_vals = (uint64_t)new_width * new_height; | |
| if (!can_fit_into_size_t(total_vals)) | |
| { | |
| // What can we do? | |
| assert(0); | |
| return *this; | |
| } | |
| vec_type oldVals((size_t)total_vals); | |
| oldVals.swap(m_values); | |
| const uint32_t w = minimum(m_width, new_width); | |
| const uint32_t h = minimum(m_height, new_height); | |
| if ((w) && (h)) | |
| { | |
| for (uint32_t y = 0; y < h; y++) | |
| for (uint32_t x = 0; x < w; x++) | |
| m_values[x + y * new_width] = oldVals[x + y * m_width]; | |
| } | |
| m_width = new_width; | |
| m_height = new_height; | |
| return *this; | |
| } | |
| bool try_resize(uint32_t new_width, uint32_t new_height) | |
| { | |
| if ((m_width == new_width) && (m_height == new_height)) | |
| return true; | |
| const uint64_t total_vals = (uint64_t)new_width * new_height; | |
| if (!can_fit_into_size_t(total_vals)) | |
| { | |
| // What can we do? | |
| assert(0); | |
| return false; | |
| } | |
| vec_type oldVals; | |
| if (!oldVals.try_resize((size_t)total_vals)) | |
| return false; | |
| oldVals.swap(m_values); | |
| const uint32_t w = minimum(m_width, new_width); | |
| const uint32_t h = minimum(m_height, new_height); | |
| if ((w) && (h)) | |
| { | |
| for (uint32_t y = 0; y < h; y++) | |
| for (uint32_t x = 0; x < w; x++) | |
| m_values[x + y * new_width] = oldVals[x + y * m_width]; | |
| } | |
| m_width = new_width; | |
| m_height = new_height; | |
| return true; | |
| } | |
| vector2D& resize_rows_cols(uint32_t rows, uint32_t cols) | |
| { | |
| return resize(cols, rows); | |
| } | |
| bool try_resize_rows_cols(uint32_t rows, uint32_t cols) | |
| { | |
| return try_resize(cols, rows); | |
| } | |
| const vector2D& extract_block_clamped(T* pDst, uint32_t src_x, uint32_t src_y, uint32_t w, uint32_t h) const | |
| { | |
| if (((src_x + w) > m_width) || ((src_y + h) > m_height)) | |
| { | |
| // Slower clamping case | |
| for (uint32_t y = 0; y < h; y++) | |
| for (uint32_t x = 0; x < w; x++) | |
| *pDst++ = at_clamped(src_x + x, src_y + y); | |
| } | |
| else | |
| { | |
| const T* pSrc = &m_values[src_x + src_y * m_width]; | |
| for (uint32_t y = 0; y < h; y++) | |
| { | |
| memcpy(pDst, pSrc, w * sizeof(T)); | |
| pSrc += m_width; | |
| pDst += w; | |
| } | |
| } | |
| return *this; | |
| } | |
| const vector2D& extract_block_clamped(T* pDst, uint32_t src_x, uint32_t src_y, uint32_t w, uint32_t h, uint32_t override_height) const | |
| { | |
| assert(override_height && (override_height <= m_height)); | |
| if (((src_x + w) > m_width) || ((src_y + h) > minimum(m_height, override_height))) | |
| { | |
| // Slower clamping case | |
| for (uint32_t y = 0; y < h; y++) | |
| for (uint32_t x = 0; x < w; x++) | |
| *pDst++ = at_clamped(src_x + x, minimum(src_y + y, override_height - 1)); | |
| } | |
| else | |
| { | |
| const T* pSrc = &m_values[src_x + src_y * m_width]; | |
| for (uint32_t y = 0; y < h; y++) | |
| { | |
| memcpy(pDst, pSrc, w * sizeof(T)); | |
| pSrc += m_width; | |
| pDst += w; | |
| } | |
| } | |
| return *this; | |
| } | |
| vector2D& swap(vector2D& other) | |
| { | |
| std::swap(m_width, other.m_width); | |
| std::swap(m_height, other.m_height); | |
| m_values.swap(other.m_values); | |
| return *this; | |
| } | |
| }; | |
| // Explictly primitive container intended for POD's, simple usage. | |
| // push_back() and resize() will refuse to push anymore and just return when full. | |
| template<typename T, uint32_t N> | |
| class static_vector | |
| { | |
| T m_data[N]; | |
| uint32_t m_size; | |
| public: | |
| static_vector() : m_size(0) { } | |
| inline void reserve(size_t reserve_size) | |
| { | |
| (void)(reserve_size); | |
| assert(reserve_size <= N); | |
| } | |
| inline void push_back(const T& value) | |
| { | |
| // Should never happen. | |
| if (m_size >= N) | |
| { | |
| assert(0); | |
| fprintf(stderr, "basisu::static_vector overflow!\n"); | |
| return; | |
| } | |
| m_data[m_size++] = value; | |
| } | |
| inline std::size_t size() const { return m_size; } | |
| inline uint32_t size_u32() const { return m_size; } | |
| inline constexpr std::size_t capacity() const { return N; } | |
| inline bool empty() const { return !m_size; } | |
| inline T& operator[](std::size_t i) { return m_data[i]; } | |
| inline const T& operator[](std::size_t i) const { return m_data[i]; } | |
| inline void resize(size_t new_size) | |
| { | |
| if (new_size > N) | |
| { | |
| assert(0); | |
| fprintf(stderr, "basisu::static_vector overflow!\n"); | |
| return; | |
| } | |
| m_size = (uint32_t)new_size; | |
| } | |
| }; | |
| } // namespace basisu | |
| namespace std | |
| { | |
| template<typename T> | |
| inline void swap(basisu::vector<T>& a, basisu::vector<T>& b) | |
| { | |
| a.swap(b); | |
| } | |
| template<typename Key, typename Value, typename Hasher, typename Equals> | |
| inline void swap(basisu::hash_map<Key, Value, Hasher, Equals>& a, basisu::hash_map<Key, Value, Hasher, Equals>& b) | |
| { | |
| a.swap(b); | |
| } | |
| } // namespace std |