gerv%gerv.net 912583b021 Bug 236613: change to MPL/LGPL/GPL tri-license.
git-svn-id: svn://10.0.0.236/trunk@155485 18797224-902f-48f8-a5cc-f745e15eee43
2004-04-25 15:37:13 +00:00

747 lines
27 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the JavaScript 2 Prototype.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef ds_h___
#define ds_h___
#include <memory>
#include "utilities.h"
namespace JavaScript
{
//
// Save-Restore Pattern
//
// Use the definition
// SaveRestore<T> temp(var)
// to save the current value of var at the time of the definition into a temporary temp
// and restore var to the saved value at the end of temp's scope, regardless of whether
// temp goes out of scope due to normal execution or due to a thrown exception.
template<typename T> class SaveRestore {
const T savedValue;
T &var;
public:
SaveRestore(T &t): savedValue(t), var(t) {}
~SaveRestore() {var = savedValue;}
};
//
// Doubly Linked Lists
//
// A ListQueue provides insert and delete operations on a doubly-linked list of
// objects threaded through fields named 'next' and 'prev'. The type parameter
// E must be a class derived from ListQueueEntry.
// The ListQueue does not own its elements. They must be deleted explicitly if
// needed.
struct ListQueueEntry {
ListQueueEntry *next; // Next entry in linked list
ListQueueEntry *prev; // Previous entry in linked list
#ifdef DEBUG
ListQueueEntry(): next(0), prev(0) {}
#endif
};
template <class E>
struct ListQueue: private ListQueueEntry {
ListQueue() {next = this; prev = this;}
// Return true if the ListQueue is nonempty.
operator bool() const {return next != static_cast<const ListQueueEntry *>(this);}
// Return true if the ListQueue is empty.
bool operator !() const {return next == static_cast<const ListQueueEntry *>(this);}
E &front() const {ASSERT(operator bool()); return *static_cast<E *>(next);}
E &back() const {ASSERT(operator bool()); return *static_cast<E *>(prev);}
void push_front(E &elt) {
ASSERT(!elt.next && !elt.prev);
elt.next = next; elt.prev = this; next->prev = &elt; next = &elt;
}
void push_back(E &elt) {
ASSERT(!elt.next && !elt.prev);
elt.next = this; elt.prev = prev; prev->next = &elt; prev = &elt;
}
E &pop_front() {
ASSERT(operator bool());
E *elt = static_cast<E *>(next); next = elt->next; next->prev = this;
DEBUG_ONLY(elt->next = 0; elt->prev = 0;) return *elt;
}
E &pop_back() {
ASSERT(operator bool());
E *elt = static_cast<E *>(prev); prev = elt->prev; prev->next = this;
DEBUG_ONLY(elt->next = 0; elt->prev = 0;);
return *elt;
}
};
//
// Growable Arrays
//
// A Buffer initially points to inline storage of initialSize elements of type T.
// The Buffer can be expanded via the expand method to increase its size by
// allocating storage from the heap.
template <typename T, size_t initialSize> class Buffer {
public:
T *buffer; // Pointer to the current buffer
size_t size; // Current size of the buffer
private:
T initialBuffer[initialSize]; // Initial buffer
public:
Buffer(): buffer(initialBuffer), size(initialSize) {}
~Buffer() {if (buffer != initialBuffer) delete[] buffer;}
void expand(size_t newSize);
};
// Expand the buffer to size newSize, which must be greater than the current
// size. The buffer's contents are not preserved.
template <typename T, size_t initialSize>
inline void Buffer<T, initialSize>::expand(size_t newSize) {
ASSERT(newSize > size);
if (buffer != initialBuffer) {
delete[] buffer;
buffer = 0; // For exception safety if the allocation below fails.
}
buffer = new T[newSize];
size = newSize;
}
// See ArrayBuffer below.
template <typename T> class RawArrayBuffer {
T *const cache; // Pointer to a fixed-size cache for holding the buffer if it's small enough
protected:
T *buffer; // Pointer to the current buffer
size_t length; // Logical size of the buffer
size_t bufferSize; // Physical size of the buffer
#ifdef DEBUG
size_t maxReservedSize; // Maximum size reserved so far
#endif
public:
RawArrayBuffer(T *cache, size_t cacheSize) :
cache(cache), buffer(cache), length(0), bufferSize(cacheSize) {
DEBUG_ONLY(maxReservedSize = 0);
}
private:
RawArrayBuffer(const RawArrayBuffer&); // No copy constructor
void operator=(const RawArrayBuffer&); // No assignment operator
public:
~RawArrayBuffer() {if (buffer != cache) delete[] buffer;}
private:
void enlarge(size_t newLength);
public:
// Methods that do not expand the buffer cannot throw exceptions.
size_t size() const {return length;}
operator bool() const {return length != 0;}
bool operator !() const {return length == 0;}
T &front() {ASSERT(length); return *buffer;}
const T &front() const {ASSERT(length); return *buffer;}
T &back() {ASSERT(length); return buffer[length-1];}
const T &back() const {ASSERT(length); return buffer[length-1];}
T *contents() const {return buffer;}
void reserve(size_t nElts);
T *reserve_back(size_t nElts = 1);
T *advance_back(size_t nElts = 1);
T *reserve_advance_back(size_t nElts = 1);
void fast_push_back(const T &elt);
void push_back(const T &elt);
void append(const T *elts, size_t nElts);
void append(const T *begin, const T *end) {ASSERT(end >= begin); append(begin, toSize_t(end - begin));}
T &pop_back() {ASSERT(length); return buffer[--length];}
};
// Enlarge the buffer so that it can hold at least newLength elements.
// May throw an exception, in which case the buffer is left unchanged.
template <typename T>
void RawArrayBuffer<T>::enlarge(size_t newLength) {
size_t newBufferSize = bufferSize * 2;
if (newBufferSize < newLength)
newBufferSize = newLength;
auto_ptr<T> newBuffer(new T[newBufferSize]);
T *oldBuffer = buffer;
std::copy(oldBuffer, oldBuffer + length, newBuffer.get());
buffer = newBuffer.release();
if (oldBuffer != cache)
delete[] oldBuffer;
bufferSize = newBufferSize;
}
// Ensure that there is room to hold nElts elements in the buffer, without
// expanding the buffer's logical length.
// May throw an exception, in which case the buffer is left unchanged.
template <typename T>
inline void RawArrayBuffer<T>::reserve(size_t nElts) {
if (bufferSize < nElts)
enlarge(nElts);
#ifdef DEBUG
if (maxReservedSize < nElts)
maxReservedSize = nElts;
#endif
}
// Ensure that there is room to hold nElts more elements in the buffer, without
// expanding the buffer's logical length. Return a pointer to the first element
// just past the logical length.
// May throw an exception, in which case the buffer is left unchanged.
template <typename T>
inline T *RawArrayBuffer<T>::reserve_back(size_t nElts) {
reserve(length + nElts);
return buffer[length];
}
// Advance the logical length by nElts, assuming that the memory has previously
// been reserved.
// Return a pointer to the first new element.
template <typename T>
inline T *RawArrayBuffer<T>::advance_back(size_t nElts) {
ASSERT(length + nElts <= maxReservedSize);
T *p = buffer + length;
length += nElts;
return p;
}
// Combine the effects of reserve_back and advance_back.
template <typename T>
inline T *RawArrayBuffer<T>::reserve_advance_back(size_t nElts) {
reserve(length + nElts);
T *p = buffer + length;
length += nElts;
return p;
}
// Same as push_back but assumes that the memory has previously been reserved.
// May throw an exception if copying elt throws one, in which case the buffer is
// left unchanged.
template <typename T>
inline void RawArrayBuffer<T>::fast_push_back(const T &elt) {
ASSERT(length < maxReservedSize);
buffer[length] = elt;
++length;
}
// Append elt to the back of the buffer.
// May throw an exception, in which case the buffer is left unchanged.
template <typename T>
inline void RawArrayBuffer<T>::push_back(const T &elt) {
*reserve_back() = elt;
++length;
}
// Append nElts elements elts to the back of the array buffer.
// May throw an exception, in which case the buffer is left unchanged.
template <typename T>
void RawArrayBuffer<T>::append(const T *elts, size_t nElts) {
size_t newLength = length + nElts;
if (newLength > bufferSize)
enlarge(newLength);
std::copy(elts, elts + nElts, buffer + length);
length = newLength;
}
// An ArrayBuffer represents an array of elements of type T. The ArrayBuffer
// contains storage for a fixed size array of cacheSize elements; if this size
// is exceeded, the ArrayBuffer allocates the array from the heap. Elements can
// be appended to the back of the array using append. An ArrayBuffer can also
// act as a stack: elements can be pushed and popped from the back.
//
// All ArrayBuffer operations are atomic with respect to exceptions -- either
// they succeed or they do not affect the ArrayBuffer's existing elements and
// length. If T has a constructor, it must have a constructor with no arguments;
// that constructor is called at the time memory for the ArrayBuffer is
// allocated, just like when allocating a regular C++ array.
template <typename T, size_t cacheSize>
class ArrayBuffer: public RawArrayBuffer<T> {
T cacheArray[cacheSize];
public:
ArrayBuffer(): RawArrayBuffer<T>(cacheArray, cacheSize) {}
};
//
// Bit Sets
//
template<size_t size> class BitSet {
STATIC_CONST(size_t, nWords = (size+31)>>5);
STATIC_CONST(uint32, lastWordMask = (2u<<((size-1)&31)) - 1);
uint32 words[nWords]; // Bitmap; the first word contains bits 0(LSB)...31(MSB), the second contains bits 32...63, etc.
public:
void clear() {zero(words, words+nWords);}
BitSet() {clear();}
// Construct a BitSet out of an array of alternating low (inclusive)
// and high (exclusive) ends of ranges of set bits.
// The array is terminated by a 0,0 range.
template<typename In> explicit BitSet(const In *a) {
clear();
size_t low, high;
while (low = *a++, (high = *a++) != 0) setRange(low, high);
}
bool operator[](size_t i) const {ASSERT(i < size); return static_cast<bool>(words[i>>5]>>(i&31) & 1);}
bool none() const;
bool operator==(const BitSet &s) const;
bool operator!=(const BitSet &s) const;
void set(size_t i) {ASSERT(i < size); words[i>>5] |= 1u<<(i&31);}
void reset(size_t i) {ASSERT(i < size); words[i>>5] &= ~(1u<<(i&31));}
void flip(size_t i) {ASSERT(i < size); words[i>>5] ^= 1u<<(i&31);}
void setRange(size_t low, size_t high);
void resetRange(size_t low, size_t high);
void flipRange(size_t low, size_t high);
};
// Return true if all bits are clear.
template<size_t size>
inline bool BitSet<size>::none() const {
if (nWords == 1)
return !words[0];
else {
const uint32 *w = words;
while (w != words + nWords)
if (*w++)
return false;
return true;
}
}
// Return true if the BitSets are equal.
template<size_t size>
inline bool BitSet<size>::operator==(const BitSet &s) const {
if (nWords == 1)
return words[0] == s.words[0];
else
return std::equal(words, s.words);
}
// Return true if the BitSets are not equal.
template<size_t size>
inline bool BitSet<size>::operator!=(const BitSet &s) const {
return !operator==(s);
}
// Set all bits between low inclusive and high exclusive.
template<size_t size>
void BitSet<size>::setRange(size_t low, size_t high) {
ASSERT(low <= high && high <= size);
if (low != high)
if (nWords == 1)
words[0] |= (2u<<(high-1)) - (1u<<low);
else {
--high;
uint32 *w = words + (low>>5);
uint32 *wHigh = words + (high>>5);
uint32 l = 1u << (low&31);
uint32 h = 2u << (high&31);
if (w == wHigh)
*w |= h - l;
else {
*w++ |= -l;
while (w != wHigh)
*w++ = static_cast<uint32>(-1);
*w |= h - 1;
}
}
}
// Clear all bits between low inclusive and high exclusive.
template<size_t size>
void BitSet<size>::resetRange(size_t low, size_t high) {
ASSERT(low <= high && high <= size);
if (low != high)
if (nWords == 1)
words[0] &= (1u<<low) - 1 - (2u<<(high-1));
else {
--high;
uint32 *w = words + (low>>5);
uint32 *wHigh = words + (high>>5);
uint32 l = 1u << (low&31);
uint32 h = 2u << (high&31);
if (w == wHigh)
*w &= l - 1 - h;
else {
*w++ &= l - 1;
while (w != wHigh)
*w++ = 0;
*w &= -h;
}
}
}
// Invert all bits between low inclusive and high exclusive.
template<size_t size>
void BitSet<size>::flipRange(size_t low, size_t high) {
ASSERT(low <= high && high <= size);
if (low != high)
if (nWords == 1)
words[0] ^= (2u<<(high-1)) - (1u<<low);
else {
--high;
uint32 *w = words + (low>>5);
uint32 *wHigh = words + (high>>5);
uint32 l = 1u << (low&31);
uint32 h = 2u << (high&31);
if (w == wHigh)
*w ^= h - l;
else {
*w++ ^= -l;
while (w != wHigh)
*w++ ^= static_cast<uint32>(-1);
*w ^= h - 1;
}
}
}
//
// Array Queues
//
// See ArrayQueue below.
template <typename T> class RawArrayQueue {
T *const cache; // Pointer to a fixed-size cache for holding the buffer if it's small enough
protected:
T *buffer; // Pointer to the current buffer
T *bufferEnd; // Pointer to the end of the buffer
T *f; // Front end of the circular buffer, used for reading elements; buffer <= f < bufferEnd
T *b; // Back end of the circular buffer, used for writing elements; buffer < b <= bufferEnd
size_t length; // Number of elements used in the circular buffer
size_t bufferSize; // Physical size of the buffer
#ifdef DEBUG
size_t maxReservedSize; // Maximum size reserved so far
#endif
public:
RawArrayQueue(T *cache, size_t cacheSize):
cache(cache), buffer(cache), bufferEnd(cache + cacheSize),
f(cache), b(cache), length(0), bufferSize(cacheSize)
{DEBUG_ONLY(maxReservedSize = 0);}
private:
RawArrayQueue(const RawArrayQueue&); // No copy constructor
void operator=(const RawArrayQueue&); // No assignment operator
public:
~RawArrayQueue() {if (buffer != cache) delete[] buffer;}
private:
void enlarge(size_t newLength);
public:
// Methods that do not expand the buffer cannot throw exceptions.
size_t size() const {return length;}
operator bool() const {return length != 0;}
bool operator !() const {return length == 0;}
T &front() {ASSERT(length); return *f;}
const T &front() const {ASSERT(length); return *f;}
T &back() {ASSERT(length); return b[-1];}
const T &back() const {ASSERT(length); return b[-1];}
T &pop_front() {
ASSERT(length);
--length;
T &elt = *f++;
if (f == bufferEnd)
f = buffer;
return elt;
}
size_t pop_front(size_t nElts, T *&begin, T *&end);
T &pop_back() {
ASSERT(length);
--length;
T &elt = *--b;
if (b == buffer)
b = bufferEnd;
return elt;
}
void reserve_back();
void reserve_back(size_t nElts);
T *advance_back();
T *advance_back(size_t nElts, size_t &nEltsAdvanced);
void fast_push_back(const T &elt);
void push_back(const T &elt);
// Same as append but assumes that memory has previously been reserved.
// Does not throw exceptions. T::operator= must not throw exceptions.
template <class InputIter>
void fast_append(InputIter begin, InputIter end) {
size_t nElts = toSize_t(std::distance(begin, end));
ASSERT(length + nElts <= maxReservedSize);
while (nElts) {
size_t nEltsAdvanced;
T *dst = advance_back(nElts, nEltsAdvanced);
nElts -= nEltsAdvanced;
while (nEltsAdvanced--) {
*dst = *begin; ++dst; ++begin;
}
}
}
// Append elements from begin to end to the back of the queue.
// T::operator= must not throw exceptions.
// reserve_back may throw an exception, in which case the queue is left
// unchanged.
template <class InputIter> void append(InputIter begin, InputIter end) {
size_t nElts = toSize_t(std::distance(begin, end));
reserve_back(nElts);
while (nElts) {
size_t nEltsAdvanced;
T *dst = advance_back(nElts, nEltsAdvanced);
nElts -= nEltsAdvanced;
while (nEltsAdvanced--) {
*dst = *begin; ++dst; ++begin;
}
}
}
};
// Pop between one and nElts elements from the front of the queue. Set begin
// and end to an array of the first n elements, where n is the return value.
// The popped elements may be accessed until the next non-const operation.
// Does not throw exceptions.
template <typename T>
size_t RawArrayQueue<T>::pop_front(size_t nElts, T *&begin, T *&end) {
ASSERT(nElts <= length);
begin = f;
size_t eltsToEnd = toSize_t(bufferEnd - f);
if (nElts < eltsToEnd) {
length -= nElts;
f += nElts;
end = f;
return nElts;
} else {
length -= eltsToEnd;
end = bufferEnd;
f = buffer;
return eltsToEnd;
}
}
// Enlarge the buffer so that it can hold at least newLength elements.
// May throw an exception, in which case the queue is left unchanged.
template <typename T>
void RawArrayQueue<T>::enlarge(size_t newLength) {
size_t newBufferSize = bufferSize * 2;
if (newBufferSize < newLength)
newBufferSize = newLength;
auto_ptr<T> newBuffer(new T[newBufferSize]);
T *oldBuffer = buffer;
size_t eltsToEnd = toSize_t(bufferEnd - f);
if (eltsToEnd >= length)
std::copy(f, f + length, newBuffer.get());
else {
std::copy(f, bufferEnd, newBuffer.get());
std::copy(oldBuffer, b, newBuffer.get() + eltsToEnd);
}
buffer = newBuffer.release();
f = buffer;
b = buffer + length;
if (oldBuffer != cache)
delete[] oldBuffer;
bufferSize = newBufferSize;
bufferEnd = buffer + newBufferSize;
}
// Ensure that there is room to hold one more element at the back of the queue,
// without expanding the queue's logical length.
// May throw an exception, in which case the queue is left unchanged.
template <typename T>
inline void RawArrayQueue<T>::reserve_back() {
if (length == bufferSize)
enlarge(length + 1);
#ifdef DEBUG
if (maxReservedSize <= length)
maxReservedSize = length + 1;
#endif
}
// Ensure that there is room to hold nElts more elements at the back of the
// queue, without expanding the queue's logical length.
// May throw an exception, in which case the queue is left unchanged.
template <typename T>
inline void RawArrayQueue<T>::reserve_back(size_t nElts) {
nElts += length;
if (bufferSize < nElts)
enlarge(nElts);
#ifdef DEBUG
if (maxReservedSize < nElts)
maxReservedSize = nElts;
#endif
}
// Advance the back of the queue by one element, assuming that the memory has
// previously been reserved.
// Return a pointer to that new element.
// Does not throw exceptions.
template <typename T>
inline T *RawArrayQueue<T>::advance_back() {
ASSERT(length < maxReservedSize);
++length;
if (b == bufferEnd)
b = buffer;
return b++;
}
// Advance the back of the queue by between one and nElts elements and return a
// pointer to them, assuming that the memory has previously been reserved.
// nEltsAdvanced gets the actual number of elements advanced.
// Does not throw exceptions.
template <typename T>
T *RawArrayQueue<T>::advance_back(size_t nElts, size_t &nEltsAdvanced) {
size_t newLength = length + nElts;
ASSERT(newLength <= maxReservedSize);
if (nElts) {
T *b2 = b;
if (b2 == bufferEnd)
b2 = buffer;
size_t room = toSize_t(bufferEnd - b2);
if (nElts > room) {
nElts = room;
newLength = length + nElts;
}
length = newLength;
nEltsAdvanced = nElts;
b = b2 + nElts;
return b2;
} else {
nEltsAdvanced = 0;
return 0;
}
}
// Same as push_back but assumes that the memory has previously been reserved.
// May throw an exception if copying elt throws one, in which case the queue is
// left unchanged.
template <typename T>
inline void RawArrayQueue<T>::fast_push_back(const T &elt) {
ASSERT(length < maxReservedSize);
T *b2 = b;
if (b2 == bufferEnd)
b2 = buffer;
*b2 = elt;
b = b2 + 1;
++length;
}
// Append elt to the back of the queue.
// May throw an exception, in which case the queue is left unchanged.
template <typename T>
inline void RawArrayQueue<T>::push_back(const T &elt) {
reserve_back();
T *b2 = b == bufferEnd ? buffer : b;
*b2 = elt;
b = b2 + 1;
++length;
}
// An ArrayQueue represents an array of elements of type T that can be written
// at its back end and read at its front or back end. In addition, arrays of
// multiple elements may be written at the back end or read at the front end.
// The ArrayQueue contains storage for a fixed size array of cacheSize elements;
// if this size is exceeded, the ArrayQueue allocates the array from the heap.
template <typename T, size_t cacheSize>
class ArrayQueue: public RawArrayQueue<T> {
T cacheArray[cacheSize];
public:
ArrayQueue(): RawArrayQueue<T>(cacheArray, cacheSize) {}
};
//
// Array auto_ptr's
//
// An ArrayAutoPtr holds a pointer to an array initialized by new T[x].
// A regular auto_ptr cannot be used here because it deletes its pointer using
// delete rather than delete[].
// An appropriate operator[] is also provided.
template <typename T> class ArrayAutoPtr {
T *ptr;
public:
explicit ArrayAutoPtr(T *p = 0): ptr(p) {}
ArrayAutoPtr(ArrayAutoPtr &a): ptr(a.ptr) {a.ptr = 0;}
ArrayAutoPtr &operator=(ArrayAutoPtr &a) {reset(a.release());}
~ArrayAutoPtr() {delete[] ptr;}
T &operator*() const {return *ptr;}
T &operator->() const {return *ptr;}
template<class N> T &operator[](N i) const {return ptr[i];}
T *get() const {return ptr;}
T *release() {T *p = ptr; ptr = 0; return p;}
void reset(T *p = 0) {delete[] ptr; ptr = p;}
};
typedef ArrayAutoPtr<char> CharAutoPtr;
}
#endif /* ds_h___ */