Source: Vector.hh


Annotated List
Files
Globals
Hierarchy
Index
// templated Vector class for dynamic arrays of elements of arbitrary type
// Copyright (C) 1998,1999 Colin McCormack,
// see LICENSE (MD5 f5220f8f599e5e926f37cf32efe3ab68) for terms
// 	$Id: Vector_hh.html,v 1.1.1.1 2000/04/09 01:07:51 skeptopotamus Exp $	

#ifndef VECTOR_HH
#define VECTOR_HH

#include <typeinfo>
#include <stdlib.h>
#include <assert.h>

class ostream;

/** Templated Vector class
   A Vector is a pair of Segments, one (Vector's parent) delineates
   the active subrange of the second, (called allocation)
   allocation is a TupleBase array allocated by QVMM, it encloses the active subrange
*/
template <class T>
class Vector
    : public Segment<T>	// the active region of a Vector
{
public:
    // static vars controlling memory allocation internal overhead

    /** a extra proportion of allocation (3 == 133%)
     */
    static unsigned int extra_allocation;

    /** minimum proportional occupancy before shrinking
     */
    static unsigned int minimum_occupancy;

    /** the minimum size of an allocation in bytes
     */
    static unsigned int minimum_size;

    /** calculate an allocation based on the above parameters
     */
    static int actual_allocation(int newlen) {
        return (newlen + (newlen / extra_allocation)) >? minimum_size;	// >? is max op
    }

protected:
    /** dump Vector to file
     */
    friend ostream& operator<< <T> (ostream& out, const Vector<T> & vec);


    /** Vector's allocation is managed with rlloc, resize and shrink
     */

    /** the allocated space for this Vector
     */
    TupleBase<T> *allocation;

    /** accessor for @see allocation
     */
    TupleBase<T> *Allocation() const {
        return allocation;
    }

    /** reallocate Tuple
        create a new allocation of actual_allocation(len) size
        @param newlen new length Vector allocation
     */
    void rlloc(int newlen);

    /** realloc Tuple to accomodate new growth

        ensure Vector has at least len elements, reallocating as needed
        @param newlen new length Vector allocation
    */
    void resize(int newlen);
    
    /** reallocate Tuple if shrunk below minimum utilization
        reduce an allocation if it's fallen below the low-water-mark
        defined for this Vector type
    */
    void shrink();

    /** set the Segment of this Vector
     */
    Segment<T>::set;

public:

    /////////////////////////////
    // Simple constructors

    /** Null Vector constructor
     */
    Vector();

    /** construct with pre-allocation
     */
    Vector(int size);

    /** destructor - does nothing
     */
    virtual ~Vector();

    /** destroy contents
     */
    void destroy(int len = 1);

    /** consistency check
     */
    void check(int i = 1) const;

    
    /////////////////////////////////////
    // accessors

    /** Segment array accessor
     */
    T &operator[](int offset);

    /** hoist @see Segment element accessor
     */
    Segment<T>::element;

    /** hoist @see Segment empty predicate
     */
    Segment<T>::empty;

    /** hoist @see Segment boolean cast
     */
    Segment<T>::operator bool;


    /////////////////////////////////////
    // copy constructors of sundry kinds

    /** Copy T* array of a given size
     */
    Vector(const T *contentT, int size);

    /** Copy Segment
     */
    Vector(const Segment<T> &contentT);

    /** Make singleton T& Vector
     */
    Vector(const T &contentT);

    /** Repeat T& as array
     */
    Vector(const T &contentT, int repetition);

    /** Reference Tuple
     */
    Vector(TupleBase<T> *t);

    /** Reference Tuple
     */
    Vector(TupleBase<T> &t);

    /** Reference SubTuple
     @param _start start of subrange
     @param _len length of subrange
     */
    Vector(TupleBase<T> *t, int _start, int _len=-1);

    /** Reference SubTuple
     @param _start start of subrange
     @param _len length of subrange
     */
    Vector(TupleBase<T> &t, int _start, int _len=-1);

    /** Reference Vector<T>*
     */
    Vector(const Vector<T> *v);

    /** Reference Vector<T>&
     */
    Vector(const Vector<T> &v);

    /** Reference subrange Vector<T>*
     @param _start start of subrange
     @param _len length of subrange
     */
    Vector(const Vector<T> *v, int _start, int _len=-1);

    /** Reference subrange Vector<T>&
     @param _start start of subrange
     @param _len length of subrange
     */
    Vector(const Vector<T> &v, int _start, int _len=-1);

    /** union: v1 + v2
     */
    Vector(const Vector<T> *v1, const Vector<T> *v2);

    /** union: v1 + v2
     */
    Vector(const Vector<T> &v1, const Vector<T> &v2);
    
private:
    /** doDel - delete but don't shrink (used by replace)
     */
    Vector<T> *doDel(int where, int what);

public:

    /** mutator - remove range from Vector
     */
    Vector<T> *del(int where = 0, int what=-1);


    /** mutator - append values to right
     */
    Vector<T> *vconcat(const T *addT, int additional);


    /** mutator - append values to right
     */
    Vector<T> *vconcat(const T *addT) {
        return vconcat(addT, T::findNull(addT));
    }

    /** mutator - append values to right
     */
    Vector<T> *vconcat(const T &addT) {
        return vconcat(&addT, 1);
    }

    /** mutator - append values to right
     */
    Vector<T> *vconcat(const Segment<T> *addT) {
        return vconcat(addT->content(), addT->Length());
    }

    /** mutator - append values to right
     */
    Vector<T> *vconcat(const Segment<T> &addT) {
        return vconcat(addT.content(), addT.Length());
    }

    /** mutator - append values to right
     */
    Vector<T> *vconcat(int count, ...);

    /** mutator - insert values before point
     */
    Vector<T> *vinsert(int where, const T *addT, int what);

    /** mutator - insert values before point
     */
    Vector<T> *vinsert(int where, const T *addT) {
        return vinsert(where, addT, T::findNull(addT));
    }

    /** mutator - insert values before point
     */
    Vector<T> *vinsert(int where, const Segment<T> *addT) {
        return vinsert(where, addT->content(), addT->Length());
    }

    /** mutator - insert values before point
     */
    Vector<T> *vinsert(int where, const Segment<T> &addT) {
        return vinsert(where, addT.content(), addT.Length());
    }

    /** mutator - replace range with array
     */
    Vector<T> *Replace(int where, int what, const T *addT, int additional);

    /** mutator - replace range with array
     */
    Vector<T> *Replace(int where, int what, const T *addT) {
        return Replace(where, what, addT, T::findNull(addT));
    }

    /** mutator - replace range with array
     */
    Vector<T> *Replace(int where, int what, const Segment<T> *addT) {
        return Replace(where, what, addT->content(), addT->Length());
    }

    /** mutator - replace range with array
     */
    Vector<T> *Replace(int where, int what, const Vector<T> &addT) {
        return Replace(where, what, addT.content(), addT.Length());
    }

    ////////////////////////////////////
    // mutator - set operations on Vector
    /** add an element to the set, true if added
     */
    bool set_add(const T &d);

    /** delete an element from the set, true if deleted
     */
    bool set_remove(const T &d);

    /** convert Vector to set
     */
    void toset();
};

