1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
|
// -*- c-basic-offset: 4 -*-
// -*- c-file-style: "bsd" -*-
#ifndef _FAST_VECTOR_H_
#define _FAST_VECTOR_H_
#include <iterator>
#include <vector>
/** 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.
* You can subclass the iterator so as to request callbacks when
elements are inserted or deleted (the RobustIteratorVector at
bottom is a simple example). This obviously dents performance;
you get a proportionality to the number of iterators that have
registered this request. However, this facility imposes
virtually no overhead on the non-subclassed, default version.
* 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.
* 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.
Chris Cannam, 1996-2000
*/
template <class T>
class FastVector
{
public:
typedef T value_type;
typedef signed long size_type;
class iterator
#ifdef _STL_1997_
: public ::std::iterator<::std::random_access_iterator_tag, T, size_type>
#else
: public
#ifdef __STL_USE_NAMESPACES
::std::
#endif
random_access_iterator<T, size_type>
#endif
{
public:
iterator() :
m_v(0), m_i(-1), m_registered(false) {
}
iterator(const iterator &i) :
m_v(i.m_v), m_i(i.m_i), m_registered(false) {
if (i.m_registered) registerIterator();
}
virtual ~iterator() {
if (m_registered) unregisterIterator();
}
iterator &operator=(const iterator &i) {
if (&i != this) {
if (m_registered) unregisterIterator();
m_v = i.m_v; m_i = i.m_i; m_registered = false;
if (i.m_registered) registerIterator();
}
return *this;
}
iterator &operator--() { --m_i; return *this; }
iterator operator--(int) { iterator i(*this); --m_i; return i; }
iterator &operator++() { ++m_i; return *this; }
iterator operator++(int) { iterator i(*this); ++m_i; return i; }
bool operator==(const iterator &i) const {
return (m_v == i.m_v && m_i == i.m_i);
}
bool operator!=(const iterator &i) const {
return (m_v != i.m_v || m_i != i.m_i);
}
iterator &operator+=(size_type i) { m_i += i; return *this; }
iterator &operator-=(size_type i) { m_i -= i; return *this; }
iterator operator+(size_type i) const {
iterator n(*this); n += i; return n;
}
iterator operator-(size_type i) const {
iterator n(*this); n -= i; return n;
}
size_type operator-(const iterator &i) const {
assert(m_v == i.m_v); return m_i - i.m_i;
}
T &operator*() { return m_v->at(m_i); }
const T &operator*() const { return m_v->at(m_i); }
T *operator->() { return &(operator*()); }
const T *operator->() const { return &(operator*()); }
protected:
void registerIterator() {
if (!m_v) return;
m_v->registerIterator(this);
m_registered = true;
}
void unregisterIterator() {
if (m_v) m_v->unregisterIterator(this);
m_registered = false;
}
/** You can subclass the iterator, and your subclass can
request notifications of changes by calling
registerIterator() from its constructors. The following
methods are the ones that get called to notify it; note
that the removeCB is called *before* the elements are
actually removed, just in case you want to find out
anything about the elements before they go */
virtual void elementsAddedCB(size_type i, size_type n) { }
virtual void elementsRemovedCB(size_type i, size_type n) { }
virtual void vectorDestroyedCB() { }
size_type position() { return m_i; }
private:
friend FastVector<T>;
iterator(FastVector<T> *v, size_type i) :
m_v(v), m_i(i), m_registered(false) { }
FastVector<T> *m_v;
size_type m_i;
bool m_registered;
};
public:
FastVector() :
m_items(0), m_count(0), m_gapStart(-1),
m_gapLength(0), m_size(0) { }
FastVector(const FastVector<T> &);
virtual ~FastVector();
/** Requires a compiler that supports template methods; gcc-2.7.2
doesn't, egcs series and gcc-2.9x do */
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); }
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 ((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;
}
/** Requires a compiler that supports template methods; gcc-2.7.2
doesn't, egcs series and gcc-2.9x do */
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();
/// conceptually private for call from the iterator class
void registerIterator(iterator *const);
/// conceptually private for call from the iterator class
void unregisterIterator(iterator *const);
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, bool suppressCB = false);
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 s) 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;
void elementsAdded(size_type i, size_type n) const;
void elementsRemoved(size_type i, size_type n) const;
void vectorDestroyed();
// vector is much faster than set in this situation, because we
// frequently have to traverse the entire set and callback on
// every element, but we only relatively rarely insert into or
// delete from it. I suspect also that the most common delete is
// the one we just inserted, so reverse search should work well too.
// Can be an object not a pointer; an empty vector is small and fast
typedef ::std::vector<iterator *> IteratorSet;
IteratorSet m_registeredIterators;
};
template <class T> void *operator new(size_t size, FastVector<T> *list,
void *space);
/** RobustIteratorVector is an example of a class that provides an
iterator derived from the basic one and requesting notifications.
This one keeps each iterator pointing at the same element, by
moving it to segment when other elements are inserted or deleted
before it.
*/
template <class T>
class RobustIteratorVector : public FastVector<T>
{
public:
class iterator : public FastVector<T>::iterator
{
public:
iterator() : FastVector<T>::iterator() {
registerIterator();
}
iterator(const iterator &i) : FastVector<T>::iterator(i) {
registerIterator();
}
/** This is what makes begin() and end() work -- we can't
override them, as they don't return a ref so we'd need
covariant return */
iterator(const FastVector<T>::iterator &i) :
FastVector<T>::iterator(i) {
registerIterator();
}
virtual ~iterator() { }
protected:
virtual void elementsAddedCB(size_type i, size_type n) {
if (i <= position()) operator+=(n);
}
virtual void elementsRemovedCB(size_type i, size_type n) {
if (i < position()) operator-=(n);
}
virtual void vectorDestroyedCB() {
// nothing to see here
}
};
public:
RobustIteratorVector() : FastVector<T>() { }
RobustIteratorVector(const RobustIteratorVector<T> &l) : FastVector<T>(l) { }
virtual ~RobustIteratorVector() { }
template <class InputIterator>
RobustIteratorVector(InputIterator first, InputIterator last) :
FastVector<T>(first, last) { }
};
#endif
// For gcc-2.7.2 this was a terrible inefficiency, but newer compilers
// seem to expect it:
#include "FastVector.cxx"
|