871 lines
20 KiB
C++
871 lines
20 KiB
C++
/*
|
|
* $Id$
|
|
*
|
|
Copyright (c) 2006-2014 Chung, Hyung-Hwan. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef _QSE_CMN_ARRAY_HPP_
|
|
#define _QSE_CMN_ARRAY_HPP_
|
|
|
|
/// \file
|
|
/// Provide the Array class.
|
|
|
|
#include <qse/Growable.hpp>
|
|
#include <qse/cmn/Mmged.hpp>
|
|
|
|
/////////////////////////////////
|
|
QSE_BEGIN_NAMESPACE(QSE)
|
|
/////////////////////////////////
|
|
|
|
template <typename T>
|
|
struct ArrayPositioner
|
|
{
|
|
void operator() (T& v, qse_size_t index) const
|
|
{
|
|
// do nothing
|
|
}
|
|
};
|
|
|
|
struct ArrayResizer
|
|
{
|
|
qse_size_t operator() (qse_size_t current, const GrowthPolicy* gp) const
|
|
{
|
|
if (current <= 0) current = 1;
|
|
|
|
if (gp)
|
|
{
|
|
return gp->getNewSize (current);
|
|
}
|
|
else
|
|
{
|
|
return (current < 5000)? (current + current):
|
|
(current < 50000)? (current + (current / 2)):
|
|
(current < 100000)? (current + (current / 4)):
|
|
(current < 150000)? (current + (current / 8)):
|
|
(current + (current / 16));
|
|
}
|
|
}
|
|
};
|
|
|
|
///
|
|
/// The Array class provides a dynamically resized array.
|
|
///
|
|
/// With C++11, the Array class move-contructs values in various context.
|
|
/// The move constructor of the value must not raise an exception.
|
|
///
|
|
template <typename T, typename POSITIONER = ArrayPositioner<T>, typename RESIZER = ArrayResizer >
|
|
class Array: public Mmged, public Growable
|
|
{
|
|
public:
|
|
typedef Array<T,POSITIONER,RESIZER> SelfType;
|
|
|
|
typedef ArrayPositioner<T> DefaultPositioner;
|
|
typedef ArrayResizer DefaultResizer;
|
|
|
|
enum
|
|
{
|
|
DEFAULT_CAPACITY = 128,
|
|
INVALID_INDEX = ~(qse_size_t)0
|
|
};
|
|
|
|
void init_array (int capacity)
|
|
{
|
|
if (capacity <= 0)
|
|
{
|
|
this->buffer = QSE_NULL;
|
|
this->capacity = 0;
|
|
}
|
|
else
|
|
{
|
|
//this->buffer = new T[capacity];
|
|
this->buffer = (T*)::operator new (capacity * QSE_SIZEOF(*this->buffer), this->getMmgr());
|
|
this->capacity = capacity;
|
|
}
|
|
|
|
this->count = 0;
|
|
}
|
|
|
|
public:
|
|
Array (qse_size_t capacity = DEFAULT_CAPACITY): Mmged(QSE_NULL)
|
|
{
|
|
this->init_array (capacity);
|
|
}
|
|
|
|
Array (Mmgr* mmgr, qse_size_t capacity = DEFAULT_CAPACITY): Mmged(mmgr)
|
|
{
|
|
this->init_array (capacity);
|
|
}
|
|
|
|
Array (const SelfType& array): Mmged(array.getMmgr()), count(0), capacity(0), buffer(QSE_NULL)
|
|
{
|
|
if (array.buffer)
|
|
{
|
|
this->buffer = this->clone_buffer (array.buffer, array.capacity, array.count);
|
|
this->count = array.count;
|
|
this->capacity = array.capacity;
|
|
}
|
|
}
|
|
|
|
#if defined(QSE_CPP_ENABLE_CPP11_MOVE)
|
|
Array (SelfType&& array): Mmged(array.getMmgr()), count(0), capacity(0), buffer(QSE_NULL)
|
|
{
|
|
if (array.buffer)
|
|
{
|
|
this->buffer = array.buffer;
|
|
this->count = array.count;
|
|
this->capacity = array.capacity;
|
|
|
|
array.buffer = QSE_NULL;
|
|
array.count = 0;
|
|
array.capacity = 0;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
~Array ()
|
|
{
|
|
this->clear (true);
|
|
}
|
|
|
|
SelfType& operator= (const SelfType& array)
|
|
{
|
|
if (this != &array)
|
|
{
|
|
this->clear (true);
|
|
if (array.buffer)
|
|
{
|
|
this->buffer = this->clone_buffer (array, array.capacity, array.count);
|
|
this->count = array.count;
|
|
this->capacity = array.capacity;
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
#if defined(QSE_CPP_ENABLE_CPP11_MOVE)
|
|
SelfType& operator= (SelfType&& array)
|
|
{
|
|
if (this != &array)
|
|
{
|
|
// since 'array' is an rvalue, i know it's going to be destroyed.
|
|
// it should be safe to destroy all my items here first instead of
|
|
// arranging to swap with items of 'array'.
|
|
this->clear (true);
|
|
|
|
if (array.buffer)
|
|
{
|
|
// TODO: should i swap items if mmgrs are differnt
|
|
// between *this and array?
|
|
|
|
this->setMmgr (array.getMmgr()); // copy over mmgr.
|
|
|
|
this->buffer = array.buffer;
|
|
this->count = array.count;
|
|
this->capacity = array.capacity;
|
|
|
|
// since i cleared all items in the existing array,
|
|
// i can simply do the followings.
|
|
array.buffer = QSE_NULL;
|
|
array.count = 0;
|
|
array.capacity = 0;
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
#endif
|
|
|
|
protected:
|
|
T* clone_buffer (const T* srcbuf, qse_size_t capa, qse_size_t cnt)
|
|
{
|
|
QSE_ASSERT (capa > 0);
|
|
QSE_ASSERT (cnt <= capa);
|
|
|
|
qse_size_t index;
|
|
|
|
//T* tmp = new T[capa];
|
|
//T* tmp = (T*)::operator new (capa * QSE_SIZEOF(*tmp), this->getMmgr());
|
|
T* tmp = (T*)this->getMmgr()->allocate (capa * QSE_SIZEOF(*tmp));
|
|
|
|
try
|
|
{
|
|
for (index = 0; index < cnt; index++)
|
|
{
|
|
// copy-construct each element.
|
|
new((QSE::Mmgr*)QSE_NULL, &tmp[index]) T(srcbuf[index]);
|
|
this->_positioner (tmp[index], index);
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
// in case copy-constructor raises an exception.
|
|
QSE_ASSERT (tmp != QSE_NULL);
|
|
while (index > 0)
|
|
{
|
|
--index;
|
|
this->_positioner (tmp[index], INVALID_INDEX);
|
|
tmp[index].T::~T ();
|
|
}
|
|
|
|
//::operator delete (tmp, this->getMmgr());
|
|
this->getMmgr()->dispose (tmp);
|
|
|
|
throw;
|
|
}
|
|
|
|
return tmp;
|
|
}
|
|
|
|
#if defined(QSE_CPP_ENABLE_CPP11_MOVE)
|
|
T* clone_buffer_by_moving (T* srcbuf, qse_size_t capa, qse_size_t cnt)
|
|
{
|
|
QSE_ASSERT (capa > 0);
|
|
QSE_ASSERT (cnt <= capa);
|
|
|
|
qse_size_t index;
|
|
|
|
//T* tmp = new T[capa];
|
|
//T* tmp = (T*)::operator new (capa * QSE_SIZEOF(*tmp), this->getMmgr());
|
|
T* tmp = (T*)this->getMmgr()->allocate (capa * QSE_SIZEOF(*tmp));
|
|
|
|
try
|
|
{
|
|
for (index = 0; index < cnt; index++)
|
|
{
|
|
// move-construct(or copy-construct) each element.
|
|
new((QSE::Mmgr*)QSE_NULL, &tmp[index]) T(QSE_CPP_RVREF(srcbuf[index]));
|
|
this->_positioner (tmp[index], index);
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
// in case move-constructor(or copy-constructor) raises an exception.
|
|
QSE_ASSERT (tmp != QSE_NULL);
|
|
while (index > 0)
|
|
{
|
|
--index;
|
|
|
|
// if move-contruction ended up with an exception,
|
|
// the original array can get into an unknown state eventually.
|
|
// i don't attempt to restore the moved object as an exception
|
|
// may be raised during restoration. i don't implement noexcept
|
|
// check yet.
|
|
//
|
|
// TODO: reconsider if this unwinding is needed
|
|
//try { new((QSE::Mmgr*)QSE_NULL, &srcbuf[index]) T((T&&)tmp[index]); }
|
|
//catch (...) {}
|
|
|
|
this->_positioner (tmp[index], INVALID_INDEX);
|
|
tmp[index].T::~T ();
|
|
}
|
|
|
|
//::operator delete (tmp, this->getMmgr());
|
|
this->getMmgr()->dispose (tmp);
|
|
|
|
throw;
|
|
}
|
|
|
|
return tmp;
|
|
}
|
|
#endif
|
|
|
|
void put_item (qse_size_t index, const T& value)
|
|
{
|
|
if (index >= this->count)
|
|
{
|
|
// no value exists in the given position.
|
|
// i can copy-construct the value.
|
|
new((QSE::Mmgr*)QSE_NULL, &this->buffer[index]) T(value);
|
|
this->_positioner (this->buffer[index], index);
|
|
}
|
|
else
|
|
{
|
|
// there is an old value in the position. do classic-assignment
|
|
this->buffer[index] = value;
|
|
this->_positioner (this->buffer[index], index);
|
|
}
|
|
}
|
|
|
|
#if defined(QSE_CPP_ENABLE_CPP11_MOVE)
|
|
void put_item_by_moving (qse_size_t index, T&& value)
|
|
{
|
|
if (index >= this->count)
|
|
{
|
|
// no value exists in the given position.
|
|
// i can move-construct the value.
|
|
new((QSE::Mmgr*)QSE_NULL, &this->buffer[index]) T(QSE_CPP_RVREF(value));
|
|
this->_positioner (this->buffer[index], index);
|
|
}
|
|
else
|
|
{
|
|
// there is an old value in the position. do move-assignment.
|
|
this->buffer[index] = QSE_CPP_RVREF(value);
|
|
this->_positioner (this->buffer[index], index);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void clear_all_items ()
|
|
{
|
|
QSE_ASSERT (this->count <= 0 || (this->count >= 1 && this->buffer));
|
|
|
|
for (qse_size_t i = this->count; i > 0; )
|
|
{
|
|
--i;
|
|
this->_positioner (this->buffer[i], INVALID_INDEX);
|
|
this->buffer[i].T::~T ();
|
|
}
|
|
|
|
this->count = 0;
|
|
}
|
|
|
|
public:
|
|
bool isEmpty () const
|
|
{
|
|
return this->count == 0;
|
|
}
|
|
|
|
qse_size_t getSize () const
|
|
{
|
|
return this->count;
|
|
}
|
|
|
|
qse_size_t getCapacity () const
|
|
{
|
|
return this->capacity;
|
|
}
|
|
|
|
operator T* ()
|
|
{
|
|
return this->buffer;
|
|
}
|
|
|
|
operator const T* () const
|
|
{
|
|
return this->buffer;
|
|
}
|
|
|
|
T* getBuffer ()
|
|
{
|
|
return this->buffer;
|
|
}
|
|
|
|
const T* getBuffer () const
|
|
{
|
|
return this->buffer;
|
|
}
|
|
|
|
/// The getIndex() function returns the index of the given value \a v
|
|
/// if it belongs to the array. It returns #INVALID_INDEX if not.
|
|
/// Note that this is not a search function.
|
|
///
|
|
/// \code
|
|
/// QSE::Array<int> a;
|
|
/// a.insert (0, 10);
|
|
/// a.insert (0, 20);
|
|
/// a.insert (0, 30);
|
|
/// const int& t = a[2];
|
|
/// printf ("%lu\n", (unsigned long int)a.getIndex(t)); // print 2
|
|
/// \endcode
|
|
qse_size_t getIndex (const T& v) const
|
|
{
|
|
if (&v >= &this->buffer[0] && &v < &this->buffer[this->count])
|
|
{
|
|
return &v - &this->buffer[0];
|
|
}
|
|
|
|
return INVALID_INDEX;
|
|
}
|
|
|
|
// i don't want expose a non-const accessor as i don't like
|
|
// a proper update procesure to be skipped.
|
|
// use setValueAt() or update() to modify the existing element.
|
|
//T& operator[] (qse_size_t index)
|
|
//{
|
|
// QSE_ASSERT (index < this->count);
|
|
// return this->buffer[index];
|
|
//}
|
|
|
|
const T& operator[] (qse_size_t index) const
|
|
{
|
|
QSE_ASSERT (index < this->count);
|
|
return this->buffer[index];
|
|
}
|
|
|
|
// i don't want expose a non-const accessor as i don't like
|
|
// a proper update procesure to be skipped.
|
|
// use setValueAt() or update() to modify the existing element.
|
|
//T& getValueAt (qse_size_t index)
|
|
//{
|
|
// QSE_ASSERT (index < this->count);
|
|
// return this->buffer[index];
|
|
//}
|
|
|
|
const T& getValueAt (qse_size_t index) const
|
|
{
|
|
QSE_ASSERT (index < this->count);
|
|
return this->buffer[index];
|
|
}
|
|
|
|
void setValueAt (qse_size_t index, const T& value)
|
|
{
|
|
this->update (index, value);
|
|
}
|
|
|
|
#if defined(QSE_CPP_ENABLE_CPP11_MOVE)
|
|
void setValueAt (qse_size_t index, T&& value)
|
|
{
|
|
this->update (index, QSE_CPP_RVREF(value));
|
|
}
|
|
#endif
|
|
|
|
const T& getFirst () const
|
|
{
|
|
return this->getValueAt(0);
|
|
}
|
|
|
|
const T& getLast() const
|
|
{
|
|
return this->getValueAt(this->getSize() - 1);
|
|
}
|
|
|
|
protected:
|
|
void secure_slot (qse_size_t index)
|
|
{
|
|
if (index >= this->capacity)
|
|
{
|
|
// the position to add the element is beyond the
|
|
// capacity. resize the buffer.
|
|
qse_size_t new_capa = this->_resizer (this->capacity, this->getGrowthPolicy());
|
|
|
|
if (index < new_capa)
|
|
this->setCapacity (new_capa);
|
|
else
|
|
this->setCapacity (index + 1);
|
|
}
|
|
else if (this->count >= this->capacity)
|
|
{
|
|
// the array is already full.
|
|
// insertion requires at least one more slot
|
|
qse_size_t new_capa = this->_resizer (this->capacity, this->getGrowthPolicy());
|
|
this->setCapacity (new_capa);
|
|
}
|
|
|
|
if (index < this->count)
|
|
{
|
|
// shift the existing elements to the back by one slot.
|
|
for (qse_size_t i = this->count; i > index; i--)
|
|
{
|
|
#if defined(QSE_CPP_ENABLE_CPP11_MOVE)
|
|
this->put_item_by_moving (i, QSE_CPP_RVREF(this->buffer[i - 1]));
|
|
#else
|
|
this->put_item (i, this->buffer[i - 1]);
|
|
#endif
|
|
}
|
|
}
|
|
else if (index > this->count)
|
|
{
|
|
// the insertion position leaves some gaps in between.
|
|
// fill the gap with a default value.
|
|
for (qse_size_t i = this->count; i < index; i++)
|
|
{
|
|
new((QSE::Mmgr*)QSE_NULL, &this->buffer[i]) T();
|
|
this->_positioner (this->buffer[i], i);
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
qse_size_t insert (qse_size_t index, const T& value)
|
|
{
|
|
// Unlike insert() in RedBlackTree and HashList,
|
|
// it inserts an item when index exists in the existing array.
|
|
// It is because array allows duplicate items.
|
|
// RedBlckTree::insert() and HashList::insert() return failure
|
|
// if existing item exists.
|
|
this->secure_slot (index);
|
|
|
|
//this->buffer[index] = value;
|
|
this->put_item (index, value);
|
|
if (index > this->count) this->count = index + 1;
|
|
else this->count++;
|
|
|
|
return index;
|
|
}
|
|
|
|
#if defined(QSE_CPP_ENABLE_CPP11_MOVE)
|
|
qse_size_t insert (qse_size_t index, T&& value)
|
|
{
|
|
// Unlike insert() in RedBlackTree and HashList,
|
|
// it inserts an item when index exists in the existing array.
|
|
// It is because array allows duplicate items.
|
|
// RedBlckTree::insert() and HashList::insert() return failure
|
|
// if existing item exists.
|
|
this->secure_slot (index);
|
|
|
|
//this->buffer[index] = value;
|
|
this->put_item_by_moving (index, QSE_CPP_RVREF(value));
|
|
if (index > this->count) this->count = index + 1;
|
|
else this->count++;
|
|
|
|
return index;
|
|
}
|
|
#endif
|
|
|
|
qse_size_t update (qse_size_t index, const T& value)
|
|
{
|
|
QSE_ASSERT (index < this->count);
|
|
this->buffer[index] = value;
|
|
this->_positioner (this->buffer[index], index);
|
|
return index;
|
|
}
|
|
|
|
#if defined(QSE_CPP_ENABLE_CPP11_MOVE)
|
|
qse_size_t update (qse_size_t index, T&& value)
|
|
{
|
|
QSE_ASSERT (index < this->count);
|
|
this->buffer[index] = QSE_CPP_RVREF(value);
|
|
this->_positioner (this->buffer[index], index);
|
|
return index;
|
|
}
|
|
#endif
|
|
|
|
qse_size_t upsert (qse_size_t index, const T& value)
|
|
{
|
|
if (index < this->count)
|
|
return this->update (index, value);
|
|
else
|
|
return this->insert (index, value);
|
|
}
|
|
|
|
#if defined(QSE_CPP_ENABLE_CPP11_MOVE)
|
|
qse_size_t upsert (qse_size_t index, T&& value)
|
|
{
|
|
if (index < this->count)
|
|
return this->update (index, QSE_CPP_RVREF(value));
|
|
else
|
|
return this->insert (index, QSE_CPP_RVREF(value));
|
|
}
|
|
#endif
|
|
|
|
qse_size_t ensert (qse_size_t index, const T& value)
|
|
{
|
|
if (index < this->count)
|
|
return index; // no update
|
|
else
|
|
return this->insert (index, value);
|
|
}
|
|
|
|
#if defined(QSE_CPP_ENABLE_CPP11_MOVE)
|
|
qse_size_t ensert (qse_size_t index, T&& value)
|
|
{
|
|
if (index < this->count)
|
|
return index; // no update
|
|
else
|
|
return this->insert (index, QSE_CPP_RVREF(value));
|
|
}
|
|
#endif
|
|
|
|
qse_size_t insertFirst (const T& value)
|
|
{
|
|
return this->insert (0, value);
|
|
}
|
|
|
|
#if defined(QSE_CPP_ENABLE_CPP11_MOVE)
|
|
qse_size_t insertFirst (T&& value)
|
|
{
|
|
return this->insert (0, QSE_CPP_RVREF(value));
|
|
}
|
|
#endif
|
|
|
|
qse_size_t insertLast (const T& value)
|
|
{
|
|
return this->insert (this->getSize(), value);
|
|
}
|
|
|
|
#if defined(QSE_CPP_ENABLE_CPP11_MOVE)
|
|
qse_size_t insertLast (T&& value)
|
|
{
|
|
return this->insert (this->getSize(), QSE_CPP_RVREF(value));
|
|
}
|
|
#endif
|
|
|
|
void remove (qse_size_t index)
|
|
{
|
|
this->remove (index, 1);
|
|
}
|
|
|
|
void remove (qse_size_t from_index, qse_size_t size)
|
|
{
|
|
if (size <= 0 || this->count <= 0 || from_index >= this->count) return;
|
|
|
|
qse_size_t to_index = from_index + size - 1;
|
|
if (to_index >= this->count) to_index = this->count - 1;
|
|
|
|
qse_size_t j = from_index;
|
|
qse_size_t i = to_index + 1;
|
|
|
|
// replace deleted elements by surviving elements at the back
|
|
while (i < this->count)
|
|
{
|
|
// which is better?
|
|
// 1. destruct followed by copy construct
|
|
// 2. operator assignment.
|
|
|
|
// 1. destruct followed by copy construct
|
|
//this->_positioner (this->buffer[j], INVALID_INDEX);
|
|
//this->buffer[j].T::~T();
|
|
//new((QSE::Mmgr*)QSE_NULL, &this->buffer[j]) T(this->buffer[i]);
|
|
//this->_positioner (this->buffer[j], j);
|
|
|
|
// 2. operator assignment
|
|
this->buffer[j] = QSE_CPP_RVREF(this->buffer[i]);
|
|
this->_positioner (this->buffer[j], j);
|
|
|
|
j++; i++;
|
|
}
|
|
|
|
// call the destructor of deleted elements.
|
|
while (j < this->count)
|
|
{
|
|
this->_positioner (this->buffer[j], INVALID_INDEX);
|
|
this->buffer[j].T::~T ();
|
|
j++;
|
|
}
|
|
|
|
// recalculate the number of elements
|
|
this->count -= to_index - from_index + 1;
|
|
}
|
|
|
|
void removeFirst ()
|
|
{
|
|
this->remove(0);
|
|
}
|
|
|
|
void removeLast ()
|
|
{
|
|
this->remove(this->getSize() - 1);
|
|
}
|
|
|
|
#if 0
|
|
/// \return the number of items deleted.
|
|
int removeByValue (const T& value)
|
|
{
|
|
qse_size_t index = this->findFirstIndex (value);
|
|
if (index == INVALID_INDEX) return 0;
|
|
this->remove (index);
|
|
return 1;
|
|
}
|
|
|
|
qse_size_t removeAllByValue (const T& value)
|
|
{
|
|
qse_size_t cnt = 0;
|
|
while (this->removeByValue(value) > 0) cnt++;
|
|
return cnt;
|
|
}
|
|
#endif
|
|
|
|
public:
|
|
void clear (bool purge_buffer = false)
|
|
{
|
|
this->clear_all_items ();
|
|
|
|
if (purge_buffer && this->buffer)
|
|
{
|
|
QSE_ASSERT (this->count <= 0);
|
|
QSE_ASSERT (this->capacity > 0);
|
|
|
|
//::operator delete (this->buffer, this->getMmgr());
|
|
this->getMmgr()->dispose (this->buffer);
|
|
this->buffer = QSE_NULL;
|
|
this->capacity = 0;
|
|
}
|
|
}
|
|
|
|
void setSize (qse_size_t size)
|
|
{
|
|
if (size < this->count)
|
|
{
|
|
for (qse_size_t i = size; i < this->count; ++i)
|
|
{
|
|
// call the destructor of the items
|
|
this->_positioner (this->buffer[i], INVALID_INDEX);
|
|
this->buffer[i].T::~T ();
|
|
}
|
|
|
|
this->count = size;
|
|
}
|
|
else if (size > this->count)
|
|
{
|
|
if (size > this->capacity) this->setCapacity (size);
|
|
for (qse_size_t i = this->count; i < size; ++i)
|
|
{
|
|
// use the default contructor to set the value.
|
|
new((QSE::Mmgr*)QSE_NULL, &this->buffer[i]) T();
|
|
this->_positioner (this->buffer[i], i);
|
|
}
|
|
|
|
this->count = size;
|
|
}
|
|
}
|
|
|
|
void setCapacity (qse_size_t capa)
|
|
{
|
|
if (capa == this->capacity) return;
|
|
|
|
if (capa <= 0)
|
|
{
|
|
this->clear (true);
|
|
}
|
|
else if (this->buffer)
|
|
{
|
|
QSE_ASSERT (this->capacity > 0);
|
|
|
|
qse_size_t cnt = this->count;
|
|
if (cnt > capa) cnt = capa;
|
|
|
|
#if defined(QSE_CPP_ENABLE_CPP11_MOVE)
|
|
T* tmp = this->clone_buffer_by_moving (this->buffer, capa, cnt);
|
|
#else
|
|
T* tmp = this->clone_buffer (this->buffer, capa, cnt);
|
|
#endif
|
|
this->clear (true);
|
|
this->buffer = tmp;
|
|
this->capacity = capa;
|
|
this->count = cnt;
|
|
}
|
|
else
|
|
{
|
|
QSE_ASSERT (this->capacity <= 0);
|
|
QSE_ASSERT (this->count <= 0);
|
|
|
|
//this->buffer = (T*)::operator new (capa * QSE_SIZEOF(*this->buffer), this->getMmgr());
|
|
this->buffer = (T*)this->getMmgr()->allocate (capa * QSE_SIZEOF(*this->buffer));
|
|
this->capacity = capa;
|
|
}
|
|
}
|
|
|
|
/// The compact() function removes the unused space in the buffer.
|
|
void compact ()
|
|
{
|
|
this->setCapacity (this->size);
|
|
}
|
|
|
|
#if 0
|
|
qse_size_t findFirstIndex (const T& value) const
|
|
{
|
|
for (qse_size_t i = 0; i < this->count; i++)
|
|
{
|
|
if (this->is_equal (this->buffer[i], value)) return i;
|
|
}
|
|
return INVALID_INDEX;
|
|
}
|
|
|
|
qse_size_t findFirstIndex (const T& value, qse_size_t index) const
|
|
{
|
|
for (qse_size_t i = index; i < this->count; i++)
|
|
{
|
|
if (this->is_equal (this->buffer[i], value)) return i;
|
|
}
|
|
return INVALID_INDEX;
|
|
}
|
|
|
|
qse_size_t findLastIndex (const T& value) const
|
|
{
|
|
for (qse_size_t i = this->count; i > 0; )
|
|
{
|
|
if (this->is_equal (this->buffer[--i], value)) return i;
|
|
}
|
|
return INVALID_INDEX;
|
|
}
|
|
|
|
qse_size_t findLastIndex (const T& value, qse_size_t index) const
|
|
{
|
|
for (qse_size_t i = index + 1; i > 0; )
|
|
{
|
|
if (this->is_equal (this->buffer[--i], value)) return i;
|
|
}
|
|
return INVALID_INDEX;
|
|
}
|
|
#endif
|
|
|
|
enum RotateDirection
|
|
{
|
|
ROTATE_LEFT,
|
|
ROTATE_RIGHT
|
|
};
|
|
|
|
void rotate (RotateDirection dir, qse_size_t n)
|
|
{
|
|
qse_size_t first, last, cnt, index, nk;
|
|
T c;
|
|
|
|
if ((n %= this->count) == 0) return;
|
|
|
|
if (dir == ROTATE_RIGHT) n = this->count - n;
|
|
first = 0; nk = this->count - n; cnt = 0;
|
|
|
|
while (cnt < n)
|
|
{
|
|
last = first + nk;
|
|
index = first;
|
|
c = QSE_CPP_RVREF(this->buffer[index]);
|
|
while (1)
|
|
{
|
|
cnt++;
|
|
while (index < nk)
|
|
{
|
|
this->buffer[index] = QSE_CPP_RVREF(this->buffer[index + n]);
|
|
this->_positioner (this->buffer[index], index);
|
|
index += n;
|
|
}
|
|
if (index == last) break;
|
|
|
|
this->buffer[index] = QSE_CPP_RVREF(this->buffer[index - nk]);
|
|
this->_positioner (this->buffer[index], index);
|
|
index -= nk;
|
|
}
|
|
|
|
this->buffer[last] = QSE_CPP_RVREF(c);
|
|
this->_positioner (this->buffer[last], last);
|
|
first++;
|
|
}
|
|
}
|
|
|
|
protected:
|
|
POSITIONER _positioner;
|
|
RESIZER _resizer;
|
|
|
|
qse_size_t count;
|
|
qse_size_t capacity;
|
|
T* buffer;
|
|
};
|
|
|
|
/////////////////////////////////
|
|
QSE_END_NAMESPACE(QSE)
|
|
/////////////////////////////////
|
|
|
|
#endif
|
|
|
|
|