#pragma once #include "array/Helpers.hpp" #include "iterator/DequeIterator.hpp" #include <stdexcept> #include <stdint.h> #include "../common/compat.hpp" #include "../common/errors.hpp" #include <stdlib.h> namespace sprawl { namespace collections { template<typename T> class Deque; } } template<typename T> class sprawl::collections::Deque { public: typedef DequeIterator<T, Deque<T>> iterator; typedef DequeIterator<T, Deque<T>> const_iterator; Deque(sprawl::collections::Capacity startingCapacity = sprawl::collections::Capacity(16)) : m_circleBuffer((T*)(malloc(startingCapacity.count * sizeof(T)))) , m_size(0) , m_capacity(startingCapacity.count) , m_zeroIndex(0) { } Deque(sprawl::collections::Fill fillCount) : m_circleBuffer((T*)(malloc(fillCount.count * sizeof(T)))) , m_size(fillCount.count) , m_capacity(fillCount.count) , m_zeroIndex(0) { for(ssize_t i = 0; i < m_size; ++i) { new(&m_circleBuffer[i]) T(); } } Deque(sprawl::collections::Fill fillCount, T const& value) : m_circleBuffer((T*)(malloc(fillCount.count * sizeof(T)))) , m_size(fillCount.count) , m_capacity(fillCount.count) , m_zeroIndex(0) { for(ssize_t i = 0; i < m_size; ++i) { new(&m_circleBuffer[i]) T(value); } } template<typename... Params> Deque(sprawl::collections::Fill fillCount, Params&& ... params) : m_circleBuffer((T*)(malloc(fillCount.count * sizeof(T)))) , m_size(fillCount.count) , m_capacity(fillCount.count) , m_zeroIndex(0) { for(ssize_t i = 0; i < m_size; ++i) { new(&m_circleBuffer[i]) T(std::forward<Params>(params)...); } } Deque(Deque const& other) : m_circleBuffer(other.m_size > 0 ? (T*)(malloc(other.m_size * sizeof(T))) : nullptr) , m_size(other.m_size) , m_capacity(other.m_size) , m_zeroIndex(0) { for(ssize_t i = 0; i < m_size; ++i) { new(&m_circleBuffer[i]) T(other[i]); } } Deque(Deque&& other) : m_circleBuffer(other.m_circleBuffer) , m_size(other.m_size) , m_capacity(other.m_capacity) , m_zeroIndex(other.m_zeroIndex) { other.m_circleBuffer = nullptr; other.m_size = 0; other.m_capacity = 0; other.m_zeroIndex = 0; } Deque& operator=(Deque const& other) { cleanup_(); m_size = 0; Reserve(other.m_size); m_size = other.m_size; m_zeroIndex = 0; for(ssize_t i = 0; i < m_size; ++i) { new(&m_circleBuffer[i]) T(other[i]); } return *this; } Deque& operator=(Deque&& other) { cleanup_(); m_circleBuffer = other.m_circleBuffer; m_size = other.m_size; m_capacity = other.m_capacity; m_zeroIndex = other.m_zeroIndex; other.m_circleBuffer = nullptr; other.m_size = 0; other.m_capacity = 0; other.m_zeroIndex = 0; return *this; } ~Deque() { cleanup_(); } T& operator[](ssize_t index) { if (index < 0) { index += m_size; } return m_circleBuffer[translateIndex_(index)]; } sprawl::ErrorState<T&> At(ssize_t index) { if (index < 0) { index += m_size; } if(index > m_size) { SPRAWL_THROW_EXCEPTION(sprawl::OutOfRangeError()); } ssize_t const translated = translateIndex_(index); return m_circleBuffer[translated]; } T const& operator[](ssize_t index) const { if (index < 0) { index += m_size; } return m_circleBuffer[translateIndex_(index)]; } sprawl::ErrorState<T const&> At(ssize_t index) const { if (index < 0) { index += m_size; } if(index > m_size) { SPRAWL_THROW_EXCEPTION(sprawl::OutOfRangeError()); } ssize_t const translated = translateIndex_(index); return m_circleBuffer[translated]; } T& Front() { return m_circleBuffer[m_zeroIndex]; } T& Back() { return m_circleBuffer[translateIndex_(m_size -1)]; } T const& Front() const { return m_circleBuffer[m_zeroIndex]; } T const& Back() const { return m_circleBuffer[translateIndex_(m_size -1)]; } void PushBack(T const& value) { checkGrow_(1); new(&m_circleBuffer[translateIndex_(m_size++)]) T(value); } void PushBack(T&& value) { checkGrow_(1); new(&m_circleBuffer[translateIndex_(m_size++)]) T(std::move(value)); } template<typename... Params> void EmplaceBack(Params &&... params) { checkGrow_(1); new(&m_circleBuffer[translateIndex_(m_size++)]) T(std::forward<Params>(params)...); } void PushFront(T const& value) { checkGrow_(1); m_zeroIndex = translateIndex_(-1); new(&m_circleBuffer[m_zeroIndex]) T(value); ++m_size; } void PushFront(T&& value) { checkGrow_(1); m_zeroIndex = translateIndex_(-1); new(&m_circleBuffer[m_zeroIndex]) T(std::move(value)); ++m_size; } template<typename... Params> void EmplaceFront(Params &&... params) { checkGrow_(1); m_zeroIndex = translateIndex_(-1); new(&m_circleBuffer[m_zeroIndex]) T(std::forward<Params>(params)...); ++m_size; } void PopBack() { m_circleBuffer[translateIndex_(--m_size)].~T(); } void PopFront() { m_circleBuffer[m_zeroIndex].~T(); ++m_zeroIndex; if(m_zeroIndex >= m_capacity) { m_zeroIndex = 0; } --m_size; } void ShrinkToFit() { if(m_size != m_capacity) { ssize_t newCapacity = m_size; reallocate_(newCapacity); } } ssize_t Size() const { return m_size; } ssize_t Capacity() const { return m_capacity; } void Reserve(ssize_t newCapacity) { if(newCapacity > m_capacity) { reallocate_(newCapacity); } } void Resize(ssize_t newSize) { Resize(newSize, T()); } void Resize(ssize_t newSize, T const& value) { Reserve(newSize); //Either delete lost items or create new ones; one loop happens, the other doesn't for(ssize_t i = newSize; i < m_size; ++i) { m_circleBuffer[translateIndex_(i)].~T(); } for(ssize_t i = m_size; i < newSize; ++i) { new(&m_circleBuffer[translateIndex_(i)]) T(value); } m_size = newSize; } bool Empty() { return m_size == 0; } void Clear() { for(int i = 0; i < m_size; ++i) { m_circleBuffer[translateIndex_(i)].~T(); } m_size = 0; } void Swap(Deque& other) { char tmpData[sizeof(Deque)]; memcpy(tmpData, this, sizeof(Deque)); memcpy(this, &other, sizeof(Deque)); memcpy(&other, tmpData, sizeof(Deque)); } void Insert(const_iterator position, T const& value) { int idx = position.Index(); shiftRight_(idx, 1); new(&m_circleBuffer[translateIndex_(idx)]) T(value); } void Insert(const_iterator position, T&& value) { int idx = position.Index(); shiftRight_(idx, 1); new(&m_circleBuffer[translateIndex_(idx)]) T(std::move(value)); } template<typename... Params> void Emplace(const_iterator position, Params &&... params) { int idx = position.Index(); shiftRight_(idx, 1); new(&m_circleBuffer[translateIndex_(idx)]) T(std::forward<Params>(params)...); } void Erase(const_iterator position) { int idx = position.Index(); shiftLeft_(idx, 1); } iterator begin() { return iterator(0, this); } iterator end() { return iterator(m_size, this); } const_iterator cbegin() const { return const_iterator(0, this); } const_iterator cend() const { return const_iterator(m_size, this); } const_iterator begin() const { return cbegin(); } const_iterator end() const { return cend(); } private: ssize_t translateIndex_(int index) const { ssize_t translated = m_zeroIndex + index; if(translated < 0) { translated += m_capacity; } else if(translated >= m_capacity) { translated -= m_capacity; } return translated; } ssize_t reverseIndex_(T* element) const { ssize_t const offsetFromFront = element - m_circleBuffer; ssize_t adjustedOffset = offsetFromFront - m_zeroIndex; if(adjustedOffset < 0) { adjustedOffset += m_capacity; } return adjustedOffset; } void shiftRight_(ssize_t idx, ssize_t amount) { checkGrow_(amount); for(ssize_t i = (m_size + amount) - 1; i > idx; --i) { if(idx >= m_size) { new(&m_circleBuffer[translateIndex_(i)]) T(std::move(m_circleBuffer[translateIndex_(i - amount)])); } else { m_circleBuffer[translateIndex_(i)] = std::move(m_circleBuffer[translateIndex_(i - amount)]); } } m_size += amount; } void shiftLeft_(ssize_t idx, ssize_t amount) { for(ssize_t i = idx; i < m_size - amount; ++i) { m_circleBuffer[translateIndex_(i)] = std::move(m_circleBuffer[translateIndex_(i + amount)]); } for(ssize_t i = m_size-amount; i < m_size; ++i) { m_circleBuffer[translateIndex_(i)].~T(); } m_size -= amount; } void checkGrow_(ssize_t amount) { if(m_size + amount > m_capacity) { grow_(amount); } } void grow_(ssize_t amount) { ssize_t newCapacity = m_size + (amount > m_size ? amount : m_size); reallocate_(newCapacity); } void reallocate_(ssize_t newCapacity) { T* newArray = (T*)(malloc(newCapacity * sizeof(T))); for(ssize_t i = 0; i < m_size; ++i) { new (&newArray[i]) T(std::move(m_circleBuffer[translateIndex_(i)])); m_circleBuffer[translateIndex_(i)].~T(); } free(m_circleBuffer); m_circleBuffer = newArray; m_zeroIndex = 0; m_capacity = newCapacity; } void cleanup_() { if(m_circleBuffer) { for(ssize_t i = 0; i < m_size; ++i) { m_circleBuffer[translateIndex_(i)].~T(); } free(m_circleBuffer); m_circleBuffer = nullptr; } } T* m_circleBuffer; ssize_t m_size; ssize_t m_capacity; ssize_t m_zeroIndex; };
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#6 | 16768 | ShadauxCat |
Improvements to error handling in builds with exceptions disabled: - In debug builds or with SPRAWL_ERRORSTATE_STRICT enabled, ErrorState will output a message to stderr and terminate if Get() is called when an error flag is set. (In release buils or with SPRAWL_ERRORSTATE_PERMISSIVE defined, Get() will return junk memory in this case.) - In debug builds or with SPRAWL_ERRORSTATE_STRICT enabled, ErrorState will output a message to stderr and terminate if its destructor is called without checking the errorstate if an error is present (equivalent to an exception terminating the application if no catch() block is present for it). - On linux builds and when running "Analyze" through visual studio, a warning will be issued if any function returning ErrorState has its return value ignored. (This only applies to builds with exceptions not enabled; when exceptions are enabled no warning is issued) - Many functions that could return ErrorState were having their return values silently ignored in internal sprawl code so the user would not find out about errors if exceptions are disabled; now anything in sprawl code that calls a function returning ErrorState will either handle the error, or (in most cases) surface it back up to the user. - As a positive side-effect of the warnings for ignoring ErrorState, several constructors that were capable of throwing exceptions are no longer capable of doing so. #review-16769 |
||
#5 | 14216 | ShadauxCat |
-Moved some global sprawl::Strings into local scope in json serialization test because of initialization order issues in the memory allocator on mac. This is a temporary fix, and a real fix will come by making the pool allocator work in explicitly-sized pieces and putting all the code for those pieces into a cpp file. -Fixed a large number of warnings on mac/linux that were exposed by fixes to csbuild -Fixed compile errors on mac due to malloc and alloca not being defined, fixed by #include <stdlib.h> in appropriate places -Fixed mac os x trying to link against pthread erroneously -Provided os x implementation of time library -Fixed compile errors on os x due to std::unordered_map whining about the difference between an allocator that allocates std::pair<key, value> and one that allocates std::pair<key const, value>, which, of course, is that the allocator will be no different at all. -Fixed an actual issue where one unordered_map was allocating only key_type instead of std::pair<key_type, value_type> -Fixed a memory leak where coroutine objects would never be cleaned up because either Yield() or reactivate_() will never return (and thus never clean up their stack memory and thus never release any dynamic memory held in stack objects) depending on the situation - if the function runs to completion, reactivate_() never returns after calling swapcontext(); meanwhile, if the function does not run to completion, Yield() never returns after calling Pause(). This behavior will need to be well-documented because it will affect client-side code as well. Stack memory within a coroutine should not rely on RAII behavior. -Fixed compile failure when creating a StlWrapper with a const value_type #review-14217 |
||
#4 | 14168 | ShadauxCat |
-Implemented Array -Fixed compile error when calling Vector::Swap() -Fixed missing constness on functions called from const functions in Deque -Added fill, copy, and move constructor unit tests for Vector and Deque, and Swap() unit test for Vector #review-14169 |
||
#3 | 14121 | ShadauxCat |
-Fixed msvc compile errors (msvc sucks at decltype, btw...) -Fixed BitVector and BitSet being broken on msvc due to 1L being 32-bit rather than 64-bit -Fixed Deque being broken on 32-bit systems due to an errant int64_t -Changed types of deque indexes from size_t to ssize_t; since they have to be signed for a moment to handle circling the buffer, the sign bit is lost for capacity anyway, and using signed indexes means... -Deque and Vector now support negative indexing a la python list #review-14122 |
||
#2 | 14100 | ShadauxCat |
-Added Deque implementation using circular buffer. -Fixed List::Insert() and ForwardList::Insert() inserting after the iterator instead of before it. Adjusted the unit tests to compensate. -Fixed a couple of small vector bugs #review-14101 |
||
#1 | 11496 | ShadauxCat | Initial checkin: Current states for csbuild and libSprawl |