[DEV] start add Array

This commit is contained in:
Edouard DUPIN 2018-06-18 22:58:41 +02:00
parent 966f431549
commit f92f9fb54e
5 changed files with 1289 additions and 1 deletions

999
etk/Array.hpp Normal file
View File

@ -0,0 +1,999 @@
/**
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license MPL-2 (see license file)
*/
#pragma once
#include <etk/types.hpp>
#include <etk/Stream.hpp>
#include <etk/Allocator.hpp>
//#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 ETK_ARRAY_TYPE, size_t ETK_ARRAY_SIZE=1> class Array {
public:
class Iterator {
private:
size_t m_current; //!< current Id on the Array
Array<ETK_ARRAY_TYPE, ETK_ARRAY_SIZE>* 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(nullptr) {
// 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 = nullptr;
}
/**
* @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 != nullptr
&& m_current < m_array->size() )
{
m_current++;
}
return *this;
}
/**
* @brief Decremental operator
* @return Reference on the current iterator decrement
*/
Iterator& operator-- () {
if ( m_array != nullptr
&& 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<ETK_ARRAY_TYPE> * _obj, int32_t _pos):
m_current(_pos),
m_array(const_cast<Array<ETK_ARRAY_TYPE> *>(_obj)) {
// nothing to do ...
}
size_t getCurrent() const {
return m_current;
}
friend class Array;
};
private:
ETK_ARRAY_TYPE m_data[ETK_ARRAY_SIZE]; //!< 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<etk::String> plop = {"hello", world"}
* @param[in] _element element to add in the Array
*/
template<typename... ETK_ARRAY_TYPE_2>
Array(const ETK_ARRAY_TYPE_2& ... _args) {
int32_t nbElement = int32_t(sizeof...(ETK_ARRAY_TYPE_2));
if (nbElement >= m_allocated) {
throw etk::exception::InvalidArgument("Size too big ...");
return;
}
changeAllocation(int32_t(sizeof...(ETK_ARRAY_TYPE_2)));
pushBackN(_args...);
}
/**
* @brief Re-copy constructor (copy all needed data)
* @param[in] _obj Array that might be copy
*/
Array(const etk::Array<ETK_ARRAY_TYPE, ETK_ARRAY_SIZE>& _obj) {
reserve(_obj.m_size);
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<ETK_ARRAY_TYPE, ETK_ARRAY_SIZE>&& _obj):
m_size(_obj.m_size) {
/*
_obj.m_data = nullptr;
_obj.m_size = 0;
_obj.m_allocated = 0;
*/
throw etk::exception::InvalidArgument("TODO ...");
}
/**
* @brief Destructor of the current class
*/
~Array() {
for(size_t iii=0; iii<m_size; 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<sizeof(ETK_ARRAY_TYPE)*(iii+1); ++kkk) {
((char*)m_data)[kkk] = 0xA5;
}
#endif
}
m_size = 0;
}
/**
* @brief Swap the data of 2 Arrays
* @param[in] _obj second Array to swap data.
*/
void swap(etk::Array<ETK_ARRAY_TYPE, ETK_ARRAY_SIZE>& _obj) {
// avoid Swap of itself
if(this != &_obj) {
if (_obj.size() < size()) {
_obj.swap(*this);
return;
}
for (size_t iii=0; iii<size(); ++iii) {
etk::swap(m_data[iii], _obj.m_data[iii]);
}
for (size_t iii=size(); iii<_obj.size(); ++iii) {
// Construc new local object
new ((char*)&m_data[iii]) ETK_ARRAY_TYPE(etk::move(_obj.m_data[iii]));
// destruct remote object
_obj.m_data[iii].~ETK_ARRAY_TYPE();
}
etk::swap(m_size, _obj.m_size);
}
}
/**
* @brief Move operator
* @param[in] _obj Array that might be copy
* @return reference on the current re-copy Array
*/
Array& operator=(etk::Array<ETK_ARRAY_TYPE, ETK_ARRAY_SIZE>&& _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<ETK_ARRAY_TYPE, ETK_ARRAY_SIZE>& _obj) {
// remove all previous elements
clear();
// Force a specicfic size
reserve(_obj.m_size);
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 Add at the Last position of the Array
* @param[in] _obj Element to add at the end of Array
*/
Array& operator+= (const etk::Array<ETK_ARRAY_TYPE>& _obj) {
reserve(m_size + _obj.size());
for(size_t iii=0; iii<_obj.size(); iii++) {
// copy operator ...
new ((char*)&m_data[m_size+iii]) ETK_ARRAY_TYPE(etk::move(_obj.m_data[iii]));
}
m_size += _obj.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) {
reserve(m_size+1);
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) {
reserve(m_size+1);
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 == nullptr) {
return;
}
reserve(m_size+_nbElement);
if (m_size > m_allocated) {
//TK_ERROR("Resize does not work correctly ... not added item");
return;
}
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<typename... ETK_ARRAY_TYPE_2>
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);
// Request resize of the current buffer
reserve(m_size+_nbElement);
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<sizeof(ETK_ARRAY_TYPE)*((m_size-iii)+1); ++kkk) {
((char*)m_data)[kkk] = 0xA5;
}
#endif
}
}
// affectation of all input element
for (size_t iii=0; iii<_nbElement; iii++) {
new ((char*)&m_data[_pos-iii]) ETK_ARRAY_TYPE(etk::move(_item[iii]));
}
m_size += _nbElement;
}
/**
* @brief Insert one element in the Array at a specific position
* @param[in] _pos Position to add the elements.
* @param[in] _item Element to add.
*/
void insert(size_t _pos, const ETK_ARRAY_TYPE& _item) {
insert(_pos, &_item, 1);
}
/**
* @brief Insert one element in the Array at a specific position
* @param[in] _pos Position to add the elements.
* @param[in] _item Element to add.
*/
void insert(const Iterator& _pos, const ETK_ARRAY_TYPE& _item) {
insert(_pos.getCurrent(), _item);
}
/**
* @brief Remove N element
* @param[in] _pos Position to remove the data
* @param[in] _nbElement number of element to remove
*/
void eraseLen(size_t _pos, size_t _nbElement) {
ETK_ARRAY_DEBUG("Erase len %zu %zu\n", _pos, _nbElement);
if (_pos > m_size) {
//TK_ERROR(" can not Erase Len Element at this position : " << _pos << " > " << m_size);
return;
}
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<sizeToMove; iii++) {
etk::swap(m_data[_pos+iii], m_data[_pos+_nbElement+iii]);
}
}
// Request resize of the current buffer
resizeDown(m_size-_nbElement);
}
/**
* @brief Remove one element
* @param[in] _pos Position to remove the data
*/
inline void erase(size_t _pos) {
eraseLen(_pos, 1);
}
/**
* @brief Remove one element
* @param[in] _it Iterator on the element to remove
* @return An iterator on the new element at this position.
*/
Iterator erase(const Iterator& _it) {
ETK_ARRAY_DEBUG("Erase IT \n");
eraseLen(_it.getCurrent(), 1);
return position(_it.getCurrent());
}
/**
* @brief Remove one element
* @param[in] _pos Position to remove the data
*/
inline void remove(size_t _pos) {
eraseLen(_pos, 1);
}
/**
* @brief Remove N elements
* @param[in] _pos Position to remove the data
* @param[in] _posEnd Last position number
*/
void erase(size_t _pos, size_t _posEnd) {
if (_pos > 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<sizeToMove; iii++) {
etk::swap(m_data[_pos+iii], m_data[_pos+nbElement+iii]);
}
}
// Request resize of the current buffer
resizeDown(m_size-nbElement);
}
void erase(const Iterator& _pos, const Iterator& _posEnd) {
erase(_pos.getCurrent(), _posEnd.getCurrent());
}
/**
* @brief Get the pointer on the data
* @return the type pointer on data
*/
ETK_ARRAY_TYPE* dataPointer() {
return &m_data[0];
}
/**
* @brief Get an iterator an an specific position
* @param[in] _pos Requested position of the iterator in the Array
* @return The Iterator
*/
Iterator position(size_t _pos) {
return Iterator(this, _pos);
}
const Iterator position(size_t _pos) const {
return Iterator(this, _pos);
}
/**
* @brief Get an Iterator on the start position of the Array
* @return The Iterator
*/
Iterator begin() {
return position(0);
}
const Iterator begin() const {
return position(0);
}
/**
* @brief Get an Iterator on the end position of the Array
* @return The Iterator
*/
Iterator end() {
return position(size());
}
const Iterator end() const {
return position(size());
}
/**
* @brief Resize the Array with a basic element
* @param[in] _newSize New size of the Array
*/
void resize(size_t _newSize, const ETK_ARRAY_TYPE& _basicElement) {
// Reallocate memory
if (_newSize > m_size) {
if (_newSize > m_allocated) {
changeAllocation(_newSize);
}
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<sizeof(ETK_ARRAY_TYPE)*(iii+1); ++kkk) {
((char*)m_data)[kkk] = 0xA5;
}
#endif
}
m_size = _newSize;
}
/**
* @brief Change the current size of the Array
* @param[in] _newSize New requested size of element in the Array
*/
void resize(size_t _newSize) {
ETK_ARRAY_DEBUG("Resize %zu => %zu\n", m_size, _newSize);
// Reallocate memory
if (_newSize > m_size) {
if (_newSize > m_allocated) {
changeAllocation(_newSize);
}
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<m_size; ++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<sizeof(ETK_ARRAY_TYPE)*(iii+1); ++kkk) {
((char*)m_data)[kkk] = 0xA5;
}
#endif
}
m_size = _newSize;
}
private:
void resizeDown(size_t _newSize) {
ETK_ARRAY_DEBUG("Resize %zu => %zu\n", m_size, _newSize);
// Reallocate memory
if (_newSize > m_allocated) {
ETK_ARRAY_DEBUG("Resize Down %zu => %zu\n", m_size, _newSize);
return;
} else if (_newSize == m_allocated) {
return;
}
ETK_ARRAY_DEBUG("Reduce %zu => %zu\n", m_size, _newSize);
for (size_t iii=_newSize; iii<m_size; ++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<sizeof(ETK_ARRAY_TYPE)*(iii+1); ++kkk) {
((char*)m_data)[kkk] = 0xA5;
}
#endif
}
m_size = _newSize;
}
public:
/**
* @brief Force the container to have a minimum size in memory allocation
* @param[in] _size Size in byte that is requested.
*/
void reserve(size_t _size) {
if (_size <= m_allocated) {
return;
}
changeAllocation(_size);
}
private:
/**
* @brief Change the current allocation to the correct one (depend on the current size)
* @param[in] _newSize Minimum number of element needed
*/
void changeAllocation(size_t _newSize) {
size_t requestSize = m_allocated;
// set the size with the correct chose type:
if (_newSize == requestSize) {
return;
} else if (_newSize < requestSize) {
// we did not remove data ???
} else {
while(_newSize > requestSize) {
if (0 == requestSize) {
requestSize = 1;
} else {
requestSize = requestSize * 2;
}
}
}
// No reallocation needed :
if (requestSize <= m_allocated) {
return;
}
//TK_INFO("Change Array allocation : " << m_allocated << "==>" << requestSize);
// check if something is allocated :
if (m_data == nullptr) {
// no data allocated ==> request an allocation (might be the first)
m_data = (ETK_ARRAY_TYPE*)ETK_MALLOC(char, sizeof(ETK_ARRAY_TYPE)*requestSize);
if (m_data == nullptr) {
//TK_CRITICAL("Array : Error in data allocation request allocation:" << requestSize << "*" << (int32_t)(sizeof(ETK_ARRAY_TYPE)) << "bytes" );
m_allocated = 0;
return;
}
#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=0; kkk<sizeof(ETK_ARRAY_TYPE)*requestSize; ++kkk) {
((char*)m_data)[kkk] = 0x5A;
}
#endif
} else {
// allocate a new pool of data:
ETK_ARRAY_TYPE* dataTmp = (ETK_ARRAY_TYPE*)ETK_MALLOC(char, sizeof(ETK_ARRAY_TYPE)*requestSize);;
if (dataTmp == nullptr) {
//TK_CRITICAL("Array : Error in data allocation request allocation:" << requestSize << "*" << (int32_t)(sizeof(ETK_ARRAY_TYPE)) << "bytes" );
return;
}
ETK_MEM_FLIP_ID(dataTmp, m_data);
#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=0; kkk<sizeof(ETK_ARRAY_TYPE)*requestSize; ++kkk) {
((char*)dataTmp)[kkk] = 0x5A;
}
#endif
// copy data in the new pool
size_t nbElements = etk::min(requestSize, m_allocated);
for(size_t iii=0; iii<nbElements; iii++) {
// Move in the new element
new ((char*)&dataTmp[iii]) ETK_ARRAY_TYPE(etk::move(m_data[iii]));
// Remove the old one.
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<sizeof(ETK_ARRAY_TYPE)*(iii+1); ++kkk) {
((char*)m_data)[kkk] = 0xA5;
}
#endif
}
// switch pointer:
ETK_ARRAY_TYPE* dataTmp2 = m_data;
m_data = dataTmp;
// remove old pool
if (dataTmp2 != nullptr) {
ETK_FREE(char, dataTmp2);
}
}
// set the new allocation size
m_allocated = requestSize;
}
public :
/*****************************************************
* == operator
*****************************************************/
bool operator== (const Array<ETK_ARRAY_TYPE, ETK_ARRAY_SIZE>& _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 == nullptr
|| _obj.m_data == nullptr) {
return false;
}
for (size_t iii=0; iii<m_size; iii++) {
if (m_data[iii]!=_obj.m_data[iii]) {
return false;
}
}
return true;
}
/*****************************************************
* != operator
*****************************************************/
bool operator!= (const Array<ETK_ARRAY_TYPE, ETK_ARRAY_SIZE>& _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 == nullptr
|| _obj.m_data == nullptr) {
return false;
}
for (size_t iii=0; iii<m_size; iii++) {
if (m_data[iii]!=_obj.m_data[iii]) {
return true;
}
}
return false;
}
/*****************************************************
* >= operator
*****************************************************/
bool operator>= (const Array<ETK_ARRAY_TYPE, ETK_ARRAY_SIZE>& _obj) const {
// TODO : Later
return false;
}
/*****************************************************
* > operator
*****************************************************/
bool operator> (const Array<ETK_ARRAY_TYPE, ETK_ARRAY_SIZE>& _obj) const {
// TODO : Later
return false;
}
/*****************************************************
* <= operator
*****************************************************/
bool operator<= (const Array<ETK_ARRAY_TYPE, ETK_ARRAY_SIZE>& _obj) const {
// TODO : Later
return false;
}
/*****************************************************
* > operator
*****************************************************/
bool operator< (const Array<ETK_ARRAY_TYPE, ETK_ARRAY_SIZE>& _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<typename ETK_ARRAY_TYPE_1, typename ETK_ARRAY_TYPE_2, size_t ETK_ARRAY_SIZE>
bool isIn(const ETK_ARRAY_TYPE_1& _val, const etk::Array<ETK_ARRAY_TYPE_2, ETK_ARRAY_SIZE>& _list) {
for (size_t iii=0; iii<_list.size(); ++iii) {
if (_list[iii] == _val) {
return true;
}
}
return false;
}
class Stream;
//! @not_in_doc
template<class ETK_ARRAY_TYPE, size_t ETK_ARRAY_SIZE>
etk::Stream& operator <<(etk::Stream& _os, const etk::Array<ETK_ARRAY_TYPE, ETK_ARRAY_SIZE>& _obj) {
_os << "{";
for (size_t iii=0; iii< _obj.size(); iii++) {
if (iii>0) {
_os << ";";
}
_os << _obj[iii];
}
_os << "}";
return _os;
}
}

