// $Id: tuple.h,v 1.19 2002/12/11 00:55:05 ericb Exp $ -*- c++ -*- // // This software is subject to the terms of the IBM Jikes Compiler // License Agreement available at the following URL: // http://ibm.com/developerworks/opensource/jikes. // Copyright (C) 1996, 1998, 1999, 2000, 2001, 2002 International Business // Machines Corporation and others. All Rights Reserved. // You must accept the terms of that agreement to use this software. // #ifndef tuple_INCLUDED #define tuple_INCLUDED #include "jikesapi.h" #ifdef HAVE_JIKES_NAMESPACE namespace Jikes { // Open namespace Jikes block #endif class OutputBuffer; // // This Tuple template class can be used to construct a dynamic array of // arbitrary objects. The space for the array is allocated in blocks of size // 2**LOG_BLKSIZE. In declaring a tuple the user may specify an estimate of // how many elements he expects. Based on that estimate, suitable values will // be calculated for log_blksize and base_increment. If these estimates are // off, more space will be allocated when needed. A tuple is also designed to // act efficiently as a stack of objects. // template class Tuple { protected: friend class OutputBuffer; enum { DEFAULT_LOG_BLKSIZE = 3, DEFAULT_BASE_INCREMENT = 4 }; T** base; // array of segments unsigned base_size; // number of segments unsigned top; // current number of elements unsigned size; // maximum number of elements unsigned log_blksize; // log2(elements per segment) unsigned base_increment; // number of segments to add when growing inline unsigned Blksize() const { return 1U << log_blksize; } // // Allocate another block of storage for the dynamic array. // inline void AllocateMoreSpace() { // // The variable size always indicates the maximum number of elements // that has been allocated for the array. Initially, it is set to 0 // to indicate that the array is empty. The pool of available // elements is divided into segments of size 2**log_blksize each. // Each segment is pointed to by a slot in the array base. // // By dividing size by the size of the segment we obtain the index // for the next segment in base. If base is full, it is reallocated. // unsigned k = size >> log_blksize; // which segment? // // If the base is overflowed, reallocate it and initialize the new // elements to NULL. // if (k == base_size) { unsigned old_base_size = base_size; T** old_base = base; base_size += base_increment; base = new T*[base_size]; if (old_base) { memcpy(base, old_base, old_base_size * sizeof(T*)); delete [] old_base; } memset(&base[old_base_size], 0, (base_size - old_base_size) * sizeof(T*)); } // // We allocate a new segment and place its adjusted address in // base[k]. The adjustment allows us to index the segment directly, // instead of having to perform a subtraction for each reference. // See operator[] below. Finally, we update size. // base[k] = (new T[Blksize()]) - size; size += Blksize(); } public: // // This function is invoked with an integer argument n. It ensures // that enough space is allocated for n elements in the dynamic array. // I.e., that the array will be indexable in the range (0..n-1). // // Note that this function can be used as a garbage collector. When // invoked with no argument(or 0), it frees up all dynamic space that // was allocated for the array. // inline void Resize(const unsigned n = 0) { // // If array did not previously contain enough space, allocate // the necessary additional space. Otherwise, if the array had // more blocks than are needed, release the extra blocks. // if (n > size) { do { AllocateMoreSpace(); } while (n > size); } else if (n < size) { // slot is the index of the base element whose block // will contain the (n-1)th element. int slot = (n == 0 ? -1 : (n - 1) >> log_blksize); for (int k = (size >> log_blksize) - 1; k > slot; k--) { size -= Blksize(); delete [] (base[k] + size); base[k] = NULL; } if (slot < 0) { delete [] base; base = NULL; base_size = 0; } } top = n; } // // This function is used to reset the size of a dynamic array without // allocating or deallocting space. It may be invoked with an integer // argument n which indicates the new size or with no argument which // indicates that the size should be reset to 0. // inline void Reset(const unsigned n = 0) { assert(n <= size); top = n; } // // Return length of the dynamic array. // inline unsigned Length() const { return top; } // // Return a reference to the ith element of the dynamic array. // inline T& operator[](const unsigned i) { // // See the comments above. We purposefully pre-adjusted the pointers // of base[n]; if we hadn't, we would be doing the less efficient // base[i >> log_blocksize][i & ~(-1 << log_blocksize)]. // assert(i < top); return base[i >> log_blksize][i]; } inline const T& operator[](const unsigned i) const { assert(i < top); return base[i >> log_blksize][i]; } // // Add an uninitialized element to the dynamic array and return its index. // inline unsigned NextIndex() { unsigned i = top++; if (i == size) AllocateMoreSpace(); return i; } // // Push an element on the top of the dynamic array. // inline void Push(const T& elt) { this -> Next() = elt; } // // Return the top element of the dynamic array, removing it. // inline T Pop() { // Not "return (*this)[--top]" because that may violate an invariant // in operator[]. assert(top); top--; return base[top >> log_blksize][top]; } // // Return the top element of the dynamic array, leaving it in place. // inline T& Top() { assert(top); return (*this)[top - 1]; } inline const T& Top() const { assert(top); return (*this)[top - 1]; } // // Add an element to the dynamic array and return a reference to // that new element. // inline T& Next() { unsigned i = NextIndex(); return base[i >> log_blksize][i]; } // // Assignment of a dynamic array to another. // inline Tuple& operator=(const Tuple& rhs) { if (this != &rhs) { Resize(rhs.top); for (unsigned i = 0; i < rhs.top; i++) base[i >> log_blksize][i] = rhs.base[i >> log_blksize][i]; } return *this; } // // Constructor of a Tuple, with an estimate of final size. // Tuple(const unsigned estimate = 0) { if (estimate == 0) { log_blksize = DEFAULT_LOG_BLKSIZE; base_increment = DEFAULT_BASE_INCREMENT; } else { for (log_blksize = 1; (1U << log_blksize) < estimate && log_blksize < 31; log_blksize++) ; if (log_blksize <= DEFAULT_LOG_BLKSIZE) base_increment = 1; else if (log_blksize < 13) { base_increment = 1U << (log_blksize - 4); log_blksize = 4; } else { base_increment = 1U << (log_blksize - 8); log_blksize = 8; } // Add a little margin to avoid reallocating the base. base_increment++; } base_size = 0; size = 0; top = 0; base = NULL; } // // Constructor of a Tuple, with specified segment size and base increment. // For example, Tuple(3, 4) will allocate memory every 2**3 (or 8) // elements, and will reallocate the base every 4 segments (or 32 // elements). Fine tuning these parameters allows for more efficient // memory usage. // Tuple(const unsigned log_blksize_, const unsigned base_increment_) : log_blksize(log_blksize_), base_increment(base_increment_) { base_size = 0; size = 0; top = 0; base = NULL; } // // Initialization of a dynamic array from an existing one. // Tuple(const Tuple& rhs) : log_blksize(rhs.log_blksize), base_increment(rhs.base_increment) { base_size = 0; size = 0; base = NULL; *this = rhs; } // // Destructor of a dynamic array. // virtual ~Tuple() { Resize(0); } // // Return the total size of temporary space allocated. // inline size_t SpaceAllocated() const { return base_size * sizeof(T*) + size * sizeof(T); } // // Return the total size of temporary space used. // inline size_t SpaceUsed() const { return (size >> log_blksize) * sizeof(T*) + top * sizeof(T); } }; // // This class is similar to Tuple, in that it is a template class used to // construct an array of arbitrary objects. However, this class is designed // to grow dynamically until all elements are inserted, then it is "frozen" // by calling Array(). This moves all elements into continguous memory // locations, allowing more efficient use of memory and traversal of // elements. Also, a ConvertibleArray is not designed to perform stack // operations the way Tuple can. // template class ConvertibleArray : protected Tuple { public: // // Construct a dynamic array with an estimate for maximum size. // ConvertibleArray(const unsigned estimate = 0) : Tuple(estimate), array(NULL) {} // // Constructor a dynamic array with specified segment size and base // increment. For example, ConvertibleArray(3, 4) will allocate memory // every 2**3 (or 8) elements, and will reallocate the base every 4 // segments (or 32 elements). Fine tuning these parameters allows for // more efficient memory usage. // ConvertibleArray(const unsigned log_blksize, const unsigned base_increment) : Tuple(log_blksize, base_increment), array(NULL) {} virtual ~ConvertibleArray() { delete [] array; } // // This function converts a tuple into a regular array and destroys the // original tuple. Once called, the tuple can no longer grow. // inline T* Array() { if (! array) Compact(); return array; } inline const T* Array() const { if (! array) { // // We must discard the const qualifier of this; but it is safe // since we are not changing the contents (just the storage). // #ifdef HAVE_CONST_CAST (const_cast*> (this)) -> Compact(); #else ((ConvertibleArray*) this) -> Compact(); #endif // HAVE_CONST_CAST } return array; } // // Access an element of the array. // inline T& operator[](const unsigned i) { assert(i < Tuple::top); return array ? array[i] : Tuple::base[i >> Tuple::log_blksize][i]; } inline const T& operator[](const unsigned i) const { assert(i < Tuple::top); return array ? array[i] : Tuple::base[i >> Tuple::log_blksize][i]; } // // Returns the index of the next element, if this has not yet been // converted to an array. // inline unsigned NextIndex() { assert(! array); return Tuple::NextIndex(); } // // Returns the next element, if this has not yet been converted to // an array. // inline T& Next() { assert(! array); return Tuple::Next(); } // // These methods of Tuple work as is. // using Tuple::Length; using Tuple::SpaceUsed; inline size_t SpaceAllocated() { return array ? SpaceUsed() : Tuple::SpaceAllocated(); } private: // // Compact the array into a single contiguous chunk of memory, prohibiting // further dynamic growth. // void Compact() { // Special case an empty ConvertibleArray, since some compilers have // problems with 0-length arrays. if (! Tuple::top) { array = new T[1]; Tuple::Resize(); return; } array = new T[Tuple::top]; unsigned i = 0; unsigned processed_size = 0; // the last non-empty slot! unsigned n = (Tuple::top - 1) >> Tuple::log_blksize; while (i < n) { memcpy(array + processed_size, Tuple::base[i] + processed_size, Tuple::Blksize() * sizeof(T)); delete [] (Tuple::base[i] + processed_size); i++; processed_size += Tuple::Blksize(); } memcpy(array + processed_size, Tuple::base[n] + processed_size, (Tuple::top - processed_size) * sizeof(T)); delete [] (Tuple::base[n] + processed_size); delete [] Tuple::base; Tuple::base = NULL; Tuple::size = 0; } T* array; }; // // This class allows the building of an output file in memory, before writing // it all at once. // class OutputBuffer { public: OutputBuffer(unsigned log_blksize = 13, unsigned base_increment = 128) : buffer(log_blksize, base_increment) {} inline void PutU1(u1 u) { buffer.Next() = u; } inline void PutU2(u2 u) { buffer.Next() = u >> 8; buffer.Next() = u & 0xff; } inline void PutU4(u4 u) { buffer.Next() = u >> 24; buffer.Next() = (u >> 16) & 0xff; buffer.Next() = (u >> 8) & 0xff; buffer.Next() = u & 0xff; } inline void PutN(const u1* u, int n) { while (--n >= 0) buffer.Next() = *u++; } inline bool WriteToFile(const char* file_name) { JikesAPI::FileWriter* file = JikesAPI::getInstance() -> write(file_name, buffer.top); // NB if file was invalid it would have been destroyed by write() if (file == NULL) return false; unsigned size = 0; // the last non-empty slot! unsigned n = (buffer.top - 1) >> buffer.log_blksize; for (unsigned i = 0; i < n; i++) { file -> write(buffer.base[i] + size, buffer.Blksize()); size += buffer.Blksize(); } file -> write(buffer.base[n] + size, buffer.top - size); delete file; return true; } private: Tuple buffer; }; #ifdef HAVE_JIKES_NAMESPACE } // Close namespace Jikes block #endif #endif // tuple_INCLUDED