/** * @author Edouard DUPIN * @copyright 2011, Edouard DUPIN, all right reserved * @license MPL-2 (see license file) */ #pragma once #include #include #include #include //#define ETK_ARRAY_DEBUG(...) printf(__VA_ARGS__) #define ETK_ARRAY_DEBUG(...) do {} while (false) namespace etk { class Stream; /** * @brief Array class ... * * @param[in] ETK_ARRAY_TYPE class type of the current element. * * m_data * <------------ m_dataSize ------------> * ---------------------------------------- * | 0 | * |--------------------------------------| * | 1 | * |--------------------------------------| * | 2 | * |--------------------------------------| * m_size | 3 | * |--------------------------------------| * | x | * |--------------------------------------| * | x | * |--------------------------------------| * | x | * |--------------------------------------| * | x | * |--------------------------------------| * | x | * |--------------------------------------| * | x | * |--------------------------------------| * | x | * |--------------------------------------| * ETK_ARRAY_SIZE | x | * ---------------------------------------- */ template class Array { public: class Iterator { private: size_t m_current; //!< current Id on the Array Array* m_array; //!< Pointer on the current element of the ArrayBin public: /** * @brief Basic iterator constructor with no link with an etk::Array */ Iterator(): m_current(0), m_array(null) { // nothing to do ... } /** * @brief Recopy constructor on a specific etkArray. * @param[in] _obj The Iterator that might be copy */ Iterator(const Iterator & _obj): m_current(_obj.m_current), m_array(_obj.m_array) { // nothing to do ... } /** * @brief Assignation operator. * @param[in] _otherIterator The Iterator that might be copy * @return reference on the current Iterator */ Iterator& operator=(const Iterator & _otherIterator) { m_current = _otherIterator.m_current; m_array = _otherIterator.m_array; return *this; } /** * @brief Basic destructor */ ~Iterator() { m_current = 0; m_array = null; } /** * @brief basic boolean cast * @return true if the element is present in the etkArray size */ operator bool () { return (m_current < m_array->size()); } /** * @brief != Comparaison operator * @param[in] _obj Object to compare * @return true if the element are different */ bool operator!= (const Iterator& _obj) { if ( m_array != _obj.m_array || m_current != _obj.m_current) { return true; } return false; } /** * @brief == Comparaison operator. * @param[in] _obj Object to compare. * @return true if the element are identical. */ bool operator== (const Iterator& _obj) { if ( m_array == _obj.m_array && m_current == _obj.m_current) { return true; } return false; } /** * @brief Incremental operator * @return Reference on the current iterator increment */ Iterator& operator++ () { if ( m_array != null && m_current < m_array->size() ) { m_current++; } return *this; } /** * @brief Decremental operator * @return Reference on the current iterator decrement */ Iterator& operator-- () { if ( m_array != null && m_current > 0) { m_current--; } return *this; } /** * @brief Incremental operator * @return Reference on a new iterator and increment the other one */ Iterator operator++ (int32_t) { Iterator it(*this); ++(*this); return it; } /** * @brief Decremental operator * @return Reference on a new iterator and decrement the other one */ Iterator operator-- (int32_t) { Iterator it(*this); --(*this); return it; } Iterator& operator-= (size_t _offset) { m_current -= _offset; return *this; } Iterator operator- (size_t _offset) const { Iterator tmp(*this); tmp -= _offset; return tmp; } Iterator& operator-= (int _offset) { m_current -= _offset; return *this; } Iterator operator- (int _offset) const { Iterator tmp(*this); tmp -= _offset; return tmp; } Iterator& operator-= (int64_t _offset) { m_current -= _offset; return *this; } Iterator operator- (int64_t _offset) const { Iterator tmp(*this); tmp -= _offset; return tmp; } Iterator& operator+= (size_t _offset) { m_current += _offset; return *this; } Iterator operator+ (size_t _offset) const { Iterator tmp(*this); tmp += _offset; return tmp; } Iterator& operator+= (int _offset) { m_current += _offset; return *this; } Iterator operator+ (int _offset) const { Iterator tmp(*this); tmp += _offset; return tmp; } Iterator& operator+= (int64_t _offset) { m_current += _offset; return *this; } Iterator operator+ (int64_t _offset) const { Iterator tmp(*this); tmp += _offset; return tmp; } /** * @brief Get reference on the current Element * @return the reference on the current Element */ ETK_ARRAY_TYPE* operator-> () const { return &m_array->get(m_current); } /** * @brief Get reference on the current Element * @return the reference on the current Element */ ETK_ARRAY_TYPE& operator* () const { return m_array->get(m_current); } private: Iterator(const Array * _obj, int32_t _pos): m_current(_pos), m_array(const_cast *>(_obj)) { // nothing to do ... } size_t getCurrent() const { return m_current; } friend class Array; }; private: uint8_t m_rawData[ETK_ARRAY_SIZE*sizeof(ETK_ARRAY_TYPE)]; //!< pointer on the current table of Data (static allocated ==> can not change) ETK_ARRAY_TYPE* m_data = (ETK_ARRAY_TYPE*)m_rawData; //!< pointer on the current table of Data (static allocated ==> can not change) size_t m_size = 0; //!< Number of element in the buffer const size_t m_allocated = ETK_ARRAY_SIZE; //!< Current allocated size public: /** * @brief Create an empty Array */ Array() { } /** * @brief List initializer (ex: etk::Array plop = {"hello", world"} * @param[in] _element element to add in the Array */ template Array(const ETK_ARRAY_TYPE_2& ... _args) { int32_t nbElement = int32_t(sizeof...(ETK_ARRAY_TYPE_2)); if (nbElement >= m_allocated) { ETK_THROW_EXCEPTION(etk::exception::InvalidArgument("Size too big ...")); return; } pushBackN(_args...); } /** * @brief Re-copy constructor (copy all needed data) * @param[in] _obj Array that might be copy */ Array(const etk::Array& _obj) { for(size_t iii=0; iii<_obj.m_size; iii++) { new ((char*)&m_data[iii]) ETK_ARRAY_TYPE(etk::move(_obj.m_data[iii])); } m_size = _obj.m_size; } /** * @brief Move operator of elements * @param[in] _obj Object to move */ // TODO: This is a little complicated ... Array(etk::Array&& _obj) { swap(_obj); } /** * @brief Destructor of the current class */ ~Array() { for(size_t iii=0; iii& _obj) { // avoid Swap of itself if(this != &_obj) { if (_obj.size() < size()) { _obj.swap(*this); return; } for (size_t iii=0; iii&& _obj) { if(this != &_obj) { etk::swap(this, _obj); } // Return the current pointer return *this; } /** * @brief Re-copy operator * @param[in] _obj Array that might be copy * @return reference on the current re-copy Array */ Array& operator=(const etk::Array& _obj) { // remove all previous elements clear(); for(size_t iii=0; iii<_obj.m_size; iii++) { new ((char*)&m_data[iii]) ETK_ARRAY_TYPE(etk::move(_obj.m_data[iii])); } m_size = _obj.m_size; // Return the current pointer return *this; } /** * @brief Get the number of element in the Array * @return The number requested */ size_t size() const { return m_size; } /** * @brief Check if the container have some element * @return true The container is empty * @return famse The container have some element */ bool empty() const { return m_size == 0; } /** * @brief Get the Allocated size in the Array * @return The size of allocation */ size_t allocatedSize() const { return m_allocated; } /** * @brief Get a current element in the Array * @param[in] _pos Desired position read * @return Reference on the Element */ ETK_ARRAY_TYPE& get(const size_t _pos) { return m_data[_pos]; } /** * @brief Get an copy Element an a special position * @param[in] _pos Position in the Array that might be get [0..Size()] * @return An reference on the copy of selected element */ ETK_ARRAY_TYPE& operator[] (const size_t _pos) { return m_data[_pos]; } /** * @brief Get an Element an a special position * @param[in] _pos Position in the Array that might be get [0..Size()] * @return An reference on the selected element */ const ETK_ARRAY_TYPE& operator[] (const size_t _pos) const { return m_data[_pos]; } /** * @brief Get the last element of the Array * @return An reference on the element */ ETK_ARRAY_TYPE& back() { return m_data[m_size-1]; } const ETK_ARRAY_TYPE& back() const { return m_data[m_size-1]; } /** * @brief Get the first element of the Array * @return An reference on the element */ ETK_ARRAY_TYPE& front() { return m_data[0]; } const ETK_ARRAY_TYPE& front() const { return m_data[0]; } /** * @brief Add at the First position of the Array * @param[in] _item Element to add at the end of Array */ void pushFront(const ETK_ARRAY_TYPE& _item) { insert(0, &_item, 1); } /** * @brief Add at the Last position of the Array * @param[in] _item Pointer on a list of Element to add at the start of Array * @param[in] _nbElement Number of element to add. */ void pushFront(const ETK_ARRAY_TYPE * _item, size_t _nbElement) { insert(0, _item, _nbElement); } /** * @brief Add at the Last position of the Array (move push) * @param[in] _item Element to add at the end of Array */ void pushBack(ETK_ARRAY_TYPE&& _item) { if (m_size == ETK_ARRAY_SIZE) { ETK_THROW_EXCEPTION(etk::exception::OverflowError("try add to much data in array")); } new ((char*)&m_data[m_size]) ETK_ARRAY_TYPE(etk::move(_item)); m_size += 1; } /** * @brief Add at the Last position of the Array * @param[in] _item Element to add at the end of Array */ void pushBack(const ETK_ARRAY_TYPE& _item) { if (m_size == ETK_ARRAY_SIZE) { ETK_THROW_EXCEPTION(etk::exception::OverflowError("try add to much data in array")); } new ((char*)&m_data[m_size]) ETK_ARRAY_TYPE(etk::move(_item)); m_size += 1; } /** * @brief Add at the Last position of the Array * @param[in] _item Pointer on a list of Element to add at the end of Array * @param[in] _nbElement Number of element to add. */ void pushBack(const ETK_ARRAY_TYPE * _item, size_t _nbElement) { if (_item == null) { return; } if (m_size+_nbElement > ETK_ARRAY_SIZE) { ETK_THROW_EXCEPTION(etk::exception::OverflowError("try add to much data in array")); } for (size_t iii=0; iii<_nbElement; iii++) { new ((char*)&m_data[m_size+iii]) ETK_ARRAY_TYPE(_item[iii]); m_size += 1; } } private: void pushBackN(const ETK_ARRAY_TYPE& _value) { pushBack(_value); } template void pushBackN(const ETK_ARRAY_TYPE& _value, const ETK_ARRAY_TYPE_2& ... _args) { pushBack(_value); pushBackN(_args...); } public: /** * @brief Remove the first element of the Array */ void popFront() { if(m_size>0) { eraseLen(0, 1); } } /** * @brief Remove the last element of the Array */ void popBack() { if(m_size>0) { resizeDown(m_size-1); } } /** * @brief Remove all element in the current Array */ void clear() { if(m_size>0) { resizeDown(0); } } /** * @brief Insert N element in the Array. * @param[in] _pos Position to add the elements. * @param[in] _item Pointer on a table of the elements to add. * @param[in] _nbElement Number of element to add in the Array */ void insert(size_t _pos, const ETK_ARRAY_TYPE * _item, size_t _nbElement) { if (_pos > m_size) { //TK_WARNING(" can not insert Element at this position : " << _pos << " > " << m_size << " add it at the end ... "); pushBack(_item, _nbElement); return; } // move current data (after the position) size_t sizeToMove = (m_size - _pos); if (sizeToMove > 0) { for (size_t iii=1; iii<=sizeToMove; iii++) { // placement allocate of the element new ((char*)&m_data[m_size+_nbElement-iii]) ETK_ARRAY_TYPE(etk::move(m_data[m_size-iii])); // Remove previous element ==> simplify code. m_data[m_size-iii].~ETK_ARRAY_TYPE(); #ifdef DEBUG // we place bad data to permit to detect stipid thing that is done in C++ code when developement is in progress. // Only in debug this is really slow ... and for the real allocation of memory for (size_t kkk=(m_size-iii)*sizeof(ETK_ARRAY_TYPE); kkk m_size) { //TK_ERROR(" can not Erase Len Element at this position : " << _pos << " > " << m_size); return; } if (m_size+_nbElement > ETK_ARRAY_SIZE) { ETK_THROW_EXCEPTION(etk::exception::OverflowError("try add to much data in array")); } if (_pos + _nbElement > m_size) { _nbElement = m_size - _pos; } ETK_ARRAY_DEBUG("Erase len %zu %zu\n", _pos, _nbElement); size_t idElement = m_size; // move current data size_t sizeToMove = (idElement - (_pos+_nbElement)); if ( 0 < sizeToMove) { for (size_t iii=0; iii m_size) { //TK_ERROR(" can not Erase Element at this position : " << _pos << " > " << m_size); return; } if (_posEnd > m_size) { _posEnd = m_size; } size_t nbElement = m_size - _pos; size_t tmpSize = m_size; // move current data (to the end) ==> auto removed by the resize() size_t sizeToMove = (tmpSize - (_pos+nbElement)); if ( 0 < sizeToMove) { for (size_t iii=0; iii ETK_ARRAY_SIZE) { ETK_THROW_EXCEPTION(etk::exception::OverflowError("try resize with larger size in array")); } // Reallocate memory if (_newSize > m_size) { for (size_t iii=m_size; iii<_newSize; ++iii) { new ((char*)&m_data[iii]) ETK_ARRAY_TYPE(_basicElement); } m_size = _newSize; return; } else if (_newSize == m_size) { return; } for (size_t iii=m_size; iii<_newSize; ++iii) { m_data[iii].~ETK_ARRAY_TYPE(); #ifdef DEBUG // we place bad data to permit to detect stipid thing that is done in C++ code when developement is in progress. // Only in debug this is really slow ... and for the real allocation of memory for (size_t kkk=iii*sizeof(ETK_ARRAY_TYPE); kkk %zu\n", m_size, _newSize); if (_newSize > ETK_ARRAY_SIZE) { ETK_THROW_EXCEPTION(etk::exception::OverflowError("try resize with larger size in array")); } // Reallocate memory if (_newSize > m_size) { for (size_t iii=m_size; iii<_newSize; ++iii) { new ((char*)&m_data[iii]) ETK_ARRAY_TYPE(); } } else if (_newSize == m_size) { return; } ETK_ARRAY_DEBUG("Reduce %zu => %zu\n", m_size, _newSize); for (size_t iii=_newSize; iii %zu\n", m_size, _newSize); for (size_t iii=_newSize; iii& _obj) const { // check if it was the same pointer if( this == &_obj ) { return true; } // first step : check the size ... if (m_size != _obj.m_size) { return false; } if( m_data == null || _obj.m_data == null) { return false; } for (size_t iii=0; iii& _obj) const { // check if it was the same pointer if( this == &_obj ) { return false; } // first step : check the size ... if (m_size != _obj.m_size) { return true; } if( m_data == null || _obj.m_data == null) { return false; } for (size_t iii=0; iii= operator *****************************************************/ bool operator>= (const Array& _obj) const { // TODO : Later return false; } /***************************************************** * > operator *****************************************************/ bool operator> (const Array& _obj) const { // TODO : Later return false; } /***************************************************** * <= operator *****************************************************/ bool operator<= (const Array& _obj) const { // TODO : Later return false; } /***************************************************** * > operator *****************************************************/ bool operator< (const Array& _obj) const { // TODO : Later return false; } void sort(size_t _start, size_t _stop, bool (*_comparator)(const ETK_ARRAY_TYPE&, const ETK_ARRAY_TYPE&)) { if (_stop > m_size) { _stop = m_size; } if (_start > m_size) { _start = m_size; } if (_start > _stop) { size_t start = _start; _start = _stop; _stop = start; } for (size_t iii=_start; iii<_stop; ++iii) { bool swapped = false; for (size_t jjj=_start; jjj<_stop - (iii+1); ++jjj) { if (_comparator(m_data[jjj], m_data[jjj+1]) == false) { etk::swap(m_data[jjj], m_data[jjj+1]); swapped = true; } } if (swapped == false) { break; } } } }; //! @not_in_doc template bool isIn(const ETK_ARRAY_TYPE_1& _val, const etk::Array& _list) { for (size_t iii=0; iii<_list.size(); ++iii) { if (_list[iii] == _val) { return true; } } return false; } class Stream; //! @not_in_doc template etk::Stream& operator <<(etk::Stream& _os, const etk::Array& _obj) { _os << "{"; for (size_t iii=0; iii< _obj.size(); iii++) { if (iii>0) { _os << ";"; } _os << _obj[iii]; } _os << "}"; return _os; } }