// reallocate Segment
template <class T>
void Vector<T>::rlloc(int newlen)
{
    assert(newlen > 0);
    check();					// validate the current state
    const int asize = actual_allocation(newlen);
    TupleBase<T> *newalloc = new (asize) TupleBase<T>(this, asize);
    if (allocation)
        allocation->dncount();		// destroy contents of old Tuple
    allocation = newalloc;		// make new allocation the current one
    allocation->upcount();		// bump refcount
    start = allocation->content();	// point Vector to correct Tuple
    T::init(start + len, asize - len);	// initialize new space in new Tuple
    len = newlen;			// set the new length
    check();				// test the result
}

// realloc Vector's Segment to accomodate new growth
// realloc uses extra_allocation to give each allocation more space for growth
template <class T>
void Vector<T>::resize(int newlen)
{
    assert(newlen > 0);
    if (!allocation) {
        rlloc(newlen);
    } else if (allocation->Length() < newlen) {
        // insufficient space, realloc
        if (newlen) {
            if (!start) {
                // this was a NULL Vector - allocate it some space
                const int asize = actual_allocation(newlen);
                check();
                allocation = new (asize) TupleBase<T>(asize);
                allocation->upcount();
                set(allocation->content(), newlen);// point Vector at Tuple
                T::init(start, newlen);	// initialize contents of new Segment 
                check();
            } else {
                rlloc(newlen);
            }
        } else {
            // Segment's shrunk to nothing, delete it and make a NULL Vector
            allocation->dncount();
            allocation = (TupleBase<T>*)0;
            (Segment<T>)*this = Segment<T>((T*)0,0);
        }
    } else {
        len = newlen;
    }
}

