//======================================================================= // Copyright Baptiste Wicht 2013-2018. // Distributed under the terms of the MIT License. // (See accompanying file LICENSE or copy at // http://www.opensource.org/licenses/MIT) //======================================================================= #ifndef VECTOR_H #define VECTOR_H #include #include #include #include #include namespace std { /*! * \brief A contiguous container of elements, automatically increasing. */ template struct vector { using value_type = T; ///< The value type contained in the vector using pointer_type = value_type*; ///< The pointer type contained in the vector using reference_type = value_type&; ///< The pointer type contained in the vector using const_reference_type = const value_type&; ///< The pointer type contained in the vector using size_type = size_t; ///< The size type using iterator = value_type*; ///< The iterator type using const_iterator = const value_type*; ///< The const iterator type using reverse_iterator = std::reverse_iterator; ///< The reverse iterator type using const_reverse_iterator = std::reverse_iterator; ///< The const reverse iterator type /*! * \brief Constructs en empty vector */ vector() : _data(nullptr), _size(0), _capacity(0) {} /*! * \brief Constructs a vector of the given size */ explicit vector(uint64_t c) : _data(allocate(c)), _size(0), _capacity(c) {} /*! * \brief Construct a vector containing the given values */ vector(initializer_list values) : _data(allocate(values.size())), _size(values.size()), _capacity(values.size()) { size_t i = 0; for(auto& v : values){ new (&_data[i++]) value_type(v); } } vector(const vector& rhs) : _data(nullptr), _size(rhs._size), _capacity(rhs._capacity) { if(!rhs.empty()){ _data = allocate(_capacity); for(size_t i = 0; i < _size; ++i){ new (&_data[i]) value_type(rhs._data[i]); } } } // Note: This is a bit of a lazy scheme // We always destruct all previous memory and copy construct the // new information. It could be better to simply copy assign and // only in the case of new allocation copy construct vector& operator=(const vector& rhs){ if (this != &rhs) { if (_capacity < rhs._capacity) { if (_data) { release(); } _capacity = rhs._capacity; _data = allocate(_capacity); } else { destruct_all(); } _size = rhs._size; for (size_t i = 0; i < _size; ++i) { new (&_data[i]) value_type(rhs._data[i]); } } return *this; } //Move constructors vector(vector&& rhs) : _data(rhs._data), _size(rhs._size), _capacity(rhs._capacity) { rhs._data = nullptr; rhs._size = 0; rhs._capacity = 0; }; vector& operator=(vector&& rhs){ if (this != &rhs) { if (_data) { release(); } _data = rhs._data; _size = rhs._size; _capacity = rhs._capacity; rhs._data = nullptr; rhs._size = 0; rhs._capacity = 0; } return *this; } ~vector(){ if(_data){ release(); } } //Getters /*! * \brief Returns the size of the vector */ constexpr size_type size() const noexcept { return _size; } /*! * \brief Indicates if the vector is empty */ bool empty() const noexcept { return _size == 0; } /*! * \brief Returns the capacity of the vector */ constexpr size_type capacity() const noexcept { return _capacity; } /*! * \brief Returns a pointer to the underlying array. */ value_type* data() noexcept { return _data; } /*! * \brief Returns a pointer to the underlying array. */ const value_type* data() const noexcept { return _data; } /*! * \brief Returns a const reference to the elemenet at the given position */ constexpr const value_type& operator[](size_type pos) const { return _data[pos]; } /*! * \brief Returns a reference to the elemenet at the given position */ value_type& operator[](size_type pos){ return _data[pos]; } /*! * \brief Returns a reference to the element at the front of the collection */ value_type& front(){ return _data[0]; } /*! * \brief Returns a const reference to the element at the front of the collection */ const value_type& front() const { return _data[0]; } /*! * \brief Returns a reference to the element at the back of the collection */ value_type& back(){ return _data[size() - 1]; } /*! * \brief Returns a const reference to the element at the back of the collection */ const value_type& back() const { return _data[size() - 1]; } //Modifiers /*! * \brief Augments the capacity to at least the given capacity */ void reserve(size_t new_capacity){ if(new_capacity > capacity()){ ensure_capacity(new_capacity); } } /*! * \brief Resize the vector to the given size */ void resize(size_t new_size){ if(new_size > size()){ ensure_capacity(new_size); // Default initialize the new elements for(size_t i = _size; i < new_size; ++i){ new (&_data[i]) value_type(); } _size = new_size; } else if(new_size < _size){ // Call the necessary destructors for(size_t i = new_size; i < _size; ++i){ _data[i].~value_type(); } //By diminishing the size, the last elements become unreachable _size = new_size; } } /*! * \brief Add an element at the back of the vector */ void push_back(value_type&& element){ ensure_capacity(_size + 1); new (&_data[_size++]) value_type(std::move(element)); } /*! * \brief Add an element at the back of the vector */ void push_back(const value_type& element){ ensure_capacity(_size + 1); new (&_data[_size++]) value_type(element); } /*! * \brief Construct a new element inplace */ value_type& emplace_back(){ ensure_capacity(_size + 1); new (&_data[_size++]) value_type(); return back(); } /*! * \brief Construct a new element inplace */ template value_type& emplace_back(Args... args){ ensure_capacity(_size + 1); new (&_data[_size++]) value_type{std::forward(args)...}; return back(); } /*! * \brief Add an element at the front of the vector */ void push_front(value_type&& element){ ensure_capacity(_size + 1); if(!empty()){ new (&_data[_size]) value_type(std::move(_data[_size - 1])); for (size_t i = _size - 1; i > 0; --i) { _data[i] = std::move(_data[i - 1]); } } // At this point _data[0] has been deleted _data[0] = std::move(element); ++_size; } /*! * \brief Add an element at the front of the vector */ void push_front(const value_type& element){ ensure_capacity(_size + 1); if(!empty()){ new (&_data[_size]) value_type(std::move(_data[_size - 1])); for (size_t i = _size - 1; i > 0; --i) { _data[i] = std::move(_data[i - 1]); } } // At this point _data[0] has been deleted _data[0] = element; ++_size; } /*! * \brief Removes the last element of the vector */ void pop_back(){ --_size; // Call the destructor of the erased value _data[_size].~value_type(); } /*! * \brief Removes all the elements of the vector */ void clear(){ destruct_all(); _size = 0; } /*! * \brief Erase the element at the given position */ void erase(size_t position){ for(size_t i = position; i < _size - 1; ++i){ _data[i] = std::move(_data[i+1]); } --_size; // Call the destructor of the last value _data[_size].~value_type(); } /*! * \brief Erase the element at the given position */ void erase(iterator position){ for(size_t i = position - begin(); i < _size - 1; ++i){ _data[i] = std::move(_data[i+1]); } --_size; // Call the destructor of the last value _data[_size].~value_type(); } /*! * \brief Erase all the elements of the given range */ void erase(iterator first, iterator last){ auto n = std::distance(first, last); for(size_t i = first - begin(); i < _size - n; ++i){ _data[i] = std::move(_data[i+n]); } // Call the destructors on the erase elements for(size_t i = _size - n; i < _size; ++i){ _data[i].~value_type(); } _size -= n; } //Iterators /*! * \brief Return an iterator to point to the first element */ iterator begin(){ return iterator(&_data[0]); } /*! * \brief Return an iterator to point to the first element */ constexpr const_iterator begin() const { return const_iterator(&_data[0]); } /*! * \brief Return an iterator to point to the past-the-end element */ iterator end(){ return iterator(&_data[_size]); } /*! * \brief Return an iterator to point to the past-the-end element */ constexpr const_iterator end() const { return const_iterator(&_data[_size]); } // Reverse Iterators /*! * \brief Return a reverse iterator to point to the first element */ reverse_iterator rbegin(){ return reverse_iterator(&_data[int64_t(_size) - 1]); } /*! * \brief Return a reverse iterator to point to the first element */ constexpr const_reverse_iterator rbegin() const { return const_iterator(&_data[int64_t(_size) - 1]); } /*! * \brief Return a reverse iterator point to the past-the-end element */ reverse_iterator rend(){ return reverse_iterator(&_data[-1]); } /*! * \brief Return a reverse iterator point to the past-the-end element */ constexpr const_reverse_iterator rend() const { return const_reverse_iterator(&_data[-1]); } // Relational operators bool operator==(const vector& rhs) const { if(size() != rhs.size()){ return false; } for(size_t i = 0; i < size(); ++i){ if((*this)[i] != rhs[i]){ return false; } } return true; } bool operator!=(const vector& rhs) const { return !(*this == rhs); } private: static value_type* allocate(size_t n){ return reinterpret_cast(new uint8_t[n * sizeof(value_type)]); } static void deallocate(value_type* ptr){ delete[] reinterpret_cast(ptr); } void destruct_all(){ // Call the destructors for(size_t i = 0; i< _size; ++i){ _data[i].~value_type(); } } void release(){ destruct_all(); // Deallocate the memory deallocate(_data); _data = nullptr; } void ensure_capacity(size_t new_capacity){ if(_capacity == 0){ _capacity = new_capacity; _data = allocate(_capacity); } else if(_capacity < new_capacity){ // Double the current capacity _capacity= _capacity * 2; // If not enough, use the given new_capacity if(new_capacity > _capacity){ _capacity = new_capacity; } auto new_data = allocate(_capacity); // Move the old _data into the new one for(size_t i = 0; i < _size; ++i){ new (&new_data[i]) value_type(std::move(_data[i])); } release(); _data = new_data; } } T* _data; ///< The data storage uint64_t _size; ///< The vector size uint64_t _capacity; ///< The data capacity }; } //end of namespace std #endif