#pragma once #include "StarAlgorithm.hpp" namespace Star { // A vector that is stack allocated up to a maximum size, becoming heap // allocated when it grows beyond that size. Always takes up stack space of // MaxStackSize * sizeof(Element). template class SmallVector { public: typedef Element* iterator; typedef Element const* const_iterator; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; typedef Element value_type; typedef Element& reference; typedef Element const& const_reference; SmallVector(); SmallVector(SmallVector const& other); SmallVector(SmallVector&& other); template SmallVector(SmallVector const& other); template SmallVector(Iterator first, Iterator last); SmallVector(size_t size, Element const& value = Element()); SmallVector(initializer_list list); ~SmallVector(); SmallVector& operator=(SmallVector const& other); SmallVector& operator=(SmallVector&& other); SmallVector& operator=(std::initializer_list list); size_t size() const; bool empty() const; void resize(size_t size, Element const& e = Element()); void reserve(size_t capacity); reference at(size_t i); const_reference at(size_t i) const; reference operator[](size_t i); const_reference operator[](size_t i) const; const_iterator begin() const; const_iterator end() const; iterator begin(); iterator end(); const_reverse_iterator rbegin() const; const_reverse_iterator rend() const; reverse_iterator rbegin(); reverse_iterator rend(); // Pointer to internal data, always valid even if empty. Element const* ptr() const; Element* ptr(); void push_back(Element e); void pop_back(); iterator insert(iterator pos, Element e); template iterator insert(iterator pos, Iterator begin, Iterator end); iterator insert(iterator pos, initializer_list list); template void emplace(iterator pos, Args&&... args); template void emplace_back(Args&&... args); void clear(); iterator erase(iterator pos); iterator erase(iterator begin, iterator end); bool operator==(SmallVector const& other) const; bool operator!=(SmallVector const& other) const; bool operator<(SmallVector const& other) const; private: typename std::aligned_storage::type m_stackElements; bool isHeapAllocated() const; Element* m_begin; Element* m_end; Element* m_capacity; }; template SmallVector::SmallVector() { m_begin = (Element*)&m_stackElements; m_end = m_begin; m_capacity = m_begin + MaxStackSize; } template SmallVector::~SmallVector() { clear(); if (isHeapAllocated()) { free(m_begin, (m_capacity - m_begin) * sizeof(Element)); } } template SmallVector::SmallVector(SmallVector const& other) : SmallVector() { insert(begin(), other.begin(), other.end()); } template SmallVector::SmallVector(SmallVector&& other) : SmallVector() { for (auto& e : other) emplace_back(std::move(e)); } template template SmallVector::SmallVector(SmallVector const& other) : SmallVector() { for (auto const& e : other) emplace_back(e); } template template SmallVector::SmallVector(Iterator first, Iterator last) : SmallVector() { insert(begin(), first, last); } template SmallVector::SmallVector(size_t size, Element const& value) : SmallVector() { resize(size, value); } template SmallVector::SmallVector(initializer_list list) : SmallVector() { for (auto const& e : list) emplace_back(e); } template auto SmallVector::operator=(SmallVector const& other) -> SmallVector& { if (this == &other) return *this; resize(other.size()); for (size_t i = 0; i < size(); ++i) operator[](i) = other[i]; return *this; } template auto SmallVector::operator=(SmallVector&& other) -> SmallVector& { resize(other.size()); for (size_t i = 0; i < size(); ++i) operator[](i) = std::move(other[i]); return *this; } template auto SmallVector::operator=(std::initializer_list list) -> SmallVector& { resize(list.size()); for (size_t i = 0; i < size(); ++i) operator[](i) = std::move(list[i]); return *this; } template size_t SmallVector::size() const { return m_end - m_begin; } template bool SmallVector::empty() const { return m_begin == m_end; } template void SmallVector::resize(size_t size, Element const& e) { reserve(size); for (size_t i = this->size(); i > size; --i) pop_back(); for (size_t i = this->size(); i < size; ++i) emplace_back(e); } template void SmallVector::reserve(size_t newCapacity) { size_t oldCapacity = m_capacity - m_begin; if (newCapacity > oldCapacity) { newCapacity = max(oldCapacity * 2, newCapacity); auto newMem = (Element*)Star::malloc(newCapacity * sizeof(Element)); if (!newMem) throw MemoryException::format("Could not set new SmallVector capacity {}\n", newCapacity); size_t size = m_end - m_begin; auto oldMem = m_begin; auto oldHeapAllocated = isHeapAllocated(); // We assume that move constructors can never throw. for (size_t i = 0; i < size; ++i) { new (&newMem[i]) Element(std::move(oldMem[i])); } m_begin = newMem; m_end = m_begin + size; m_capacity = m_begin + newCapacity; auto freeOldMem = finally([=]() { if (oldHeapAllocated) Star::free(oldMem, oldCapacity * sizeof(Element)); }); for (size_t i = 0; i < size; ++i) { oldMem[i].~Element(); } } } template auto SmallVector::at(size_t i) -> reference { if (i >= size()) throw OutOfRangeException::format("out of range in SmallVector::at({})", i); return m_begin[i]; } template auto SmallVector::at(size_t i) const -> const_reference { if (i >= size()) throw OutOfRangeException::format("out of range in SmallVector::at({})", i); return m_begin[i]; } template auto SmallVector::operator[](size_t i) -> reference { starAssert(i < size()); return m_begin[i]; } template auto SmallVector::operator[](size_t i) const -> const_reference { starAssert(i < size()); return m_begin[i]; } template auto SmallVector::begin() const -> const_iterator { return m_begin; } template auto SmallVector::end() const -> const_iterator { return m_end; } template auto SmallVector::begin() -> iterator { return m_begin; } template auto SmallVector::end() -> iterator { return m_end; } template auto SmallVector::rbegin() const -> const_reverse_iterator { return const_reverse_iterator(end()); } template auto SmallVector::rend() const -> const_reverse_iterator { return const_reverse_iterator(begin()); } template auto SmallVector::rbegin() -> reverse_iterator { return reverse_iterator(end()); } template auto SmallVector::rend() -> reverse_iterator { return reverse_iterator(begin()); } template Element const* SmallVector::ptr() const { return m_begin; } template Element* SmallVector::ptr() { return m_begin; } template void SmallVector::push_back(Element e) { emplace_back(std::move(e)); } template void SmallVector::pop_back() { if (m_begin == m_end) throw OutOfRangeException("SmallVector::pop_back called on empty SmallVector"); --m_end; m_end->~Element(); } template auto SmallVector::insert(iterator pos, Element e) -> iterator { emplace(pos, std::move(e)); return pos; } template template auto SmallVector::insert(iterator pos, Iterator begin, Iterator end) -> iterator { size_t toAdd = std::distance(begin, end); size_t startIndex = pos - m_begin; size_t endIndex = startIndex + toAdd; size_t toShift = size() - startIndex; resize(size() + toAdd); for (size_t i = toShift; i != 0; --i) operator[](endIndex + i - 1) = std::move(operator[](startIndex + i - 1)); for (size_t i = 0; i != toAdd; ++i) operator[](startIndex + i) = *begin++; return pos; } template auto SmallVector::insert(iterator pos, initializer_list list) -> iterator { return insert(pos, list.begin(), list.end()); } template template void SmallVector::emplace(iterator pos, Args&&... args) { size_t index = pos - m_begin; emplace_back(Element()); for (size_t i = size() - 1; i != index; --i) operator[](i) = std::move(operator[](i - 1)); operator[](index) = Element(std::forward(args)...); } template template void SmallVector::emplace_back(Args&&... args) { if (m_end == m_capacity) reserve(size() + 1); new (m_end) Element(std::forward(args)...); ++m_end; } template void SmallVector::clear() { while (m_begin != m_end) pop_back(); } template auto SmallVector::erase(iterator pos) -> iterator { size_t index = pos - ptr(); for (size_t i = index; i < size() - 1; ++i) operator[](i) = std::move(operator[](i + 1)); pop_back(); return pos; } template auto SmallVector::erase(iterator begin, iterator end) -> iterator { size_t startIndex = begin - ptr(); size_t endIndex = end - ptr(); size_t toRemove = endIndex - startIndex; for (size_t i = endIndex; i < size(); ++i) operator[](startIndex + (i - endIndex)) = std::move(operator[](i)); resize(size() - toRemove); return begin; } template bool SmallVector::operator==(SmallVector const& other) const { if (this == &other) return true; if (size() != other.size()) return false; for (size_t i = 0; i < size(); ++i) { if (operator[](i) != other[i]) return false; } return true; } template bool SmallVector::operator!=(SmallVector const& other) const { return !operator==(other); } template bool SmallVector::operator<(SmallVector const& other) const { for (size_t i = 0; i < size(); ++i) { if (i >= other.size()) return false; Element const& a = operator[](i); Element const& b = other[i]; if (a < b) return true; else if (b < a) return false; } return size() < other.size(); } template bool SmallVector::isHeapAllocated() const { return m_begin != (Element*)&m_stackElements; } }