diff --git a/packages/audio/tests/common/AudioUtilsTest.cpp b/packages/audio/tests/common/AudioUtilsTest.cpp new file mode 100644 index 00000000..3fcaa9eb --- /dev/null +++ b/packages/audio/tests/common/AudioUtilsTest.cpp @@ -0,0 +1,16 @@ +#include "testing/Test.h" +#include "audio/AudioUtils.h" + +#include + + +TEST(AudioUtils, Basic) { + + l::audio::PCBeep(800, 25); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + return 0; +} + + diff --git a/packages/memory/include/memory/Containers.h b/packages/memory/include/memory/Containers.h index 0079e4f7..9c16cd3a 100644 --- a/packages/memory/include/memory/Containers.h +++ b/packages/memory/include/memory/Containers.h @@ -24,335 +24,4 @@ namespace l::container { return dst; }; - template - class sorted_vector { - public: - - //-------------------------------------------------------------------// - // SetSorted() // - //-------------------------------------------------------------------// - // I didn't want to override every constructor to set this - // member variable, so this function is publicly accessible. - // You should call SetSorted( true ) right after construction. - // TO DO: if you feel like it... derive all constructors to avoid - // the need for this. There are 4 last time I checked. - //-------------------------------------------------------------------// - void SetSorted(bool bSorted = true) { m_bSorted = bSorted; } - - //-------------------------------------------------------------------// - // sort() // - //-------------------------------------------------------------------// - // This function sorts the data as needed. Call it after repeated calls to - // push_unsorted(), or just let other members call it for you on next access. - // It calls std::sort(), which defaults to using operator<() for - // comparisons. - //-------------------------------------------------------------------// - void sort() { - if (!m_bSorted) - { - std::sort(vec.begin(), vec.end()); - SetSorted(); - } - } - - typename std::vector::iterator lower_bound_it(const T& key) { - if (!m_bSorted) - sort(); - - typename std::vector::iterator it = std::lower_bound(vec.begin(), vec.end(), key); - return it; - } - - /*const*/ T* lower_bound_ptr(const T& key) { - typename std::vector::iterator it = lower_bound_it(key); - - if (it == vec.end()) - return 0; - - /*const*/ T* t = &(*it); - return t; - } - - - //-------------------------------------------------------------------// - // find_it_or_fail() // - //-------------------------------------------------------------------// - // This function takes the given object and determines if there is - // a match in the vector. It returns an iterator to the actual - // object in the vector, if found. Otherwise returns std::vector::end(). - // - // This is the function you want to use most of the time - // (or the predicate version if you are using object pointers). - // - // USAGE: it makes most sense to use this function if you have - // an object with a key, other member variables, and operator<() - // that uses the key to test for equality. You then set up a dummy - // "search" object with the key set to the search value, call the - // function, and use the result to extract the additional information - // from the object. - //-------------------------------------------------------------------// - typename std::vector::iterator find_it_or_fail(const T& key) { - typename std::vector::iterator it = lower_bound_it(key); - - if (it != vec.end()) - - // lower_bound() does not necessarily indicate a successful search. - // The iterator points to the object where an insertion - // should take place. We check that result to see if we actually - // had an exact match. - - // NOTE: This is how the STL determines equality using only operator()<. - // Two comparisons, ugg, but it is a nice little trick. - if (!((*it) < key) && !(key < (*it))) - - return it; - - return vec.end(); - } - - //-------------------------------------------------------------------// - // find_ptr_or_fail() // - //-------------------------------------------------------------------// - // A variation of find_it_or_fail() that provides a pointer to result. - //-------------------------------------------------------------------// - T* find_ptr_or_fail(const T& key) { - typename std::vector::iterator it = find_it_or_fail(key); - if (it != vec.end()) - return &(*it); - - return 0; - } - - //-------------------------------------------------------------------// - // push_sorted() // - //-------------------------------------------------------------------// - // This is used to insert into a vector that always remains sorted. - // Because we have a sorted vector, finding the insertion location - // with std::lower_bound() is relatively cheap. - // - // If you have multiple insertions, consider - // using push_unsorted() for each, then calling sort(). - //-------------------------------------------------------------------// - void push_sorted(const T& t) { - if (!m_bSorted) - { - sort(); - } - - // Insert at "lower_bound" (the proper sorted location). - vec.insert(std::lower_bound(vec.begin(), vec.end(), t), t); - } - - //-------------------------------------------------------------------// - // push_unsorted() // - //-------------------------------------------------------------------// - // This is similar to push_back(), but in addition, it sets the - // unsorted flag. - //-------------------------------------------------------------------// - void push_unsorted(const T& t) { - SetSorted(false); - vec.push_back(t); - } - - //-------------------------------------------------------------------// - // operator=() // - //-------------------------------------------------------------------// - // This allows us to set the sorted_vector from a std::vector. - //-------------------------------------------------------------------// - sorted_vector& operator=(std::vector& v) { - typename std::vector::iterator it; - for (it = v.begin(); it != v.end(); ++it) - push_unsorted((*it)); - return this; - } - - // CALLS WHERE YOU PROVIDE THE FUNCTOR OR FUNCTION POINTER - // If you need to use a predicate sort function, ALWAYS use these methods - // instead of the non-functor versions. - // NOTE: UPDATE THIS when C++0x polymorphic function wrappers are available. - template - inline void sort(_Pr pr) { - if (!m_bSorted) - { - std::sort(vec.begin(), vec.end(), pr); - SetSorted(); - } - } - template - inline typename std::vector::iterator lower_bound_it(const T& key, _Pr pr) { - if (!m_bSorted) - { - std::sort(vec.begin(), vec.end(), pr); - SetSorted(); - } - typename std::vector::iterator it = std::lower_bound(vec.begin(), vec.end(), key, pr); - return it; - } - - template - inline /*const*/ T* lower_bound_ptr(const T& key, _Pr pr) { - typename std::vector::iterator it = lower_bound_it(key, pr); - - if (it == vec.end()) - return 0; - - /*const*/ T* t = &(*it); - return t; - } - - template - inline void push_sorted(const T& t, _Pr pr) { - if (!m_bSorted) - { - std::sort(vec.begin(), vec.end(), pr); - SetSorted(); - } - - // Insert at "lower_bound" (the proper sorted location). - vec.insert(std::lower_bound(vec.begin(), vec.end(), t, pr), t); - } - - template - inline typename std::vector::iterator find_it_or_fail(const T& key, _Pr pr) { - typename std::vector::iterator it = lower_bound_it(key, pr); - - if (it != vec.end()) - - // NOTE: We have to apply this using the predicate function, be careful... - if (!(pr((*it), key)) && !(pr(key, (*it)))) - - return it; - - return vec.end(); - } - - template - inline T* find_ptr_or_fail(const T& key, _Pr pr) { - typename std::vector::iterator it = find_it_or_fail(key, pr); - if (it != vec.end()) - return &(*it); - - return 0; - } - - inline typename std::vector::iterator begin() noexcept { - return vec.begin(); - } - - inline typename std::vector::iterator end() noexcept { - return vec.end(); - } - - inline size_t size() const noexcept { - return vec.size(); - } - - inline bool empty() const noexcept { - return vec.empty(); - } - - protected: - std::vector vec; - bool m_bSorted; - }; - - template - class vector_sorted_ptr { - public: - vector_sorted_ptr(size_t reserve) { - mContainer.reserve(reserve); - } - - virtual ~vector_sorted_ptr() = default; - - void insert(T* ptr) { - - } - - void remove(T* ptr) { - - } - protected: - std::vector mContainer; - }; - - template - class vector { - public: - static const size_t DataSize = MaxElementSize - 2; - - vector(size_t reserve) { - mContainer.reserve(reserve); - } - virtual ~vector() = default; - - class ContainerType { - public: - inline void* data() { - return (char*)this + 2; - } - - uint8_t mTypeInfo; - uint8_t mDataTypeCheckSum; - uint8_t mData[DataSize]; - }; - - struct Iterator - { - public: - using iterator_category = std::forward_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = ContainerType; - using pointer = value_type*; // or also value_type* - using reference = value_type&; // or also value_type& - - Iterator(pointer ptr) : m_ptr(ptr) {} - - reference operator*() const { return *m_ptr; } - pointer operator->() { return m_ptr; } - - template - T* get() { - if (l::meta::class_checksum() != m_ptr->mDataTypeCheckSum) { - return nullptr; - } - return reinterpret_cast(m_ptr->data()); - } - - // Prefix increment - Iterator& operator++() { m_ptr++; return *this; } - - // Postfix increment - Iterator operator++(int) { - Iterator tmp = *this; ++(*this); return tmp; } - - friend bool operator== (const Iterator& a, const Iterator& b) { return a.m_ptr == b.m_ptr; }; - friend bool operator!= (const Iterator& a, const Iterator& b) { return a.m_ptr != b.m_ptr; }; - - private: - pointer m_ptr; - }; - - Iterator begin() { return Iterator(mContainer.data()); - } - Iterator end() { - return Iterator(mContainer.data() + mContainer.size()); - } - - template - void push_back(T&& value) { - static_assert(sizeof(T) <= sizeof(ContainerType)); - ContainerType* ptr = mContainer.data() + mContainer.size(); - mContainer.push_back({}); - ptr->mDataTypeCheckSum = l::meta::class_checksum(); - memcpy(ptr->data(), (void*)&value, sizeof(T)); - } - - void erase(size_t pos) { - mContainer.erase(mContainer.begin() + pos); - } - - protected: - std::vector mContainer; - }; } diff --git a/packages/memory/include/memory/VectorPolymorphic.h b/packages/memory/include/memory/VectorPolymorphic.h new file mode 100644 index 00000000..dbfc3188 --- /dev/null +++ b/packages/memory/include/memory/VectorPolymorphic.h @@ -0,0 +1,115 @@ +#pragma once + +#include +#include +#include +#include +#include // For lower_bound +#include +#include +#include // For std::forward_iterator_tag +#include // For std::ptrdiff_t + +#include "logging/Log.h" +#include "meta/Reflection.h" + +namespace l::container { + + template + class VectorPolymorphic { + public: + static const size_t DataSize = MaxElementSize - 2; + + VectorPolymorphic(size_t reserve) { + mContainer.reserve(reserve); + } + virtual ~VectorPolymorphic() = default; + + class ContainerType { + public: + inline void* data() { + return (char*)this + 2; + } + + template + bool isType() { + auto type = l::meta::class_checksum(); + return mDataTypeCheckSum == (type % 255); + } + + template + void setType() { + auto type = l::meta::class_checksum(); + mDataTypeCheckSum = type % 255; + } + + uint8_t mTypeInfo; + uint8_t mDataTypeCheckSum; + uint8_t mData[DataSize]; + }; + + struct Iterator + { + public: + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = ContainerType; + using pointer = value_type*; // or also value_type* + using reference = value_type&; // or also value_type& + + Iterator(pointer ptr) : m_ptr(ptr) {} + + reference operator*() const { return *m_ptr; } + pointer operator->() { return m_ptr; } + + template + T* get() { + if (!m_ptr->isType()) { + return nullptr; + } + return reinterpret_cast(m_ptr->data()); + } + + Iterator& operator+=(int index) { m_ptr+=index; return *this; } + + // Prefix increment + Iterator& operator++() { m_ptr++; return *this; } + + // Postfix increment + Iterator operator++(int) { + Iterator tmp = *this; ++(*this); return tmp; } + + friend bool operator== (const Iterator& a, const Iterator& b) { return a.m_ptr == b.m_ptr; }; + friend bool operator!= (const Iterator& a, const Iterator& b) { return a.m_ptr != b.m_ptr; }; + + private: + pointer m_ptr; + }; + + Iterator begin() { return Iterator(mContainer.data()); + } + Iterator end() { + return Iterator(mContainer.data() + mContainer.size()); + } + + Iterator operator[](size_t index) { + return Iterator(mContainer.data() + index); + } + + template + void push_back(T&& value) { + static_assert(sizeof(T) <= sizeof(ContainerType)); + ContainerType* ptr = mContainer.data() + mContainer.size(); + mContainer.push_back({}); + ptr->setType(); + memcpy(ptr->data(), (void*)&value, sizeof(T)); + } + + void erase(size_t pos) { + mContainer.erase(mContainer.begin() + pos); + } + + protected: + std::vector mContainer; + }; +} diff --git a/packages/memory/include/memory/VectorSorted.h b/packages/memory/include/memory/VectorSorted.h new file mode 100644 index 00000000..a8e71bdb --- /dev/null +++ b/packages/memory/include/memory/VectorSorted.h @@ -0,0 +1,250 @@ +#pragma once + +#include +#include +#include +#include +#include // For lower_bound +#include +#include +#include // For std::forward_iterator_tag +#include // For std::ptrdiff_t + +#include "logging/Log.h" +#include "meta/Reflection.h" + +namespace l::container { + + template + class VectorSorted { + public: + + //-------------------------------------------------------------------// + // SetSorted() // + //-------------------------------------------------------------------// + // I didn't want to override every constructor to set this + // member variable, so this function is publicly accessible. + // You should call SetSorted( true ) right after construction. + // TO DO: if you feel like it... derive all constructors to avoid + // the need for this. There are 4 last time I checked. + //-------------------------------------------------------------------// + void SetSorted(bool bSorted = true) { m_bSorted = bSorted; } + + //-------------------------------------------------------------------// + // sort() // + //-------------------------------------------------------------------// + // This function sorts the data as needed. Call it after repeated calls to + // push_unsorted(), or just let other members call it for you on next access. + // It calls std::sort(), which defaults to using operator<() for + // comparisons. + //-------------------------------------------------------------------// + void sort() { + if (!m_bSorted) + { + std::sort(vec.begin(), vec.end()); + SetSorted(); + } + } + + typename std::vector::iterator lower_bound_it(const T& key) { + if (!m_bSorted) + sort(); + + typename std::vector::iterator it = std::lower_bound(vec.begin(), vec.end(), key); + return it; + } + + /*const*/ T* lower_bound_ptr(const T& key) { + typename std::vector::iterator it = lower_bound_it(key); + + if (it == vec.end()) + return 0; + + /*const*/ T* t = &(*it); + return t; + } + + + //-------------------------------------------------------------------// + // find_it_or_fail() // + //-------------------------------------------------------------------// + // This function takes the given object and determines if there is + // a match in the vector. It returns an iterator to the actual + // object in the vector, if found. Otherwise returns std::vector::end(). + // + // This is the function you want to use most of the time + // (or the predicate version if you are using object pointers). + // + // USAGE: it makes most sense to use this function if you have + // an object with a key, other member variables, and operator<() + // that uses the key to test for equality. You then set up a dummy + // "search" object with the key set to the search value, call the + // function, and use the result to extract the additional information + // from the object. + //-------------------------------------------------------------------// + typename std::vector::iterator find_it_or_fail(const T& key) { + typename std::vector::iterator it = lower_bound_it(key); + + if (it != vec.end()) + + // lower_bound() does not necessarily indicate a successful search. + // The iterator points to the object where an insertion + // should take place. We check that result to see if we actually + // had an exact match. + + // NOTE: This is how the STL determines equality using only operator()<. + // Two comparisons, ugg, but it is a nice little trick. + if (!((*it) < key) && !(key < (*it))) + + return it; + + return vec.end(); + } + + //-------------------------------------------------------------------// + // find_ptr_or_fail() // + //-------------------------------------------------------------------// + // A variation of find_it_or_fail() that provides a pointer to result. + //-------------------------------------------------------------------// + T* find_ptr_or_fail(const T& key) { + typename std::vector::iterator it = find_it_or_fail(key); + if (it != vec.end()) + return &(*it); + + return 0; + } + + //-------------------------------------------------------------------// + // push_sorted() // + //-------------------------------------------------------------------// + // This is used to insert into a vector that always remains sorted. + // Because we have a sorted vector, finding the insertion location + // with std::lower_bound() is relatively cheap. + // + // If you have multiple insertions, consider + // using push_unsorted() for each, then calling sort(). + //-------------------------------------------------------------------// + void push_sorted(const T& t) { + if (!m_bSorted) + { + sort(); + } + + // Insert at "lower_bound" (the proper sorted location). + vec.insert(std::lower_bound(vec.begin(), vec.end(), t), t); + } + + //-------------------------------------------------------------------// + // push_unsorted() // + //-------------------------------------------------------------------// + // This is similar to push_back(), but in addition, it sets the + // unsorted flag. + //-------------------------------------------------------------------// + void push_unsorted(const T& t) { + SetSorted(false); + vec.push_back(t); + } + + //-------------------------------------------------------------------// + // operator=() // + //-------------------------------------------------------------------// + // This allows us to set the VectorSorted from a std::vector. + //-------------------------------------------------------------------// + VectorSorted& operator=(std::vector& v) { + typename std::vector::iterator it; + for (it = v.begin(); it != v.end(); ++it) + push_unsorted((*it)); + return this; + } + + // CALLS WHERE YOU PROVIDE THE FUNCTOR OR FUNCTION POINTER + // If you need to use a predicate sort function, ALWAYS use these methods + // instead of the non-functor versions. + // NOTE: UPDATE THIS when C++0x polymorphic function wrappers are available. + template + inline void sort(_Pr pr) { + if (!m_bSorted) + { + std::sort(vec.begin(), vec.end(), pr); + SetSorted(); + } + } + template + inline typename std::vector::iterator lower_bound_it(const T& key, _Pr pr) { + if (!m_bSorted) + { + std::sort(vec.begin(), vec.end(), pr); + SetSorted(); + } + typename std::vector::iterator it = std::lower_bound(vec.begin(), vec.end(), key, pr); + return it; + } + + template + inline /*const*/ T* lower_bound_ptr(const T& key, _Pr pr) { + typename std::vector::iterator it = lower_bound_it(key, pr); + + if (it == vec.end()) + return 0; + + /*const*/ T* t = &(*it); + return t; + } + + template + inline void push_sorted(const T& t, _Pr pr) { + if (!m_bSorted) + { + std::sort(vec.begin(), vec.end(), pr); + SetSorted(); + } + + // Insert at "lower_bound" (the proper sorted location). + vec.insert(std::lower_bound(vec.begin(), vec.end(), t, pr), t); + } + + template + inline typename std::vector::iterator find_it_or_fail(const T& key, _Pr pr) { + typename std::vector::iterator it = lower_bound_it(key, pr); + + if (it != vec.end()) + + // NOTE: We have to apply this using the predicate function, be careful... + if (!(pr((*it), key)) && !(pr(key, (*it)))) + + return it; + + return vec.end(); + } + + template + inline T* find_ptr_or_fail(const T& key, _Pr pr) { + typename std::vector::iterator it = find_it_or_fail(key, pr); + if (it != vec.end()) + return &(*it); + + return 0; + } + + inline typename std::vector::iterator begin() noexcept { + return vec.begin(); + } + + inline typename std::vector::iterator end() noexcept { + return vec.end(); + } + + inline size_t size() const noexcept { + return vec.size(); + } + + inline bool empty() const noexcept { + return vec.empty(); + } + + protected: + std::vector vec; + bool m_bSorted; + }; + +} diff --git a/packages/tools/tests/common/PolymorphicTest.cpp b/packages/memory/tests/common/VectorPolymorphicTest.cpp similarity index 63% rename from packages/tools/tests/common/PolymorphicTest.cpp rename to packages/memory/tests/common/VectorPolymorphicTest.cpp index c84344ce..50485840 100644 --- a/packages/tools/tests/common/PolymorphicTest.cpp +++ b/packages/memory/tests/common/VectorPolymorphicTest.cpp @@ -1,7 +1,7 @@ #include "testing/Test.h" #include "logging/Log.h" -#include "memory/Containers.h" +#include "memory/VectorPolymorphic.h" using namespace l; @@ -45,17 +45,25 @@ TEST(Container, Polymorphic) { static_assert(sizeof(Base) <= 16); static_assert(sizeof(Derived) <= 24); static_assert(sizeof(DoubleDerived) <= 32); - auto storage = container::vector<32>(256); + auto storage = container::VectorPolymorphic<32>(256); storage.push_back(Base()); storage.push_back(Derived()); storage.push_back(DoubleDerived()); - for (auto it = storage.begin(), end = storage.end(); it != end; ++it) { - Base* a = it.get(); - Derived* b = it.get(); - DoubleDerived* c = it.get(); - DoubleDerived* d = it.get(); - } + auto a = storage[0].get(); + TEST_TRUE(a != nullptr, ""); + TEST_TRUE(a->base == 0, ""); + + auto b = storage[1].get(); + TEST_TRUE(b != nullptr, ""); + TEST_TRUE(b->base == 1, ""); + TEST_TRUE(b->derived == 1, ""); + + auto c = storage[2].get(); + TEST_TRUE(c != nullptr, ""); + TEST_TRUE(c->base == 2, ""); + TEST_TRUE(c->derived == 2, ""); + TEST_TRUE(c->doublederived == 2, ""); } return 0;