// reallocate Vector's Segment if it's shrunk below minimum utilization
template <class T>
void Vector<T>::shrink()
{
    if (start) {
        // it's not a NULL Vector
        if (!len) {
            // Segment's shrunk to nothing, delete it and make a NULL Vector
            allocation->dncount();
            allocation = (TupleBase<T>*)0;
            set(0, 0);
        } else if ((allocation->Length() / (int)minimum_occupancy) > len) {
            // the allocation Segment is under-occupied,
            // make a new tighter-fitting one
            rlloc(len);
        }
    }
    check();
}

// return an element of Vector, extending as required
template <class T>
T & Vector<T>::operator[] (int index) //const
{
    if (index >= len) {
        resize(index+1);	// extend the Vector
    len = index+1;
    check();
    }
    return start[index];
}

// consistency check
template <class T>
void Vector<T>::check(int i) const
{
    if (allocation) {
#ifdef BTREE_INTEGRITY
        Memory::Assert();
#endif
        assert(allocation->encloses(*this) || !"active doesn't enclose allocation");
        T::check(start, len);
    } else if (len) {
        assert(!"Null start pointer, non-zero length");
    }
    DEBLOG(cerr << "check: " << *this << '\n');
    
    while (--i) {
        // recursive - lets Vector<Vector<>> work
        (this+1)->check(i);
    }
}

// Null Vector constructor
template <class T>
Vector<T>::Vector()
    : Segment<T>(0),
      allocation((TupleBase<T>*)0)
{}

// construct a Vector with pre-allocation
template <class T>
Vector<T>::Vector(int size)
    : allocation(new (actual_allocation(size))
                 TupleBase<T>(actual_allocation(size)))
{
    set(allocation->content(), 0);
    allocation->upcount();
}

// Copy T* array of a given size
template <class T>
Vector<T>::Vector(const T *contentT, int size)
    : allocation(new (actual_allocation(size))
                 TupleBase<T>(actual_allocation(size)))
{
    set(allocation->content(), size);
    T::check(contentT, size);
    T::dup(start, contentT, size);
    allocation->upcount();
}

// Copy Segment
template <class T>
Vector<T>::Vector(const Segment<T> &contentT)
    : allocation(new (actual_allocation(len))
                 TupleBase<T>(actual_allocation(len)))
{
    set(allocation->content(), contentT.Length());
    T::check(contentT.content(), len);
    T::dup(start, contentT, len);
    allocation->upcount();
}

// Singleton T& Vector
template <class T>
Vector<T>::Vector(const T &contentT)
    : allocation(new (actual_allocation(1)) TupleBase<T>(actual_allocation(1)))
{
    set(allocation->content(), 1);
    *start = contentT;
    allocation->upcount();
}

// Repeat T& as array
template <class T>
Vector<T>::Vector(const T &contentT, int repetition)
    : allocation(new (actual_allocation(repetition))
                 TupleBase<T>(actual_allocation(repetition)))
{
    set(allocation->content(), repetition);
    T::segFill(start, contentT, repetition);
    allocation->upcount();
}

// Reference Tuple
template <class T>
Vector<T>::Vector(TupleBase<T> *t)
    : allocation(t)
{
    if (allocation) {
        set(allocation->content(), allocation->Length());
        check();
        allocation->upcount();
    }
}

// Reference Tuple
template <class T>
Vector<T>::Vector(TupleBase<T> &t)
    : allocation(&t)
{
    if (allocation) {
        set(allocation->content(), allocation->Length());
        check();
        allocation->upcount();
    }
}

// Reference Sub-Tuple
template <class T>
Vector<T>::Vector(TupleBase<T> *t, int _start, int _len)
    : allocation(t)
{
    if (allocation) {
        set(allocation->content() + _start,
            (_len < 0)?allocation->Length():_len);
        check();
        allocation->upcount();
    }
}

// Reference Sub-Tuple
template <class T>
Vector<T>::Vector(TupleBase<T> &t, int _start, int _len)
    : allocation(&t)
{
    if (allocation) {
        set(allocation->content() + _start,
            (_len < 0)?allocation->Length():_len);
        check();
        allocation->upcount();
    }
}

// Reference a Vector<T>'s contents
template <class T>
Vector<T>::Vector(const Vector<T> *v)
    : allocation(v->Allocation())
{
    if (allocation) {
        set(v->content(), v->Length());
        check();
        allocation->upcount();
    }
}

