/hphp/util/compact-vector.h
C Header | 477 lines | 384 code | 59 blank | 34 comment | 61 complexity | b1f46f67a6b57b7d3d1cf8de170dd6bf MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, MIT, LGPL-2.0, Apache-2.0
- /*
- +----------------------------------------------------------------------+
- | HipHop for PHP |
- +----------------------------------------------------------------------+
- | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- */
- #ifndef incl_HPHP_COMPACT_VECTOR_H_
- #define incl_HPHP_COMPACT_VECTOR_H_
- #include <stdlib.h>
- #include <cstring>
- #include <type_traits>
- #include <initializer_list>
- #include "hphp/util/assertions.h"
- #include "hphp/util/safe-cast.h"
- namespace HPHP {
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * During its lifetime, an instance of CompactVector can transition
- * between 2 states:
- * State 0: m_val == 0
- * This is the initial state for a newly constructed CompactVector.
- * There are no elements and no allocated block of memory.
- * State 1: m_val != 0
- * In this state, m_data points to a malloced block of memory. The
- * number of elements, the capacity of the block, and the values of
- * all the elements reside in the allocated block of memory.
- */
- template <typename T, typename Alloc = std::allocator<char>>
- struct CompactVector : private Alloc::template rebind<char>::other {
- using size_type = std::size_t;
- using value_type = T;
- using iterator = T*;
- using const_iterator = const T*;
- using Allocator = typename Alloc::template rebind<char>::other;
- friend iterator begin(CompactVector& v) { return v.begin(); }
- friend iterator end(CompactVector& v) { return v.end(); }
- friend const_iterator begin(const CompactVector& v) { return v.begin(); }
- friend const_iterator end(const CompactVector& v) { return v.end(); }
- CompactVector();
- explicit CompactVector(size_type n);
- CompactVector(size_type n, const T&);
- CompactVector(CompactVector&& other) noexcept;
- CompactVector(const CompactVector& other);
- CompactVector(std::initializer_list<T> init);
- CompactVector& operator=(CompactVector&&);
- CompactVector& operator=(const CompactVector&);
- ~CompactVector();
- void swap(CompactVector& other) noexcept;
- bool operator==(const CompactVector& other) const;
- bool operator!=(const CompactVector& other) const {
- return !(*this == other);
- }
- iterator begin() { return m_data ? elems() : nullptr; }
- iterator end() { return m_data ? elems() + size() : nullptr; }
- const_iterator begin() const { return m_data ? elems() : nullptr; }
- const_iterator end() const { return m_data ? elems() + size() : nullptr; }
- T* data() { return m_data ? elems() : nullptr; }
- const T* data() const { return m_data ? elems() : nullptr; }
- bool empty() const;
- size_type size() const;
- size_type capacity();
- void clear();
- void push_back(const T& val);
- void push_back(T&& val);
- template <class... Args>
- void emplace_back(Args&&... args);
- void pop_back();
- void erase(iterator);
- void erase(iterator, iterator);
- iterator insert(iterator p, const T& v) { return insert_impl(p, 1, v); }
- iterator insert(iterator p, T&& v) { return insert_impl(p, 1, std::move(v)); };
- iterator insert(iterator p, size_t num, const T& v) {
- return insert_impl(p, num, v);
- };
- template<typename U>
- iterator insert(iterator p, U i1, U i2);
- void resize(size_type sz);
- void resize(size_type sz, const value_type& value);
- void shrink_to_fit();
- T& operator[](size_type index) { return *get(index); }
- const T& operator[](size_type index) const { return *get(index); }
- T& front() { return *get(0); }
- const T& front() const { return *get(0); }
- T& back() { return *get(m_data->m_len - 1); }
- const T& back() const { return *get(m_data->m_len - 1); }
- void reserve(size_type sz);
- private:
- using Allocator::allocate;
- using Allocator::deallocate;
- struct CompactVectorData {
- uint32_t m_len;
- uint32_t m_capacity;
- };
- iterator insert_elems(iterator, size_t num);
- template <class U>
- iterator insert_impl(iterator, size_t num, U&&);
- void assign(const CompactVector& other);
- void grow();
- T* get(size_type index) const;
- T* elems() const;
- static size_t required_mem(size_type n);
- void reserve_impl(size_type sz);
- bool resize_helper(size_type sz);
- CompactVectorData* m_data;
- static constexpr size_type initial_capacity = 4;
- /* We mainly want this so pretty.py can figure out the alignment */
- static constexpr size_type elems_offset =
- alignof(T) >= sizeof(CompactVectorData) ?
- alignof(T) : sizeof(CompactVectorData);
- /* And we need this to prevent gcc from throwing away elems_offset */
- using elems_offset_type = char[elems_offset];
- };
- template <typename T, typename A>
- CompactVector<T, A>::CompactVector() {
- m_data = nullptr;
- }
- template <typename T, typename A>
- CompactVector<T, A>::CompactVector(size_type n) {
- m_data = nullptr;
- resize(n);
- }
- template <typename T, typename A>
- CompactVector<T, A>::CompactVector(size_type n, const T& val) {
- m_data = nullptr;
- resize(n, val);
- }
- template <typename T, typename A>
- CompactVector<T, A>::CompactVector(CompactVector&& other) noexcept
- : m_data(other.m_data) {
- other.m_data = nullptr;
- }
- template <typename T, typename A>
- CompactVector<T, A>::CompactVector(const CompactVector& other)
- : m_data(nullptr) {
- assign(other);
- }
- template <typename T, typename A>
- CompactVector<T, A>&
- CompactVector<T, A>::operator=(const CompactVector& other) {
- if (this == &other) return *this;
- clear();
- assign(other);
- return *this;
- }
- template <typename T, typename A>
- CompactVector<T, A>::CompactVector(std::initializer_list<T> init) :
- m_data(nullptr) {
- reserve_impl(init.size());
- for (auto const& e : init) {
- push_back(e);
- }
- }
- template <typename T, typename A>
- void CompactVector<T, A>::assign(const CompactVector& other) {
- assert(!m_data);
- if (!other.size()) return;
- reserve_impl(other.m_data->m_len);
- auto const sz = other.m_data->m_len;
- for (size_type i = 0; i < sz; ++i) {
- push_back(other[i]);
- }
- }
- template <typename T, typename A>
- CompactVector<T, A>& CompactVector<T, A>::operator=(CompactVector&& other) {
- std::swap(m_data, other.m_data);
- return *this;
- }
- template <typename T, typename A>
- void CompactVector<T, A>::swap(CompactVector& other) noexcept {
- std::swap(m_data, other.m_data);
- }
- template <typename T, typename A>
- bool CompactVector<T, A>::operator==(const CompactVector& other) const {
- auto const sz = size();
- if (sz != other.size()) return false;
- for (size_type i = 0; i < sz; ++i) {
- if (!(*get(i) == other[i])) return false;
- }
- return true;
- }
- template <typename T, typename A>
- CompactVector<T, A>::~CompactVector() {
- clear();
- }
- template <typename T, typename A>
- T* CompactVector<T, A>::elems() const {
- assert(m_data);
- return (T*)((char*)m_data + elems_offset);
- }
- template <typename T, typename A>
- size_t CompactVector<T, A>::required_mem(size_type n) {
- return elems_offset + sizeof(T) * n;
- }
- template <typename T, typename A>
- T* CompactVector<T, A>::get(size_type index) const {
- // Index into the allocated block of memory
- auto e = elems();
- assert(index < m_data->m_len);
- return e + index;
- }
- template <typename T, typename A>
- bool CompactVector<T, A>::empty() const {
- return size() == 0;
- }
- template <typename T, typename A>
- typename CompactVector<T, A>::size_type CompactVector<T, A>::size() const {
- return m_data ? m_data->m_len : 0;
- }
- template <typename T, typename A>
- typename CompactVector<T, A>::size_type CompactVector<T, A>::capacity() {
- return m_data ? m_data->m_capacity : 0;
- }
- template <typename T, typename A>
- void CompactVector<T, A>::erase(iterator elm) {
- assert(elm - elems() < size());
- auto const e = end();
- while (++elm != e) {
- elm[-1] = std::move(*elm);
- }
- elm[-1].~T();
- m_data->m_len--;
- }
- template <typename T, typename A>
- void CompactVector<T, A>::erase(iterator elm1, iterator elm2) {
- if (elm1 == elm2) return;
- assert(elems() <= elm1 && elm1 <= elm2 && elm2 <= end());
- auto const e = end();
- while (elm2 != e) {
- *elm1++ = std::move(*elm2++);
- }
- m_data->m_len -= elm2 - elm1;
- while (elm1 != e) {
- elm1++->~T();
- }
- }
- template <typename T, typename A>
- bool CompactVector<T, A>::resize_helper(size_type sz) {
- auto const old_size = size();
- if (sz == old_size) return true;
- if (sz > old_size) {
- reserve_impl(sz);
- return false;
- }
- auto elm = get(sz);
- m_data->m_len = sz;
- do {
- elm++->~T();
- } while (++sz < old_size);
- return true;
- }
- template <typename T, typename A>
- void CompactVector<T, A>::resize(size_type sz, const value_type& v) {
- if (resize_helper(sz)) return;
- while (m_data->m_len < sz) {
- push_back(v);
- }
- }
- template <typename T, typename A>
- void CompactVector<T, A>::resize(size_type sz) {
- if (resize_helper(sz)) return;
- while (m_data->m_len < sz) {
- push_back(T{});
- }
- }
- template <typename T, typename A>
- void CompactVector<T, A>::shrink_to_fit() {
- if (!m_data || m_data->m_capacity == m_data->m_len) return;
- if (!m_data->m_len) {
- clear();
- return;
- }
- reserve_impl(m_data->m_len);
- }
- template <typename T, typename A>
- void copy(CompactVector<T, A>& dest, const std::vector<T>& src) {
- dest.clear();
- dest.reserve(src.size());
- for (auto const& v : src) dest.push_back(v);
- }
- template <typename T, typename A>
- void CompactVector<T, A>::clear() {
- if (!m_data) return;
- if (!std::is_trivially_destructible<T>::value) {
- if (auto sz = size()) {
- auto elm = elems();
- do { elm++->~T(); } while (--sz);
- }
- }
- deallocate(reinterpret_cast<char*>(m_data),
- required_mem(m_data->m_capacity));
- m_data = nullptr;
- }
- template <typename T, typename A>
- void CompactVector<T, A>::grow() {
- reserve_impl(m_data ? m_data->m_capacity * 2LL : initial_capacity);
- }
- template <typename T, typename A>
- void CompactVector<T, A>::reserve_impl(size_type new_capacity) {
- auto new_data = (CompactVectorData*)allocate(required_mem(new_capacity));
- if (!new_data) throw std::bad_alloc{};
- if (m_data) {
- auto len = m_data->m_len;
- auto old_data = m_data;
- auto const old_capacity = old_data->m_capacity;
- auto old_elems = elems();
- m_data = new_data;
- auto new_elems = elems();
- m_data->m_len = len;
- m_data->m_capacity = safe_cast<uint32_t>(new_capacity);
- while (len--) {
- new (new_elems++) T(std::move(*old_elems));
- old_elems++->~T();
- }
- deallocate(reinterpret_cast<char*>(old_data), required_mem(old_capacity));
- } else {
- // If there are currently no elements, all we have to do is allocate a
- // block of memory and initialize m_len and m_capacity.
- m_data = new_data;
- m_data->m_len = 0;
- m_data->m_capacity = new_capacity;
- }
- }
- template <typename T, typename A>
- void CompactVector<T, A>::reserve(size_type new_capacity) {
- if (new_capacity > capacity()) reserve_impl(new_capacity);
- }
- template <typename T, typename A>
- typename CompactVector<T, A>::iterator
- CompactVector<T, A>::insert_elems(iterator before, size_t num) {
- if (!num) return before;
- auto const sz = size();
- assert(sz <= capacity());
- auto cap = capacity();
- if (sz + num > cap) {
- auto const pos = sz ? before - elems() : 0;
- assert(pos <= sz);
- cap <<= 1;
- if (sz + num > cap) {
- cap = sz + num;
- if (cap < initial_capacity) cap = initial_capacity;
- }
- reserve(cap);
- before = elems() + pos;
- }
- auto e = end();
- m_data->m_len += num;
- while (e != before) {
- --e;
- new (e + num) T(std::move(*e));
- e->~T();
- }
- return e;
- }
- template <typename T, typename A>
- template <typename U>
- typename CompactVector<T, A>::iterator
- CompactVector<T, A>::insert_impl(iterator before, size_t num, U&& val) {
- auto e = insert_elems(before, num);
- while (num--) {
- new (e + num) T(std::forward<U>(val));
- }
- return e;
- }
- template <typename T, typename A>
- template <typename U>
- typename CompactVector<T, A>::iterator
- CompactVector<T, A>::insert(iterator before, U i1, U i2) {
- auto e = insert_elems(before, i2 - i1);
- auto i = e;
- while (i1 != i2) {
- new (i++) T(*i1++);
- }
- return e;
- }
- template <typename T, typename A>
- void CompactVector<T, A>::push_back(const T& val) {
- auto const sz = size();
- assert(sz <= capacity());
- if (sz == capacity()) grow();
- ++(m_data->m_len);
- new (get(sz)) T(val);
- }
- template <typename T, typename A>
- void CompactVector<T, A>::push_back(T&& val) {
- auto const sz = size();
- assert(sz <= capacity());
- if (sz == capacity()) grow();
- ++(m_data->m_len);
- new (get(sz)) T(std::move(val));
- }
- template <typename T, typename A>
- template <class... Args>
- void CompactVector<T, A>::emplace_back(Args&&... args) {
- auto const sz = size();
- assert(sz <= capacity());
- if (sz == capacity()) grow();
- ++(m_data->m_len);
- new (get(sz)) T(std::forward<Args>(args)...);
- }
- template <typename T, typename A>
- void CompactVector<T, A>::pop_back() {
- if (m_data && m_data->m_len > 0) {
- // Otherwise, we just decrement the length
- --(m_data->m_len);
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- }
- #endif