View File

@ -9,7 +9,6 @@
namespace etk {
//class String;
/**
* @brief Global exception handle
*/

View File

@ -47,6 +47,7 @@ def configure(target, my_module):
'etk/String.hpp',
'etk/UString.hpp',
'etk/utf8.hpp',
'etk/Array.hpp',
'etk/Vector.hpp',
'etk/Stream.hpp',
'etk/Pair.hpp',

View File

@ -35,6 +35,7 @@ def configure(target, my_module):
'test/testMatrix3x3.cpp',
'test/testRegExp.cpp',
'test/testTransform.cpp',
'test/testArray.cpp',
'test/testVector.cpp',
'test/testVector3_f.cpp',
'test/testArchive.cpp',

288
test/testArray.cpp Normal file
View File

@ -0,0 +1,288 @@
/**
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file)
*/
#include <etk/Pair.hpp>
#include <etk/Array.hpp>
#include <test-debug/debug.hpp>
#include <etest/etest.hpp>
TEST(TestArray, constructor) {
// Test contructor value
etk::Array<float, 20> test;
EXPECT_EQ(test.size(), 0);
}
TEST(TestArray, constructor_2) {
// Test contructor value
etk::Array<float, 20> test(3.9f, 33.0f,55.8f);
EXPECT_EQ(test.size(), 3);
EXPECT_EQ(test[0], 3.9f);
EXPECT_EQ(test[1], 33.0f);
EXPECT_EQ(test[2], 55.8f);
}
TEST(TestArray, resize) {
// Test contructor value
etk::Array<float, 20> test;
test.resize(3);
EXPECT_EQ(test.size(), 3);
}
TEST(TestArray, empty) {
// Test contructor value
etk::Array<float, 20> test;
EXPECT_EQ(test.empty(), true);
test.pushBack(2);
EXPECT_EQ(test.empty(), false);
}
TEST(TestArray, pushBack) {
// Test contructor value
etk::Array<float, 20> test;
EXPECT_EQ(test.size(), 0);
test.pushBack(2);
EXPECT_EQ(test.size(), 1);
EXPECT_EQ(test[0], 2);
test.pushBack(4);
EXPECT_EQ(test.size(), 2);
EXPECT_EQ(test[0], 2);
EXPECT_EQ(test[1], 4);
test.pushBack(8);
EXPECT_EQ(test.size(), 3);
EXPECT_EQ(test[0], 2);
EXPECT_EQ(test[1], 4);
EXPECT_EQ(test[2], 8);
test.popBack();
EXPECT_EQ(test.size(), 2);
EXPECT_EQ(test[0], 2);
EXPECT_EQ(test[1], 4);
}
TEST(TestArray, pushFront) {
// Test contructor value
etk::Array<float, 20> test;
EXPECT_EQ(test.size(), 0);
test.pushBack(2);
EXPECT_EQ(test.size(), 1);
EXPECT_EQ(test[0], 2);
test.pushBack(4);
EXPECT_EQ(test.size(), 2);
EXPECT_EQ(test[0], 2);
EXPECT_EQ(test[1], 4);
test.pushFront(8);
EXPECT_EQ(test.size(), 3);
EXPECT_EQ(test[0], 8);
EXPECT_EQ(test[1], 2);
EXPECT_EQ(test[2], 4);
test.pushFront(55);
EXPECT_EQ(test.size(), 4);
EXPECT_EQ(test[0], 55);
EXPECT_EQ(test[1], 8);
EXPECT_EQ(test[2], 2);
EXPECT_EQ(test[3], 4);
}
TEST(TestArray, insert) {
// Test contructor value
etk::Array<float, 20> test;
EXPECT_EQ(test.size(), 0);
test.pushBack(2);
EXPECT_EQ(test.size(), 1);
EXPECT_EQ(test[0], 2);
test.pushBack(4);
EXPECT_EQ(test.size(), 2);
EXPECT_EQ(test[0], 2);
EXPECT_EQ(test[1], 4);
test.pushFront(8);
EXPECT_EQ(test.size(), 3);
EXPECT_EQ(test[0], 8);
EXPECT_EQ(test[1], 2);
EXPECT_EQ(test[2], 4);
test.pushFront(55);
EXPECT_EQ(test.size(), 4);
EXPECT_EQ(test[0], 55);
EXPECT_EQ(test[1], 8);
EXPECT_EQ(test[2], 2);
EXPECT_EQ(test[3], 4);
}
TEST(TestArray, back) {
// Test contructor value
etk::Array<float, 20> test;
EXPECT_EQ(test.size(), 0);
test.pushBack(2);
test.pushBack(4);
test.pushBack(8);
EXPECT_EQ(test.size(), 3);
EXPECT_EQ(test[0], 2);
EXPECT_EQ(test[1], 4);
EXPECT_EQ(test[2], 8);
EXPECT_EQ(test.back(), 8);
}
TEST(TestArray, popBack) {
// Test contructor value
etk::Array<float, 20> test;
EXPECT_EQ(test.size(), 0);
test.pushBack(2);
test.pushBack(4);
test.pushBack(8);
EXPECT_EQ(test.size(), 3);
EXPECT_EQ(test[0], 2);
EXPECT_EQ(test[1], 4);
EXPECT_EQ(test[2], 8);
test.popBack();
EXPECT_EQ(test.size(), 2);
EXPECT_EQ(test[0], 2);
EXPECT_EQ(test[1], 4);
}
TEST(TestArray, front) {
// Test contructor value
etk::Array<float, 20> test;
EXPECT_EQ(test.size(), 0);
test.pushBack(2);
test.pushBack(4);
test.pushBack(8);
EXPECT_EQ(test.size(), 3);
EXPECT_EQ(test[0], 2);
EXPECT_EQ(test[1], 4);
EXPECT_EQ(test[2], 8);
EXPECT_EQ(test.front(), 2);
}
TEST(TestArray, popFront) {
// Test contructor value
etk::Array<float, 20> test;
EXPECT_EQ(test.size(), 0);
test.pushBack(2);
test.pushBack(4);
test.pushBack(8);
EXPECT_EQ(test.size(), 3);
EXPECT_EQ(test[0], 2);
EXPECT_EQ(test[1], 4);
EXPECT_EQ(test[2], 8);
test.popFront();
EXPECT_EQ(test.size(), 2);
EXPECT_EQ(test[0], 4);
EXPECT_EQ(test[1], 8);
}
TEST(TestArray, initializationList_1) {
// Test contructor value
etk::Array<int, 20> test = {1, 5, 6, 8};
EXPECT_EQ(test.size(), 4);
EXPECT_EQ(test[0], 1);
EXPECT_EQ(test[1], 5);
EXPECT_EQ(test[2], 6);
EXPECT_EQ(test[3], 8);
}
TEST(TestArray, initializationList_2) {
// Test contructor value
etk::Array<int, 20> test(1, 5, 6, 8);
EXPECT_EQ(test.size(), 4);
EXPECT_EQ(test[0], 1);
EXPECT_EQ(test[1], 5);
EXPECT_EQ(test[2], 6);
EXPECT_EQ(test[3], 8);
}
static uint32_t isDestroy = 0;
class testContructDestruct {
private:
uint32_t m_addValue;
public:
testContructDestruct(uint32_t _addValue):
m_addValue(_addValue) {
isDestroy += m_addValue;
TEST_DEBUG("Create class " << m_addValue);
}
testContructDestruct(testContructDestruct&& _obj):
m_addValue(_obj.m_addValue) {
_obj.m_addValue = 0;
TEST_DEBUG("move contruction " << m_addValue);
}
virtual ~testContructDestruct() {
if (m_addValue == 0) {
TEST_DEBUG("Remove class (after move)");
return;
}
TEST_DEBUG("Remove Class " << m_addValue);
isDestroy -= m_addValue;
}
testContructDestruct& operator= (testContructDestruct&& _obj) {
TEST_DEBUG("move operator " << m_addValue);
if (this != &_obj) {
etk::swap(m_addValue, _obj.m_addValue);
}
return *this;
}
};
TEST(TestArray, destroyElementAtTheCorectMoment) {
isDestroy = 0;
{
etk::Array<testContructDestruct, 20> list;
list.pushBack(testContructDestruct(55));
EXPECT_EQ(list.size(), 1);
EXPECT_EQ(isDestroy, 55);
auto it = list.erase(list.begin());
EXPECT_EQ(isDestroy, 0);
EXPECT_EQ(list.size(), 0);
EXPECT_EQ(it, list.end());
}
EXPECT_EQ(isDestroy, 0);
}
TEST(TestArray, destroyElementAtTheCorectMoment_2) {
isDestroy = 0;
{
etk::Array<testContructDestruct, 20> list;
list.pushBack(testContructDestruct(4));
list.pushBack(testContructDestruct(30));
list.pushBack(testContructDestruct(1000));
list.pushBack(testContructDestruct(200));
EXPECT_EQ(list.size(), 4);
EXPECT_EQ(isDestroy, 1234);
auto it = list.erase(list.begin());
EXPECT_EQ(list.size(), 3);
EXPECT_EQ(isDestroy, 1230);
it = list.erase(list.begin()+1);
EXPECT_EQ(isDestroy, 230);
EXPECT_EQ(list.size(), 2);
}
EXPECT_EQ(isDestroy, 0);
}
TEST(TestArray, allocateElementAtTheCorectMoment) {
isDestroy = 0;
{
etk::Array<testContructDestruct, 20> list;
list.reserve(10);
EXPECT_EQ(list.size(), 0);
EXPECT_EQ(isDestroy, 0);
}
EXPECT_EQ(isDestroy, 0);
}
/*
TEST(TestArray, allocateBench) {
while (1) {
etk::Array<uint8_t, 20> list;
//list.reserve(1024*1024);
for (size_t iii=0; iii < 1024*1024; ++iii) {
list.pushBack(66);
}
EXPECT_EQ(list.size(), 1024*1024);
}
}
*/