template <class T>
Vector<T>::Vector(const Vector<T> &v)
    : allocation(v.Allocation())
{
    if (allocation) {
        set(v.content(), v.Length());
        check();
        allocation->upcount();
    }
}

// Share a subrange of Vector<T>'s contents
template <class T>
Vector<T>::Vector(const Vector<T> *v, int _start, int _len)
    : allocation(v->Allocation())
{
    if (allocation) {
        set(v->content() + _start, (_len < 0) ?(v->Length() - _start) :_len);
        check();
        allocation->upcount();
    }
}

template <class T>
Vector<T>::Vector(const Vector<T> &v, int _start, int _len)
    : allocation(v.Allocation())
{
    if (allocation) {
        set(v.content() + _start, (_len < 0) ?(v.Length() - _start) :_len);
        check();
        allocation->upcount();
    }
}

// union vector constructor - returns a vector of v1 + v2
template <class T>
Vector<T>::Vector(const Vector<T> *v1, const Vector<T> *v2)
    : allocation(new (actual_allocation(v1->len + v2->len))
                 TupleBase<T>(actual_allocation(v1->len + v2->len)))
{
    v1->check();
    v2->check();
    
    set(allocation->content(), v1->len + v2->len);
    
    T::dup(start, v1->start, v1->len);
    T::dup(start + v1->len, v2->start, v2->len);
    allocation->upcount();
}

template <class T>
Vector<T>::Vector(const Vector<T> &v1, const Vector<T> &v2)
    : allocation(new (actual_allocation(v1.len + v2.len))
                 TupleBase<T>(actual_allocation(v1.len + v2.len)))
{
    v1.check();
    v2.check();
    
    set(allocation->content(), v1.len + v2.len);
    
    T::dup(start, v1.start, v1.len);
    T::dup(start + v1.len, v2.start, v2.len);
    allocation->upcount();
}

template <class T>
Vector<T>::~Vector()
{
    if (allocation) {
        allocation->dncount();
    }
}

// doDel - delete but don't shrink (used by replace)
template <class T>
Vector<T> *Vector<T>::doDel(int where, int what)
{
    DEBLOG(cerr << this << "->Vector<>::doDel("
           << where << ", " << what << ")\n");
    
    if (!start || len == 0)
        throw new Error("nil", 0, "Destination of delete is empty");
    
    // negative where is end-relative
    if ((where = endRelative(where)) < 0)
        throw new Error("range", where, "End is before start");
    
    if (where >= len)
        throw new Error("range", where, "Start is after end");
    
    // negative location is end-relative
    if (what < 0)
        what = len - where + what + 1;
    
    // check bounds on deletion
    if ((where + what) > len)
        throw new Error("range", where, "End is after end");
    
    // trivial deletion of no items
    if (what == 0)
        return this;
    
    // can't delete more than is there
    if (what > len)
        throw new Error("range", what, "Deleting too much");
    
    // check allocation's refcount, copy if necessary
    if (allocation && (allocation->refcount() > 1))
        rlloc(len);
    
    // destroy the segment to be elided
    T::destroy(start+where, what);
    check();
    
    if ((where+what) == len) {
        // reduction in strength - deletion at end just reduces len
        T::init(start + len - what, what);
        check();
    } else if (where == 0) {
        // reduction in strength - deletion at beginning just increases start
        T::init(start, what);
        check();
        start += what;
    } else {
        // move the relevant parts down
        T::move(start+where, start + where + what, len - where - what);
        check();
        T::init(start + len - what, what);
        check();
    }
    
    // adjust length
    len -= what;
    
    check();
    return this;
}

template <class T>
Vector<T> *Vector<T>::del(int where, int what)
{
    check();
    doDel(where, what);
    shrink();
    return this;
}

// append values to right
template <class T>
Vector<T> *Vector<T>::vconcat(const T *addT, int additional)
{
    check();
    
    // appending NULL always works
    if (!addT || !additional)
        return this;
    
    T::check(addT, additional);
    check();
    
    // check allocation's refcount, copy if necessary
    int oldlen = len;
    if (allocation && (allocation->refcount() > 1))
        rlloc(len + additional);
    else {
        resize(len + additional);	// ensure there's enough space
    }
    
    if (start + oldlen + additional > allocation->last()) {
        // insufficient space on the right.
        // we have to move the contents left anyway,
        // so move it to the allocation start
        T::move(allocation->content(), start, oldlen);
        start = allocation->content();
    }
    
    // there's now enough space to add to the right
    T::dup(start+oldlen, addT, additional);
    
    check();
    
    return this;
}

