// array.h see license.txt for copyright and terms of use // some array classes #ifndef ARRAY_H #define ARRAY_H #include "xassert.h" // xassert #include // qsort // -------------------- Array ---------------------- // This is the same as C++'s built-in array, but automatically deallocates. // If you want bounds checking too, use GrowArray, below. template class Array { private: // data T *arr; private: // not allowed Array(Array&); void operator=(Array&); public: Array(int len) : arr(new T[len]) {} ~Array() { delete[] arr; } T const &operator[] (int i) const { return arr[i]; } T &operator[] (int i) { return arr[i]; } operator T const* () const { return arr; } operator T const* () { return arr; } operator T * () { return arr; } T const *ptrC() const { return arr; } T *ptr() { return arr; } T const* operator+ (int i) const { return arr+i; } T * operator+ (int i) { return arr+i; } // convenience void setAll(T val, int len) { for (int i=0; i class GrowArray { private: // data T *arr; // underlying array; NULL if sz==0 int sz; // # allocated entries in 'arr' private: // funcs void bc(int i) const // bounds-check an index { xassert((unsigned)i < (unsigned)sz); } void eidLoop(int index); // make 'this' equal to 'obj' void copyFrom(GrowArray const &obj) { setSize(obj.size()); // not terribly efficient, oh well copyFrom_limit(obj, sz); } protected: // funcs void copyFrom_limit(GrowArray const &obj, int limit); private: // disallowed void operator=(GrowArray&); void operator==(GrowArray&); public: // funcs GrowArray(int initSz); GrowArray(GrowArray const &obj) : arr(0), sz(0) { copyFrom(obj); } ~GrowArray(); GrowArray& operator=(GrowArray const &obj) { copyFrom(obj); return *this; } // allocated space int size() const { return sz; } // element access T const& operator[] (int i) const { bc(i); return arr[i]; } T & operator[] (int i) { bc(i); return arr[i]; } // set size, reallocating if old size is different; if the // array gets bigger, existing elements are preserved; if the // array gets smaller, elements are truncated void setSize(int newSz); // make sure there are at least 'minSz' elements in the array; void ensureAtLeast(int minSz) { if (minSz > sz) { setSize(minSz); } } // grab a read-only pointer to the raw array T const *getArray() const { return arr; } // grab a writable pointer; use with care T *getDangerousWritableArray() { return arr; } T *getArrayNC() { return arr; } // ok, not all that dangerous.. // make sure the given index is valid; if this requires growing, // do so by doubling the size of the array (repeatedly, if // necessary) void ensureIndexDoubler(int index) { if (sz-1 < index) { eidLoop(index); } } // set an element, using the doubler if necessary void setIndexDoubler(int index, T const &value) { ensureIndexDoubler(index); arr[index] = value; } // swap my data with the data in another GrowArray object void swapWith(GrowArray &obj) { T *tmp1 = obj.arr; obj.arr = this->arr; this->arr = tmp1; int tmp2 = obj.sz; obj.sz = this->sz; this->sz = tmp2; } // set all elements to a single value void setAll(T val) { for (int i=0; i GrowArray::GrowArray(int initSz) { sz = initSz; if (sz > 0) { arr = new T[sz]; } else { arr = NULL; } } template GrowArray::~GrowArray() { if (arr) { delete[] arr; } } template void GrowArray::copyFrom_limit(GrowArray const &obj, int limit) { for (int i=0; i void GrowArray::setSize(int newSz) { if (newSz != sz) { // keep track of old int oldSz = sz; T *oldArr = arr; // make new sz = newSz; if (sz > 0) { arr = new T[sz]; } else { arr = NULL; } // copy elements in common for (int i=0; i void GrowArray::eidLoop(int index) { if (sz-1 >= index) { return; } int newSz = sz; while (newSz-1 < index) { #ifndef NDEBUG_NO_ASSERTIONS // silence warning.. int prevSz = newSz; #endif if (newSz == 0) { newSz = 1; } newSz = newSz*2; xassert(newSz > prevSz); // otherwise overflow -> infinite loop } setSize(newSz); } // ---------------------- ArrayStack --------------------- // This is an array where some of the array is unused. Specifically, // it maintains a 'length', and elements 0 up to length-1 are // considered used, whereas length up to size-1 are unused. The // expected use is as a stack, where "push" adds a new (used) element. template class ArrayStack : public GrowArray { private: int len; // # of elts in the stack public: ArrayStack(int initArraySize = 10) : GrowArray(initArraySize), len(0) {} ArrayStack(ArrayStack const &obj) : GrowArray(obj), len(obj.len) {} ~ArrayStack(); // copies contents of 'obj', but the allocated size of 'this' will // only change when necessary ArrayStack& operator=(ArrayStack const &obj) { this->ensureIndexDoubler(obj.length() - 1); this->copyFrom_limit(obj, obj.length()); len = obj.len; return *this; } // element access; these declarations are necessary because // the uses of 'operator[]' below are not "dependent", hence // they can't use declarations inherited from GrowArray T const& operator[] (int i) const { return GrowArray::operator[](i); } T & operator[] (int i) { return GrowArray::operator[](i); } void push(T const &val) { setIndexDoubler(len++, val); } T pop() { return operator[](--len); } T const &top() const { return operator[](len-1); } T &top() { return operator[](len-1); } // alternate interface, where init/deinit is done explicitly // on returned references T &pushAlt() // returns newly accessible item { GrowArray::ensureIndexDoubler(len++); return top(); } T &popAlt() // returns item popped { return operator[](--len); } // items stored int length() const { return len; } bool isEmpty() const { return len==0; } bool isNotEmpty() const { return !isEmpty(); } void popMany(int ct) { len -= ct; xassert(len >= 0); } void empty() { len = 0; } // useful when someone has used 'getDangerousWritableArray' to // fill the array's internal storage void setLength(int L) { len = L; } // consolidate allocated space to match length void consolidate() { this->setSize(length()); } // swap void swapWith(ArrayStack &obj) { GrowArray::swapWith(obj); int tmp = obj.len; obj.len = this->len; this->len = tmp; } void sort(int (*compare)(T const *t1, T const *t2)) { qsort(GrowArray::getArrayNC(), len, sizeof(T), (int (*)(void const*, void const*))compare ); } }; template ArrayStack::~ArrayStack() {} // iterator over contents of an ArrayStack, to make it easier to // switch between it and SObjList as a representation template class ArrayStackIterNC { NO_OBJECT_COPIES(ArrayStackIterNC); // for now private: // data ArrayStack /*const*/ &arr; // array being accessed int index; // current element public: // funcs ArrayStackIterNC(ArrayStack /*const*/ &a) : arr(a), index(0) {} // iterator actions bool isDone() const { return index >= arr.length(); } void adv() { xassert(!isDone()); index++; } T /*const*/ *data() const { return &(arr[index]); } }; // I want const polymorphism! // pop (and discard) a value off a stack at end of scope template class ArrayStackPopper { private: ArrayStack &stk; public: ArrayStackPopper(ArrayStack &s) : stk(s) {} ArrayStackPopper(ArrayStack &s, T const &pushVal) : stk(s) { stk.push(pushVal); } ~ArrayStackPopper() { stk.pop(); } }; // ------------------- ObjArrayStack ----------------- // an ArrayStack of owner pointers template class ObjArrayStack { private: // data ArrayStack arr; public: // funcs ObjArrayStack(int initArraySize = 10) : arr(initArraySize) {} ~ObjArrayStack() { deleteAll(); } void push(T *ptr) { arr.push(ptr); } T *pop() { return arr.pop(); } T const *topC() const { return arr.top(); } T *top() { return arr.top(); } T const * operator[](int index) const { return arr[index]; } T * operator[](int index) { return arr[index]; } int length() const { return arr.length(); } bool isEmpty() const { return arr.isEmpty(); } bool isNotEmpty() const { return !isEmpty(); } void deleteTopSeveral(int ct); void deleteAll() { deleteTopSeveral(length()); } // remove an element from the middle, shifting others down to // maintain the original order T *removeIntermediate(int toRemove); // will not delete any items void consolidate() { arr.consolidate(); } void swapWith(ObjArrayStack &obj) { arr.swapWith(obj.arr); } }; template void ObjArrayStack::deleteTopSeveral(int ct) { while (ct--) { delete pop(); } } template T *ObjArrayStack::removeIntermediate(int toRemove) { T *ret = arr[toRemove]; // shift remaining elements down for (int i=toRemove+1; i < length(); i++) { arr[i-1] = arr[i]; } // remove and throw away the final (now redundant) pointer pop(); return ret; } // ------------------------- ArrayStackEmbed -------------------------- // This is like ArrayStack, but the first 'n' elements are stored // embedded in this object, instead of allocated on the heap; in some // circumstances, this lets us avoid allocating memory in common cases. // // For example, suppose you have an algorithm that is usually given a // small number of elements, say 1 or 2, but occasionally needs to // work with more. If you put the array of elements in the heap, then // even in the common case a heap allocation is required, which is // bad. But by using ArrayStackEmbed, you can be sure that if // the number of elements is <= 2 there will be no heap allocation, // even though you still get a uniform (array-like) interface to all // the elements. template class ArrayStackEmbed { private: // data // embedded storage T embed[n]; // heap-allocated storage GrowArray heap; // total number of elements in the stack; if this // exceeds 'n', then heap.arr is non-NULL int len; private: // funcs void bc(int i) const // bounds-check an index { xassert((unsigned)i < (unsigned)len); } public: // funcs ArrayStackEmbed() : /*embed is default-init'd*/ heap(0), // initially a NULL ptr len(0) {} ~ArrayStackEmbed() {} // heap auto-deallocs its internal data void push(T const &val) { if (len < n) { embed[len++] = val; } else { heap.setIndexDoubler(len++ - n, val); } } T pop() { xassert(len > 0); if (len <= n) { return embed[--len]; } else { return heap[--len - n]; } } int length() const { return len; } bool isEmpty() const { return len==0; } bool isNotEmpty() const { return !isEmpty(); } // direct element access T const &getElt(int i) const { bc(i); if (i < n) { return embed[i]; } else { return heap[i - n]; } } T const& operator[] (int i) const { return getElt(i); } T & operator[] (int i) { return const_cast(getElt(i)); } T const &top() const { return getElt(len-1); } }; #endif // ARRAY_H