/* Copyright (c) 2013 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Author: Leonardo de Moura */ #pragma once #include #include #include "runtime/debug.h" #include "runtime/optional.h" namespace lean { /** \brief Very similar to std::vector, but stores elements on the system stack when collection has less than \c INITIAL_SIZE. This collection is useful when writing functions that need temporary storage. */ template class buffer { protected: T * m_buffer; size_t m_pos; size_t m_capacity; char m_initial_buffer[INITIAL_SIZE * sizeof(T)]; void free_memory() { if (m_buffer != reinterpret_cast(m_initial_buffer)) #if __cpp_sized_deallocation >= 201309L operator delete[](reinterpret_cast(m_buffer), sizeof(T) * m_capacity); #else delete[] reinterpret_cast(m_buffer); #endif } void set_capacity(size_t new_capacity) { lean_assert(new_capacity > m_capacity); char * new_buffer_char = new char[sizeof(T) * new_capacity]; T * new_buffer = reinterpret_cast(new_buffer_char); std::uninitialized_copy(m_buffer, m_buffer + m_pos, new_buffer); destroy(); m_buffer = new_buffer; m_capacity = new_capacity; } void expand() { set_capacity(m_capacity << 1); } void destroy_elements() { std::for_each(begin(), end(), [](T & e) { e.~T(); }); } void destroy() { destroy_elements(); free_memory(); } public: typedef T value_type; typedef T * iterator; typedef T const * const_iterator; buffer(): m_buffer(reinterpret_cast(m_initial_buffer)), m_pos(0), m_capacity(INITIAL_SIZE) { } buffer(buffer const & source): m_buffer(reinterpret_cast(m_initial_buffer)), m_pos(0), m_capacity(INITIAL_SIZE) { std::for_each(source.begin(), source.end(), [=](T const & e) { push_back(e); }); } void ensure_capacity(size_t new_capacity) { if (new_capacity > m_capacity) { set_capacity(new_capacity); } } ~buffer() { destroy(); } buffer & operator=(buffer const & source) { clear(); std::for_each(source.begin(), source.end(), [=](T const & e) { push_back(e); }); return *this; } bool operator==(buffer const & other) const { if (size() != other.size()) { return false; } else { for (size_t i = 0; i < size(); i++) { if (operator[](i) != other[i]) return false; } return true; } } bool operator!=(buffer const & other) const { return !operator==(other); } T const & back() const { lean_assert(!empty() && m_pos > 0); return m_buffer[m_pos - 1]; } T & back() { lean_assert(!empty() && m_pos > 0); return m_buffer[m_pos - 1]; } T & operator[](size_t idx) { lean_assert(idx < size()); return m_buffer[idx]; } T const & operator[](size_t idx) const { lean_assert(idx < size()); return m_buffer[idx]; } void clear() { destroy_elements(); m_pos = 0; } size_t size() const { return m_pos; } T const * data() const { return m_buffer; } T * data() { return m_buffer; } bool empty() const { return m_pos == 0; } iterator begin() { return m_buffer; } iterator end() { return m_buffer + size(); } const_iterator begin() const { return m_buffer; } const_iterator end() const { return m_buffer + size(); } size_t capacity() const { return m_capacity; } void push_back(T const & elem) { if (m_pos >= m_capacity) expand(); new (m_buffer + m_pos) T(elem); m_pos++; } template void emplace_back(Args&&... args) { if (m_pos >= m_capacity) expand(); new (m_buffer + m_pos) T(args...); m_pos++; } void pop_back() { back().~T(); m_pos--; } void append(size_t sz, T const * elems) { ensure_capacity(size() + sz); for (size_t i = 0; i < sz; i++) push_back(elems[i]); } template void append(C const & c) { append(c.size(), c.data()); } void resize(size_t nsz, T const & elem = T()) { size_t sz = size(); if (nsz > sz) { ensure_capacity(nsz); for (size_t i = sz; i < nsz; i++) push_back(elem); } else if (nsz < sz) { for (size_t i = nsz; i < sz; i++) pop_back(); } lean_assert(size() == nsz); } void erase(size_t idx) { lean_assert(idx < size()); for (size_t i = idx+1; i < size(); i++) m_buffer[i-1] = std::move(m_buffer[i]); pop_back(); } void erase_elem(T const & elem) { for (size_t i = 0; i < size(); i++) { if (m_buffer[i] == elem) { erase(i); return; } } } optional index_of(T const & elem) const { for (size_t i = 0; i < size(); i++) if (m_buffer[i] == elem) return optional(i); return optional(); } void insert(size_t idx, T const & elem) { using std::swap; lean_assert(idx <= size()); push_back(elem); size_t i = size(); while (i > idx + 1) { --i; swap(m_buffer[i], m_buffer[i-1]); } } void shrink(size_t nsz) { size_t sz = size(); lean_assert(nsz <= sz); for (size_t i = nsz; i < sz; i++) pop_back(); lean_assert(size() == nsz); } }; }