// insert values before point
template <class T>
Vector<T> *Vector<T>::vinsert(int where, const T *addT, int what)
{
    DEBLOG(cerr << this << "->Vector<>::vinsert("
           << where << ", " << addT << ", " << what << ")\n");
    
    check();

    // inserting null always succeeds
    if (!addT || !what)
        return this;
    
    T::check(addT, what);
    
    // negative where is end-relative
    if ((where = endRelative(where)) < 0)
        throw new Error("range", where, "End is before start");
    
    // check bounds on insertion point
    if (where > len)
        throw new Error("range", where, "Start is after end");
    
    if (where == len) {
        // strength-reduction: inserting at/after the end is adding
        return vconcat(addT, what);
    }
    
    // we're inserting contents in the middle
    
    // check allocation's refcount, copy if necessary
    int oldlen = len;
    if (allocation && (allocation->refcount() > 1))
        rlloc(len + what);
    else
        resize(len + what);
    
    DEBLOG(cerr << *this << ' ' << oldlen
           << " Vector<>::vinsert post-resize "
           << where << " " << what << '\n');

    if (start > allocation->content()) {
        // make hole leftward then fill it
        if (start - what > allocation->content()) {
            // we can slide the start left and do the complete insertion
            T::move(start - what, start, where);	// slide some left
            start -= what;				// adjust start
        } else {
            // there's not enough space to the left, but there's some,
            // slide left to the start of allocation
            int diff = what - (start - allocation->content());	// right-space needed
            T::move(start+where+diff, start + where, oldlen - where);	// slide some right
            T::move(allocation->content(), start, where);// slide some left
            start = allocation->content();		// adjust start
        }
    } else {
        // make hole rightward
        T::move(start+where+what, start + where, oldlen - where);// slide some right
    }

    T::init(start + where, what);	// zero the opened space
    T::dup(start+where, addT, what);	// add in new content

    check();

    return this;
}

// replace range with array
template <class T>
Vector<T> *Vector<T>::Replace(int where, int what,
                              const T *addT, int additional)
{
    DEBLOG(cerr << this << "->Vector<>::replace("
           << where << ", " << what << ", " << addT 
           << ", " << additional << ")\n");
    assert(what > 0);
    check();
    T::check(addT, additional);
    
    if ((where = endRelative(where)) < 0)
        throw new Error("range", where, "End is before start");
    
    doDel(where, what);
    if (where > len)
        where = len;
    
    return vinsert(where, addT, additional);
}

// add an element to the set, true if added
template <class T>
bool Vector<T>::set_add(const T &d)
{
    check();
    T::check(&d, 1);
    int index;
    if (tsearch(d, index)) {
        return false;
    } else {
        // not yet present
        vinsert(index, &d, 1);
        // qsort(); shouldn't be necessary if tsearch is stable
        return true;
    }
}

// delete an element from the set, true if deleted
template <class T>
bool Vector<T>::set_remove(const T &d)
{
    check();
    T::check(&d, 1);
    int pos;
    if (tsearch(d, pos)) {
        // found it
        doDel(pos, 1);
        shrink();
        return true;
    } else {
        // not present
        return false;
    }
}

// convert Vector to set
template <class T>
void Vector<T>::toset()
{
    check();
    
    if (len) {
        // check allocation's refcount, copy if necessary
        if (allocation->refcount() > 1)
            rlloc(len);
        
        qsort();
        
        // remove duplicates
        for (int i = 0; i < len - 1; i++) {
            if (*(start + i) == *(start + i+1)) {
                doDel(i,1);
                i--;
            }
        }
        shrink();
    }
}

// destroy contents
template <class T>
void Vector<T>::destroy(int len)
{
    assert(len > 0);
    allocation->dncount();
}

// dump Vector to file
template <class T>
ostream& operator<< (ostream& out,  const Vector<T> & vec)
{
    if (&vec && vec.allocation) {
        out.form("[0x%08x,%d] [0x%08x,%d] ",
                 vec.allocation->content(), vec.allocation->Length(),
                 vec.content(), vec.Length());
        out << "\"";
        const T *contents = vec.content();
        for (int i = 0; i < vec.Length(); i++)
            out << contents[i];
        out << "\"";
    } else {
        out << "[NULL]\n";
    }
    return out;
}

#endif

Generated by: colin@sharedtech.dhis.org on Sat Nov 6 11:59:24 199.