diff --git a/include/cxconfig.h b/include/cxconfig.h index 7547f1a..ca8822c 100644 --- a/include/cxconfig.h +++ b/include/cxconfig.h @@ -34,9 +34,7 @@ # define CX_INL inline # define CX_NDISC [[nodiscard]] -/** - * namespace for exposed structs and functions - */ +// namespace for exposed structs and functions namespace cxstructs {} @@ -57,4 +55,4 @@ typedef int uint_16_cx; typedef int int_32_cx; # endif -#endif //CXSTRUCTS_SRC_CONFIG_H_ +#endif //CXSTRUCTS_SRC_CONFIG_H_ \ No newline at end of file diff --git a/include/cxstructs/StackHashMap.h b/include/cxstructs/StackHashMap.h index 668d270..8ba4287 100644 --- a/include/cxstructs/StackHashMap.h +++ b/include/cxstructs/StackHashMap.h @@ -494,14 +494,14 @@ class StackHashMap { CX_ASSERT(myMap2["hey"] == 100, ""); - cxstructs::StackHashMap compMap; - compMap.insert("hello", 5); - compMap.insert("bye", 10); - compMap.insert("hihi", 15); - - printf("%d\n", compMap["hello"]); - printf("%d\n", compMap["bye"]); - printf("%d\n", compMap["hihi"]); + //cxstructs::StackHashMap compMap; + //compMap.insert("hello", 5); + //compMap.insert("bye", 10); + //compMap.insert("hihi", 15); + + //printf("%d\n", compMap["hello"]); + //printf("%d\n", compMap["bye"]); + // printf("%d\n", compMap["hihi"]); } # endif }; diff --git a/include/cxstructs/StackVector.h b/include/cxstructs/StackVector.h index 47d5251..bb8fcd2 100644 --- a/include/cxstructs/StackVector.h +++ b/include/cxstructs/StackVector.h @@ -31,79 +31,58 @@ //Basically you trade 8 bytes of additional memory (the size type will always be padded to 8) for clean syntax and a nice interface // SUPPORTS non-trivial types! +// SUPPORTS Complex constructors! namespace cxstructs { template class StackVector { - - T data_[N]; // Stack-allocated array - size_type size_ = 0; // Current number of elements in the array + alignas(T) std::byte data_[sizeof(T) * N]; // Stack-allocated array + size_type size_ = 0; // Current number of elements in the array public: StackVector() : size_(0) {} - explicit StackVector(size_type elems) : size_(elems) {} + explicit StackVector(size_type count, const T& value = T()) : size_(count) { + CX_ASSERT(count <= N, "Initial size exceeds maximum capacity"); + for (size_type i = 0; i < count; ++i) { + new (reinterpret_cast(data_) + i) T(value); + } + } StackVector(const StackVector& other) : size_(other.size_) { - if constexpr (std::is_trivially_copyable_v) { - std::memcpy(data_, other.data_, other.size_ * sizeof(T)); - } else { - for (size_type i = 0; i < other.size_; ++i) { - new (data_ + i) T(other.data_[i]); // Copy construct T - } + for (size_type i = 0; i < other.size_; ++i) { + new (reinterpret_cast(data_) + i) T(*(reinterpret_cast(other.data_) + i)); } } + // Copy assignment operator StackVector& operator=(const StackVector& other) { if (this != &other) { - if constexpr (std::is_trivially_copyable_v && std::is_trivially_destructible_v) { - std::memcpy(data_, other.data_, other.size_ * sizeof(T)); - size_ = other.size_; - } else { - // Properly handle existing elements - if (other.size_ < size_) { - // Destroy extra elements - for (size_type i = other.size_; i < size_; ++i) { - data_[i].~T(); - } - } - for (size_type i = 0; i < other.size_ && i < size_; ++i) { - data_[i] = other.data_[i]; // Copy assignment of T - } - for (size_type i = size_; i < other.size_; ++i) { - new (data_ + i) T(other.data_[i]); // Copy construct T - } - size_ = other.size_; + clear(); + for (size_type i = 0; i < other.size_; ++i) { + new (reinterpret_cast(data_) + i) T(*(reinterpret_cast(other.data_) + i)); } + size_ = other.size_; } return *this; } - StackVector(StackVector&& other) noexcept(std::is_nothrow_move_constructible_v) - : size_(other.size_) { - if constexpr (std::is_trivially_copyable_v) { - std::memcpy(data_, other.data_, other.size_ * sizeof(T)); - } else { - for (size_type i = 0; i < other.size_; ++i) { - new (data_ + i) T(std::move(other.data_[i])); - other.data_[i].~T(); - } + // Move constructor + StackVector(StackVector&& other) noexcept(std::is_nothrow_move_constructible_v) : size_(other.size_) { + for (size_type i = 0; i < other.size_; ++i) { + new (reinterpret_cast(data_) + i) T(std::move(*(reinterpret_cast(other.data_) + i))); } other.size_ = 0; } + // Move assignment operator StackVector& operator=(StackVector&& other) noexcept(std::is_nothrow_move_assignable_v) { if (this != &other) { clear(); - size_ = other.size_; - if constexpr (std::is_trivially_copyable_v) { - std::memcpy(data_, other.data_, other.size_ * sizeof(T)); - } else { - for (size_type i = 0; i < other.size_; ++i) { - new (data_ + i) T(std::move(other.data_[i])); - other.data_[i].~T(); - } + for (size_type i = 0; i < other.size_; ++i) { + new (reinterpret_cast(data_) + i) T(std::move(*(reinterpret_cast(other.data_) + i))); } + size_ = other.size_; other.size_ = 0; } return *this; @@ -115,23 +94,15 @@ class StackVector { void push_back(const T& value) { CX_ASSERT(size_ < N, "Attempt to add to a full StackArray"); CX_STACK_ABORT_IMPL(); - if constexpr (std::is_trivially_copyable_v) { - data_[size_++] = value; - } else { - new (data_ + size_) T(value); - ++size_; - } + new (reinterpret_cast(data_) + size_) T(value); + ++size_; } void push_back(T&& value) { CX_ASSERT(size_ < N, "Attempt to add to a full StackArray"); CX_STACK_ABORT_IMPL(); - if constexpr (std::is_trivially_copyable_v) { - data_[size_++] = std::move(value); - } else { - new (data_ + size_) T(std::move(value)); - ++size_; - } + new (reinterpret_cast(data_) + size_) T(std::move(value)); + ++size_; } // Call cannot fail | Overwrites first elements and resets size on loop @@ -139,10 +110,10 @@ class StackVector { size_type index = size_ % N; if constexpr (!std::is_trivially_copyable_v) { if (size_ >= N) { - data_[index].~T(); + reinterpret_cast(data_ + index * sizeof(T))->~T(); } } - new (data_ + index) T(value); + new (reinterpret_cast(data_) + size_) T(value); size_ = (size_ + 1) % N; } @@ -150,18 +121,18 @@ class StackVector { void emplace_back(Args&&... args) { CX_ASSERT(size_ < N, "Attempt to add to a full StackArray"); CX_STACK_ABORT_IMPL(); - new (data_ + size_) T(std::forward(args)...); + new (reinterpret_cast(data_) + size_) T(std::forward(args)...); ++size_; } auto operator[](size_type index) -> T& { CX_ASSERT(index < size_, "Index out of range"); - return data_[index]; + return *reinterpret_cast(data_ + index * sizeof(T)); } auto operator[](size_type index) const -> const T& { CX_ASSERT(index < size_, "Index out of range"); - return data_[index]; + return *reinterpret_cast(data_ + index * sizeof(T)); } [[nodiscard]] auto size() const -> size_type { return size_; } @@ -180,83 +151,87 @@ class StackVector { } void clear() { - if constexpr (!std::is_trivially_destructible_v) { - for (size_type i = 0; i < size_; ++i) { - data_[i].~T(); // Call destructor for each constructed element - } + for (size_type i = 0; i < size_; ++i) { + reinterpret_cast(data_ + i * sizeof(T))->~T(); } size_ = 0; } - void resize(size_type size) { size_ = size; } + void resize(size_type new_size, const T& value = T()) { + if (new_size > size_) { + CX_ASSERT(new_size <= N, "Resize size exceeds maximum capacity"); + for (size_type i = size_; i < new_size; ++i) { + new (reinterpret_cast(data_) + i) T(value); + } + } else { + for (size_type i = new_size; i < size_; ++i) { + reinterpret_cast(data_ + i * sizeof(T))->~T(); + } + } + size_ = new_size; + } auto front() -> T& { if (size_ > 0) { - return data_[0]; + return *reinterpret_cast(data_); } std::abort(); } - auto back() -> T& { + auto back() noexcept -> T& { if (size_ > 0) { - return data_[size_ - 1]; + return *reinterpret_cast(data_ + size_ * sizeof(T)); } std::abort(); } auto front() const -> T { if (size_ > 0) { - return data_[0]; + return *reinterpret_cast(data_); } std::abort(); } auto back() const -> T { if (size_ > 0) { - return data_[size_ - 1]; + return *reinterpret_cast(data_ + size_ * sizeof(T)); } std::abort(); } - auto data() -> T* { return data_; } + auto data() -> T* { return reinterpret_cast(data_); } - auto cdata() -> const T* { return data_; } + auto cdata() -> const T* { return reinterpret_cast(data_); } template class IteratorTemplate { public: using iterator_category = std::forward_iterator_tag; - using value_type = std::remove_const_t; // Remove const for value_type + using value_type = std::remove_const_t; using difference_type = std::ptrdiff_t; using pointer = PtrType*; using reference = PtrType&; explicit IteratorTemplate(pointer ptr) : ptr_(ptr) {} - auto operator*() const -> reference { return *ptr_; } - - auto operator->() const -> pointer { return ptr_; } + reference operator*() const { return *ptr_; } + pointer operator->() const { return ptr_; } - // Prefix increment - auto operator++() -> IteratorTemplate& { - ptr_++; + IteratorTemplate& operator++() { + ptr_ = reinterpret_cast( + reinterpret_cast, const std::byte*, std::byte*>>(ptr_) + + sizeof(T)); return *this; } - // Postfix increment - auto operator++(int) -> IteratorTemplate { + IteratorTemplate operator++(int) { IteratorTemplate tmp = *this; ++(*this); return tmp; } - friend auto operator==(const IteratorTemplate& a, const IteratorTemplate& b) -> bool { - return a.ptr_ == b.ptr_; - } - - friend auto operator!=(const IteratorTemplate& a, const IteratorTemplate& b) -> bool { - return a.ptr_ != b.ptr_; - } + friend bool operator==(const IteratorTemplate& a, const IteratorTemplate& b) { return a.ptr_ == b.ptr_; } + friend bool operator!=(const IteratorTemplate& a, const IteratorTemplate& b) { return a.ptr_ != b.ptr_; } private: pointer ptr_; @@ -277,29 +252,92 @@ class StackVector { --size_; - return Iterator(data_ + index); + return Iterator(reinterpret_cast(data_ + index * sizeof(T))); } - auto begin() const -> ConstIterator { return ConstIterator(&data_[0]); } + using Iterator = IteratorTemplate; + using ConstIterator = IteratorTemplate; + + Iterator begin() { return Iterator(reinterpret_cast(data_)); } - auto end() const -> ConstIterator { return ConstIterator(&data_[size_]); } + Iterator end() { return Iterator(reinterpret_cast(data_ + size_ * sizeof(T))); } - auto begin() -> Iterator { return Iterator(&data_[0]); } + ConstIterator begin() const { return ConstIterator(reinterpret_cast(data_)); } - auto end() -> Iterator { return Iterator(&data_[size_]); } + ConstIterator end() const { return ConstIterator(reinterpret_cast(data_ + size_ * sizeof(T))); } +}; +} // namespace cxstructs # ifdef CX_INCLUDE_TESTS - static void TEST() { - StackVector arr; - for (int i = 0; i < 10; i++) { - arr.push_back(100); - } - CX_ASSERT(arr.size() == 10, "Size has to be 10"); +# include +# include +namespace cxtests { +struct ComplexType { + std::string name; + int value; + + ComplexType(std::string n, int v) : name(std::move(n)), value(v) { + std::cout << "ComplexType created: " << name << std::endl; + } - arr.clear(); - CX_ASSERT(arr.size() == 0, "Size has to be 0"); + ComplexType(const ComplexType& other) : name(other.name), value(other.value) { + std::cout << "ComplexType copied: " << name << std::endl; } -# endif + + ~ComplexType() { std::cout << "ComplexType destroyed: " << name << std::endl; } }; -} // namespace cxstructs + +// Test functions +static void testBasicOperations() { + cxstructs::StackVector intVector; + for (int i = 0; i < 10; i++) { + intVector.push_back(i * 10); + } + CX_ASSERT(intVector.size() == 10, "Size should be 10 after adding 10 elements"); + + intVector.clear(); + CX_ASSERT(intVector.size() == 0, "Size should be 0 after clear"); +} + +static void testComplexTypeOperations() { + cxstructs::StackVector complexVector; + complexVector.push_back(ComplexType("Item1", 100)); + complexVector.push_back(ComplexType("Item2", 200)); + + CX_ASSERT(complexVector.size() == 2, "Size should be 2 after adding 2 complex elements"); + + // Checking destructor calls on clear by listening to console output + complexVector.clear(); + CX_ASSERT(complexVector.size() == 0, "Size should be 0 after clear"); +} + +static void testCopyConstructor() { + cxstructs::StackVector original; + for (int i = 0; i < 5; i++) { + original.push_back(i); + } + + cxstructs::StackVector copied = original; + CX_ASSERT(copied.size() == 5, "Copied vector should have size 5"); + for (int i = 0; i < 5; i++) { + CX_ASSERT(copied[i] == i, "Copied vector elements should match original"); + } +} + +static void TEST_STACK_VECTOR() { + cxstructs::StackVector arr; + for (int i = 0; i < 10; i++) { + arr.push_back(100); + } + CX_ASSERT(arr.size() == 10, "Size has to be 10"); + + arr.clear(); + CX_ASSERT(arr.size() == 0, "Size has to be 0"); + + testBasicOperations(); + testComplexTypeOperations(); + testCopyConstructor(); +} +} // namespace cxtests +# endif #endif //CXSTRUCTS_SRC_CXSTRUCTS_STACKARRAY_H_ \ No newline at end of file diff --git a/include/cxutil/cxmath.h b/include/cxutil/cxmath.h index 59fb296..9570825 100644 --- a/include/cxutil/cxmath.h +++ b/include/cxutil/cxmath.h @@ -57,12 +57,7 @@ inline auto d_tanh(float x) noexcept -> float { return 1 - t * t; } -//-----------UTILS-----------// -/** - * Finds the next closest power of two to the right of the given number - * @param n the start number - * @return the next power of two or n, if n is a power of 2 - */ +// Finds the closest power of two to the right of the given number inline auto next_power_of_2(uint32_t n) noexcept -> uint32_t { n--; n |= n >> 1; @@ -73,11 +68,7 @@ inline auto next_power_of_2(uint32_t n) noexcept -> uint32_t { n++; return n; } -/** - * Fast inverse square root from quake (inversed) - * @param n - * @return the square root of n - */ +// Fast inverse square root from quake (inversed) inline auto fast_sqrt(float n) noexcept -> float { long i; float x2, y; @@ -91,14 +82,7 @@ inline auto fast_sqrt(float n) noexcept -> float { y = y * (threehalfs - (x2 * y * y)); return 1.0F / y; } -/** - * Clamps the given value between the range - * @tparam T - * @param val value to check - * @param low lowest possible value - * @param high highest possible value - * @return low if val is smaller than low, val if val is between low and high, and high if val is bigger than high - */ +// Clamps the given value between the ]range - low[ if val is smaller than low, val if val is between low and high, and high if val is bigger than high template constexpr auto clamp(const T& val, const T& low, const T& high) -> T requires(!std::is_integral_v) @@ -112,7 +96,7 @@ constexpr auto clamp(const T& val, const T& low, const T& high) -> T return val; } - +// Clamps the given value between the ]range - low[ if val is smaller than low, val if val is between low and high, and high if val is bigger than high template constexpr auto clamp(T val, T low, T high) -> T requires std::is_integral_v @@ -125,15 +109,16 @@ constexpr auto clamp(T val, T low, T high) -> T } return val; } - +// Linear interpolation - translates the given old value in the old scaling to the corresponding point in the new scale template -T lerp(T value, T minOld, T maxOld, U minNew, U maxNew) { - return (static_cast(value - minOld) / - static_cast(maxOld - minOld)) * - (maxNew - minNew) + - minNew; +T lerp(T valueOld, T minOld, T maxOld, U minNew, U maxNew) { + return (static_cast(valueOld - minOld) / static_cast(maxOld - minOld)) * (maxNew - minNew) + minNew; +} +// True if the given "val" is within the specified range +template +bool in_range(T val, T min, T max) { + return val > min && val < max; } - //-----------DISTANCE-----------// inline auto euclidean(float p1x, float p1y, float p2x, float p2y) noexcept -> float { diff --git a/include/cxutil/cxstring.h b/include/cxutil/cxstring.h index c37d0ce..5c652f0 100644 --- a/include/cxutil/cxstring.h +++ b/include/cxutil/cxstring.h @@ -78,6 +78,90 @@ inline bool str_cmp(const char* arg, const char* arg2) { } return *arg == *arg2; // Both should be '\0' if strings are truly equal } +// Parses the given string into a int on best effort basis +inline int str_parse_int(const char* str, const int radix = 10) { + if (str == nullptr || *str == '\0') return 0; + + int result = 0; + bool negative = false; + if (*str == '-') { + negative = true; + ++str; + } + + while (*str) { + char digit = *str; + int value; + if (digit >= '0' && digit <= '9') value = digit - '0'; + else if (digit >= 'a' && digit <= 'z') value = 10 + digit - 'a'; + else if (digit >= 'A' && digit <= 'Z') value = 10 + digit - 'A'; + else break; + + if (value >= radix) break; + + result = result * radix + value; + ++str; + } + + return negative ? -result : result; +} +// Parses the given string into a int64 on best effort basis +inline int64_t str_parse_long(const char* str, const int radix = 10) { + if (str == nullptr || *str == '\0') return 0; + + int64_t result = 0; + bool negative = false; + if (*str == '-') { + negative = true; + ++str; + } + + while (*str) { + char digit = *str; + int64_t value; + if (digit >= '0' && digit <= '9') value = digit - '0'; + else if (digit >= 'a' && digit <= 'z') value = 10 + digit - 'a'; + else if (digit >= 'A' && digit <= 'Z') value = 10 + digit - 'A'; + else break; + + if (value >= radix) break; + + result = result * radix + value; + ++str; + } + + return negative ? -result : result; +} +inline float str_parse_float(const char* str) { + if (str == nullptr || *str == '\0') return 0.0; + + double result = 0.0; + bool negative = false; + if (*str == '-') { + negative = true; + ++str; + } + + // Parse the integer part + while (*str && *str != '.') { + if (*str < '0' || *str > '9') break; + result = result * 10 + (*str - '0'); + ++str; + } + + // Parse the fractional part + if (*str == '.') { + ++str; + double factor = 0.1; + while (*str && *str >= '0' && *str <= '9') { + result += (*str - '0') * factor; + factor *= 0.1; + ++str; + } + } + + return negative ? -result : result; +} // string hash function constexpr auto fnv1a_32(char const* s) noexcept -> uint32_t { uint32_t hash = 2166136261U;