You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rosegarden/src/base/FastVector.h

595 lines
18 KiB

/*
Rosegarden
A sequencer and musical notation editor.
This program is Copyright 2000-2008
Guillaume Laurent <glaurent@telegraph-road.org>,
Chris Cannam <cannam@all-day-breakfast.com>,
Richard Bown <bownie@bownie.com>
The moral right of the authors to claim authorship of this work
has been asserted.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _FAST_VECTOR_H_
#define _FAST_VECTOR_H_
#include <iterator>
#include <cstdlib> /* for malloc, realloc, free */
#include <cstring> /* for memmove */
#include <cassert>
/**
FastVector is a sequence class with an interface similar to that
of the STL vector, with several nice properties and one nasty one:
* It allows fast random access, like the STL vector -- although
access is not quite as fast, as a little arithmetic is required.
* Appending (push_back) and prepending (push_front) are both fast.
* The worst-case behaviour is repeated random inserts and deletes
of single items, and performance in this case is still as good
as vector where builtin types are stored, and much better where
deep-copied objects are stored.
* Performance is not as good as vector for very short sequences
(where vector's simple implementation excels), but it's not bad.
* BUT: To achieve all this, it cheats. Objects are moved around
from place to place in the vector using memmove(), rather than
deep copy. If you store objects with internal pointers, they
will break badly. Storing simple structures will be no problem,
and if you just store pointers to objects you'll be fine, but
it's unwise (for example) to store other containers.
* One other difference from the STL vector: It uses placement new
with the copy constructor to construct objects, rather than
the default constructor and assignment. Thus the copy
constructor must work on the stored objects, though assignment
doesn't have to.
Do not use this class if:
* You do not require random access (operator[]). Use the STL
linked list instead, it'll almost certainly be faster.
* Your sequence is constructed once at a non-time-critical
moment, and subsequently is only read. Use STL vector, as
it's more standard and lookup is slightly quicker.
* Your sequence is unlikely to contain more than a dozen objects
which are only appended (push_back) and you do not require
prepend (push_front). Use STL vector, as it's more standard,
simpler and often quicker in this case.
* You want to pass sequences to other libraries or return them
from library functions. Use a standard container instead.
* You want to store objects that contain internal pointers or
that do not have a working copy constructor.
Chris Cannam, 1996-2001
*/
template <class T>
class FastVector
{
public:
typedef T value_type;
typedef long size_type;
typedef long difference_type;
private:
class iterator_base : public
#if defined(_STL_1997_) || (__GNUC__ > 2)
std::iterator<std::random_access_iterator_tag, T, difference_type>
#else
#if defined(__STL_USE_NAMESPACES)
std::
#endif
random_access_iterator<T, difference_type>
#endif
{
public:
iterator_base() :
m_v(0), m_i(-1) {
}
iterator_base(const iterator_base &i) :
m_v(i.m_v), m_i(i.m_i) {
}
iterator_base &operator=(const iterator_base &i) {
if (&i != this) { m_v = i.m_v; m_i = i.m_i; }
return *this;
}
iterator_base &operator--() { --m_i; return *this; }
iterator_base operator--(int) {
iterator_base i(*this);
--m_i;
return i;
}
iterator_base &operator++() { ++m_i; return *this; }
iterator_base operator++(int) {
iterator_base i(*this);
++m_i;
return i;
}
bool operator==(const iterator_base &i) const {
return (m_v == i.m_v && m_i == i.m_i);
}
bool operator!=(const iterator_base &i) const {
return (m_v != i.m_v || m_i != i.m_i);
}
iterator_base &operator+=(FastVector<T>::difference_type i) {
m_i += i; return *this;
}
iterator_base &operator-=(FastVector<T>::difference_type i) {
m_i -= i; return *this;
}
iterator_base operator+(FastVector<T>::difference_type i) const {
iterator_base n(*this); n += i; return n;
}
iterator_base operator-(FastVector<T>::difference_type i) const {
iterator_base n(*this); n -= i; return n;
}
typename FastVector<T>::difference_type operator-(const iterator_base &i) const{
assert(m_v == i.m_v);
return m_i - i.m_i;
}
protected:
iterator_base(FastVector<T> *v, size_type i) : m_v(v), m_i(i) { }
FastVector<T> *m_v;
size_type m_i;
};
public:
// I'm sure these can be simplified
class iterator : public
iterator_base
{
public:
iterator() : iterator_base() { }
iterator(const iterator_base &i) : iterator_base(i) { }
iterator &operator=(const iterator &i) {
iterator_base::operator=(i);
return *this;
}
T &operator*() { return iterator_base::m_v->at(iterator_base::m_i); }
T *operator->() { return &(operator*()); }
const T &operator*() const { return iterator_base::m_v->at(iterator_base::m_i); }
const T *operator->() const { return &(operator*()); }
protected:
friend class FastVector<T>;
iterator(FastVector<T> *v, size_type i) : iterator_base(v,i) { }
};
class reverse_iterator : public
iterator_base
{
public:
reverse_iterator() : iterator_base() { }
reverse_iterator(const iterator_base &i) : iterator_base(i) { }
reverse_iterator &operator=(const reverse_iterator &i) {
iterator_base::operator=(i);
return *this;
}
T &operator*() { return iterator_base::m_v->at(iterator_base::m_v->size() - iterator_base::m_i - 1); }
T *operator->() { return &(operator*()); }
const T &operator*() const { return iterator_base::m_v->at(iterator_base::m_v->size() - iterator_base::m_i - 1); }
const T *operator->() const { return &(operator*()); }
protected:
friend class FastVector<T>;
reverse_iterator(FastVector<T> *v, size_type i) : iterator_base(v,i) { }
};
class const_iterator : public
iterator_base
{
public:
const_iterator() : iterator_base() { }
const_iterator(const iterator_base &i) : iterator_base(i) { }
const_iterator &operator=(const const_iterator &i) {
iterator_base::operator=(i);
return *this;
}
const T &operator*() const { return iterator_base::m_v->at(iterator_base::m_i); }
const T *operator->() const { return &(operator*()); }
protected:
friend class FastVector<T>;
const_iterator(const FastVector<T> *v, size_type i) :
iterator_base(const_cast<FastVector<T> *>(v),i) { }
};
class const_reverse_iterator : public
iterator_base
{
public:
const_reverse_iterator() : iterator_base() { }
const_reverse_iterator(const iterator_base &i) : iterator_base(i) { }
const_reverse_iterator &operator=(const const_reverse_iterator &i) {
iterator_base::operator=(i);
return *this;
}
const T &operator*() const { return iterator_base::m_v->at(iterator_base::m_v->size() - iterator_base::m_i - 1); }
const T *operator->() const { return &(operator*()); }
protected:
friend class FastVector<T>;
const_reverse_iterator(const FastVector<T> *v, size_type i) :
iterator_base(const_cast<FastVector<T> *>(v),i) { }
};
public:
FastVector() :
m_items(0), m_count(0), m_gapStart(-1),
m_gapLength(0), m_size(0) { }
FastVector(const FastVector<T> &);
virtual ~FastVector();
template <class InputIterator>
FastVector(InputIterator first, InputIterator last) :
m_items(0), m_count(0), m_gapStart(-1),
m_gapLength(0), m_size(0) {
insert(begin(), first, last);
}
FastVector<T> &operator=(const FastVector<T> &);
virtual iterator begin() { return iterator(this, 0); }
virtual iterator end() { return iterator(this, m_count); }
virtual const_iterator begin() const { return const_iterator(this, 0); }
virtual const_iterator end() const { return const_iterator(this, m_count); }
virtual reverse_iterator rbegin() { return reverse_iterator(this, 0); }
virtual reverse_iterator rend() { return reverse_iterator(this, m_count); }
virtual const_reverse_iterator rbegin() const { return const_reverse_iterator(this, 0); }
virtual const_reverse_iterator rend() const { return const_reverse_iterator(this, m_count); }
size_type size() const { return m_count; }
bool empty() const { return m_count == 0; }
/// not all of these are defined yet
void swap(FastVector<T> &v);
bool operator==(const FastVector<T> &) const;
bool operator!=(const FastVector<T> &v) const { return !operator==(v); }
bool operator<(const FastVector<T> &) const;
bool operator>(const FastVector<T> &) const;
bool operator<=(const FastVector<T> &) const;
bool operator>=(const FastVector<T> &) const;
T& at(size_type index) {
assert(index >= 0 && index < m_count);
return m_items[externalToInternal(index)];
}
const T& at(size_type index) const {
return (const_cast<FastVector<T> *>(this))->at(index);
}
T &operator[](size_type index) {
return at(index);
}
const T &operator[](size_type index) const {
return at(index);
}
virtual T* array(size_type index, size_type count);
/** We *guarantee* that push methods etc modify the FastVector
only through a call to insert(size_type, T), and that erase
etc modify it only through a call to remove(size_type). This
is important because subclasses only need to override those
functions to catch all mutations */
virtual void push_front(const T& item) { insert(0, item); }
virtual void push_back(const T& item) { insert(m_count, item); }
virtual iterator insert(const iterator &p, const T &t) {
insert(p.m_i, t);
return p;
}
template <class InputIterator>
iterator insert(const iterator &p, InputIterator &i, InputIterator &j);
virtual iterator erase(const iterator &i) {
assert(i.m_v == this);
remove(i.m_i);
return iterator(this, i.m_i);
}
virtual iterator erase(const iterator &i, const iterator &j);
virtual void clear();
protected:
/// basic insert -- all others call this
virtual void insert(size_type index, const T&);
/// basic remove -- erase(), clear() call this
virtual void remove(size_type index);
private:
void resize(size_type needed); // needed is internal (i.e. including gap)
void moveGapTo(size_type index); // index is external
void closeGap() {
if (m_gapStart >= 0) moveGapTo(m_count);
m_gapStart = -1;
}
size_type bestNewCount(size_type n, size_t) const {
if (m_size == 0) {
if (n < 8) return 8;
else return n;
} else {
// double up each time -- it's faster than just incrementing
size_type s(m_size);
if (s > n*2) return s/2;
while (s <= n) s *= 2;
return s;
}
}
size_type externalToInternal(size_type index) const {
return ((index < m_gapStart || m_gapStart < 0) ?
index : index + m_gapLength);
}
size_type minSize() const { return 8; }
size_t minBlock() const {
return minSize() * sizeof(T) > 64 ? minSize() * sizeof(T) : 64;
}
T* m_items;
size_type m_count; // not counting gap
size_type m_gapStart; // -1 for no gap
size_type m_gapLength; // undefined if no gap
size_type m_size;
};
template <class T>
void *operator new(size_t, FastVector<T> *, void *space)
{
return space;
}
template <class T>
FastVector<T>::FastVector(const FastVector<T> &l) :
m_items(0), m_count(0), m_gapStart(-1),
m_gapLength(0), m_size(0)
{
resize(l.size());
for (size_type i = 0; i < l.size(); ++i) push_back(l.at(i));
}
template <class T>
FastVector<T>::~FastVector()
{
clear();
free(static_cast<void *>(m_items));
}
template <class T>
FastVector<T>& FastVector<T>::operator=(const FastVector<T>& l)
{
if (&l == this) return *this;
clear();
if (l.size() >= m_size) resize(l.size());
for (size_type i = 0; i < l.size(); ++i) push_back(l.at(i));
return *this;
}
template <class T>
void FastVector<T>::moveGapTo(size_type index)
{
// shift some elements left or right so as to line the gap up with
// the prospective insertion or deletion point.
assert(m_gapStart >= 0);
if (m_gapStart < index) {
// need to move some stuff left to fill the gap
memmove(&m_items[m_gapStart],
&m_items[m_gapStart + m_gapLength],
(index - m_gapStart) * sizeof(T));
} else if (m_gapStart > index) {
// need to move some stuff right to fill the gap
memmove(&m_items[index + m_gapLength], &m_items[index],
(m_gapStart - index) * sizeof(T));
}
m_gapStart = index;
}
template <class T>
void FastVector<T>::resize(size_type needed)
{
size_type newSize = bestNewCount(needed, sizeof(T));
if (m_items) {
m_items = static_cast<T *>(realloc(m_items, newSize * sizeof(T)));
} else {
m_items = static_cast<T *>(malloc(newSize * sizeof(T)));
}
m_size = newSize;
}
template <class T>
void FastVector<T>::remove(size_type index)
{
assert(index >= 0 && index < m_count);
if (index == m_count - 1) {
// shorten the list without disturbing an existing gap, unless
// the item we're taking was the only one after the gap
m_items[externalToInternal(index)].T::~T();
if (m_gapStart == index) m_gapStart = -1;
} else {
if (m_gapStart >= 0) {
// moveGapTo shifts the gap around ready for insertion.
// It actually moves the indexed object out of the way, so
// that it's now at the end of the gap. We have to cope.
moveGapTo(index);
m_items[m_gapStart + m_gapLength].T::~T();
++m_gapLength;
} else { // no gap, make one
m_gapStart = index;
m_items[m_gapStart].T::~T();
m_gapLength = 1;
}
}
if (--m_count == 0) m_gapStart = -1;
if (m_count < m_size/3 && m_size > minSize()) {
closeGap();
resize(m_count); // recover some memory
}
}
template <class T>
void FastVector<T>::insert(size_type index, const T&t)
{
assert(index >= 0 && index <= m_count);
if (index == m_count) {
// Appending. No need to disturb the gap, if there is one --
// we'd rather waste a bit of memory than bother closing it up
if (externalToInternal(m_count) >= m_size || !m_items) {
resize(m_size + 1);
}
new (this, &m_items[externalToInternal(index)]) T(t);
} else if (m_gapStart < 0) {
// Inserting somewhere, when there's no gap we can use.
if (m_count >= m_size) resize(m_size + 1);
// I think it's going to be more common to insert elements
// at the same point repeatedly than at random points.
// So, if we can make a gap here ready for more insertions
// *without* exceeding the m_size limit (i.e. if we've got
// slack left over from a previous gap), then let's. But
// not too much -- ideally we'd like some space left for
// appending. Say half.
if (m_count < m_size-2) {
m_gapStart = index+1;
m_gapLength = (m_size - m_count) / 2;
memmove(&m_items[m_gapStart + m_gapLength], &m_items[index],
(m_count - index) * sizeof(T));
} else {
memmove(&m_items[index + 1], &m_items[index],
(m_count - index) * sizeof(T));
}
new (this, &m_items[index]) T(t);
} else {
// There's already a gap, all we have to do is move it (with
// no need to resize)
if (index != m_gapStart) moveGapTo(index);
new (this, &m_items[m_gapStart]) T(t);
if (--m_gapLength == 0) m_gapStart = -1;
else ++m_gapStart;
}
++m_count;
}
template <class T>
template <class InputIterator>
typename FastVector<T>::iterator FastVector<T>::insert
(const FastVector<T>::iterator &p, InputIterator &i, InputIterator &j)
{
size_type n = p.m_i;
while (i != j) {
--j;
insert(n, *j);
}
return begin() + n;
}
template <class T>
typename FastVector<T>::iterator FastVector<T>::erase
(const FastVector<T>::iterator &i, const FastVector<T>::iterator &j)
{
assert(i.m_v == this && j.m_v == this && j.m_i >= i.m_i);
for (size_type k = i.m_i; k < j.m_i; ++k) remove(i.m_i);
return iterator(this, i.m_i);
}
template <class T>
void FastVector<T>::clear()
{
// Use erase(), which uses remove() -- a subclass that overrides
// remove() will not want to have to provide this method as well
erase(begin(), end());
}
template <class T>
T* FastVector<T>::array(size_type index, size_type count)
{
assert(index >= 0 && count > 0 && index + count <= m_count);
if (m_gapStart < 0 || index + count <= m_gapStart) {
return m_items + index;
} else if (index >= m_gapStart) {
return m_items + index + m_gapLength;
} else {
closeGap();
return m_items + index;
}
}
template <class T>
bool FastVector<T>::operator==(const FastVector<T> &v) const
{
if (size() != v.size()) return false;
for (size_type i = 0; i < m_count; ++i) {
if (at(i) != v.at(i)) return false;
}
return true;
}
#endif