summaryrefslogtreecommitdiffstats
path: root/src/base/FastVector.h
blob: 0ba8e826f0b726d0a18f1e4ddfaf7f209865c10e (plain)
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
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
// -*- c-basic-offset: 4 -*-

/*
    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