Bug 610587 - improve jsvector.h. r=lw.

This commit is contained in:
Nicholas Nethercote 2010-11-15 17:15:01 -08:00
parent 892852456c
commit f9af2830e6

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Luke Wagner <lw@mozilla.com>
* Nicholas Nethercote <nnethercote@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -101,13 +102,13 @@ struct VectorImpl
T *newbuf = reinterpret_cast<T *>(v.malloc(newcap * sizeof(T)));
if (!newbuf)
return false;
for (T *dst = newbuf, *src = v.heapBegin(); src != v.heapEnd(); ++dst, ++src)
for (T *dst = newbuf, *src = v.beginNoCheck(); src != v.endNoCheck(); ++dst, ++src)
new(dst) T(*src);
VectorImpl::destroy(v.heapBegin(), v.heapEnd());
v.free(v.heapBegin());
v.heapEnd() = newbuf + v.heapLength();
v.heapBegin() = newbuf;
v.heapCapacity() = newcap;
VectorImpl::destroy(v.beginNoCheck(), v.endNoCheck());
v.free(v.mBegin);
v.mBegin = newbuf;
/* v.mLength is unchanged. */
v.mCapacity = newcap;
return true;
}
};
@ -156,12 +157,12 @@ struct VectorImpl<T, N, AP, true>
static inline bool growTo(Vector<T,N,AP> &v, size_t newcap) {
JS_ASSERT(!v.usingInlineStorage());
size_t bytes = sizeof(T) * newcap;
T *newbuf = reinterpret_cast<T *>(v.realloc(v.heapBegin(), bytes));
T *newbuf = reinterpret_cast<T *>(v.realloc(v.mBegin, bytes));
if (!newbuf)
return false;
v.heapEnd() = newbuf + v.heapLength();
v.heapBegin() = newbuf;
v.heapCapacity() = newcap;
v.mBegin = newbuf;
/* v.mLength is unchanged. */
v.mCapacity = newcap;
return true;
}
};
@ -194,6 +195,7 @@ class Vector : AllocPolicy
friend struct VectorImpl<T, N, AllocPolicy, sElemIsPod>;
bool calculateNewCapacity(size_t curLength, size_t lengthInc, size_t &newCap);
bool growStorageBy(size_t lengthInc);
bool growHeapStorageBy(size_t lengthInc);
bool convertToHeapStorage(size_t lengthInc);
@ -205,26 +207,8 @@ class Vector : AllocPolicy
/* compute constants */
/*
* Pointers to the heap-allocated buffer. Only [heapBegin(), heapEnd())
* hold valid constructed T objects. The range [heapEnd(), heapBegin() +
* heapCapacity()) holds uninitialized memory.
*/
struct BufferPtrs {
T *mBegin, *mEnd;
};
/*
* Since a vector either stores elements inline or in a heap-allocated
* buffer, reuse the storage. mLengthOrCapacity serves as the union
* discriminator. In inline mode (when elements are stored in u.storage),
* mLengthOrCapacity holds the vector's length. In heap mode (when elements
* are stored in [u.ptrs.mBegin, u.ptrs.mEnd)), mLengthOrCapacity holds the
* vector's capacity.
*/
static const size_t sInlineCapacity =
tl::Clamp<N, sizeof(BufferPtrs) / sizeof(T),
sMaxInlineBytes / sizeof(T)>::result;
tl::Min<N, sMaxInlineBytes / sizeof(T)>::result;
/* Calculate inline buffer size; avoid 0-sized array. */
static const size_t sInlineBytes =
@ -232,73 +216,16 @@ class Vector : AllocPolicy
/* member data */
size_t mLengthOrCapacity;
bool usingInlineStorage() const { return mLengthOrCapacity <= sInlineCapacity; }
/*
* Pointer to the buffer, be it inline or heap-allocated. Only [mBegin,
* mBegin + mLength) hold valid constructed T objects. The range [mBegin +
* mLength, mBegin + mCapacity) holds uninitialized memory.
*/
T *mBegin;
size_t mLength; /* Number of elements in the Vector. */
size_t mCapacity; /* Max number of elements storable in the Vector without resizing. */
union {
BufferPtrs ptrs;
AlignedStorage<sInlineBytes> storage;
} u;
/* Only valid when usingInlineStorage() */
size_t &inlineLength() {
JS_ASSERT(usingInlineStorage());
return mLengthOrCapacity;
}
size_t inlineLength() const {
JS_ASSERT(usingInlineStorage());
return mLengthOrCapacity;
}
T *inlineBegin() const {
JS_ASSERT(usingInlineStorage());
return (T *)u.storage.addr();
}
T *inlineEnd() const {
JS_ASSERT(usingInlineStorage());
return (T *)u.storage.addr() + mLengthOrCapacity;
}
/* Only valid when !usingInlineStorage() */
size_t heapLength() const {
JS_ASSERT(!usingInlineStorage());
/* Guaranteed by calculateNewCapacity. */
JS_ASSERT(size_t(u.ptrs.mEnd - u.ptrs.mBegin) ==
((size_t(u.ptrs.mEnd) - size_t(u.ptrs.mBegin)) / sizeof(T)));
return u.ptrs.mEnd - u.ptrs.mBegin;
}
size_t &heapCapacity() {
JS_ASSERT(!usingInlineStorage());
return mLengthOrCapacity;
}
T *&heapBegin() {
JS_ASSERT(!usingInlineStorage());
return u.ptrs.mBegin;
}
T *&heapEnd() {
JS_ASSERT(!usingInlineStorage());
return u.ptrs.mEnd;
}
size_t heapCapacity() const {
JS_ASSERT(!usingInlineStorage());
return mLengthOrCapacity;
}
T *heapBegin() const {
JS_ASSERT(!usingInlineStorage());
return u.ptrs.mBegin;
}
T *heapEnd() const {
JS_ASSERT(!usingInlineStorage());
return u.ptrs.mEnd;
}
AlignedStorage<sInlineBytes> storage;
#ifdef DEBUG
friend class ReentrancyGuard;
@ -308,6 +235,24 @@ class Vector : AllocPolicy
Vector(const Vector &);
Vector &operator=(const Vector &);
/* private accessors */
bool usingInlineStorage() const {
return mBegin == (T *)storage.addr();
}
T *beginNoCheck() const {
return mBegin;
}
T *endNoCheck() {
return mBegin + mLength;
}
const T *endNoCheck() const {
return mBegin + mLength;
}
public:
Vector(AllocPolicy = AllocPolicy());
~Vector();
@ -317,44 +262,39 @@ class Vector : AllocPolicy
enum { InlineLength = N };
size_t length() const {
return usingInlineStorage() ? inlineLength() : heapLength();
return mLength;
}
bool empty() const {
return usingInlineStorage() ? inlineLength() == 0 : heapBegin() == heapEnd();
return mLength == 0;
}
size_t capacity() const {
return usingInlineStorage() ? sInlineCapacity : heapCapacity();
return mCapacity;
}
T *begin() {
T *begin() const {
JS_ASSERT(!entered);
return usingInlineStorage() ? inlineBegin() : heapBegin();
}
const T *begin() const {
JS_ASSERT(!entered);
return usingInlineStorage() ? inlineBegin() : heapBegin();
return mBegin;
}
T *end() {
JS_ASSERT(!entered);
return usingInlineStorage() ? inlineEnd() : heapEnd();
return mBegin + mLength;
}
const T *end() const {
JS_ASSERT(!entered);
return usingInlineStorage() ? inlineEnd() : heapEnd();
return mBegin + mLength;
}
T &operator[](size_t i) {
JS_ASSERT(!entered && i < length());
JS_ASSERT(!entered && i < mLength);
return begin()[i];
}
const T &operator[](size_t i) const {
JS_ASSERT(!entered && i < length());
JS_ASSERT(!entered && i < mLength);
return begin()[i];
}
@ -440,13 +380,19 @@ js_AppendLiteral(Vector<T,N,AP> &v, const char (&array)[ArrayLength])
return v.append(array, array + ArrayLength - 1);
}
/* This does the re-entrancy check plus several other sanity checks. */
#define REENTRANCY_GUARD_ET_AL \
ReentrancyGuard g(*this); \
JS_ASSERT_IF(usingInlineStorage(), mCapacity == sInlineCapacity); \
JS_ASSERT(mLength <= mCapacity)
/* Vector Implementation */
template <class T, size_t N, class AllocPolicy>
JS_ALWAYS_INLINE
Vector<T,N,AllocPolicy>::Vector(AllocPolicy ap)
: AllocPolicy(ap), mLengthOrCapacity(0)
: AllocPolicy(ap), mBegin((T *)storage.addr()), mLength(0),
mCapacity(sInlineCapacity)
#ifdef DEBUG
, entered(false)
#endif
@ -456,13 +402,10 @@ template <class T, size_t N, class AP>
JS_ALWAYS_INLINE
Vector<T,N,AP>::~Vector()
{
ReentrancyGuard g(*this);
if (usingInlineStorage()) {
Impl::destroy(inlineBegin(), inlineEnd());
} else {
Impl::destroy(heapBegin(), heapEnd());
this->free(heapBegin());
}
REENTRANCY_GUARD_ET_AL;
Impl::destroy(beginNoCheck(), endNoCheck());
if (!usingInlineStorage())
this->free(beginNoCheck());
}
/*
@ -503,19 +446,20 @@ Vector<T,N,AP>::calculateNewCapacity(size_t curLength, size_t lengthInc,
/*
* This function will grow the current heap capacity to have capacity
* (heapLength() + lengthInc) and fail on OOM or integer overflow.
* (mLength + lengthInc) and fail on OOM or integer overflow.
*/
template <class T, size_t N, class AP>
JS_ALWAYS_INLINE bool
Vector<T,N,AP>::growHeapStorageBy(size_t lengthInc)
{
JS_ASSERT(!usingInlineStorage());
size_t newCap;
return calculateNewCapacity(heapLength(), lengthInc, newCap) &&
return calculateNewCapacity(mLength, lengthInc, newCap) &&
Impl::growTo(*this, newCap);
}
/*
* This function will create a new heap buffer with capacity (inlineLength() +
* This function will create a new heap buffer with capacity (mLength +
* lengthInc()), move all elements in the inline buffer to this new buffer,
* and fail on OOM or integer overflow.
*/
@ -523,8 +467,9 @@ template <class T, size_t N, class AP>
inline bool
Vector<T,N,AP>::convertToHeapStorage(size_t lengthInc)
{
JS_ASSERT(usingInlineStorage());
size_t newCap;
if (!calculateNewCapacity(inlineLength(), lengthInc, newCap))
if (!calculateNewCapacity(mLength, lengthInc, newCap))
return false;
/* Allocate buffer. */
@ -533,29 +478,33 @@ Vector<T,N,AP>::convertToHeapStorage(size_t lengthInc)
return false;
/* Copy inline elements into heap buffer. */
size_t length = inlineLength();
Impl::copyConstruct(newBuf, inlineBegin(), inlineEnd());
Impl::destroy(inlineBegin(), inlineEnd());
Impl::copyConstruct(newBuf, beginNoCheck(), endNoCheck());
Impl::destroy(beginNoCheck(), endNoCheck());
/* Switch in heap buffer. */
mLengthOrCapacity = newCap; /* marks us as !usingInlineStorage() */
heapBegin() = newBuf;
heapEnd() = newBuf + length;
mBegin = newBuf;
/* mLength is unchanged. */
mCapacity = newCap;
return true;
}
template <class T, size_t N, class AP>
JS_ALWAYS_INLINE bool
Vector<T,N,AP>::growStorageBy(size_t incr)
{
JS_ASSERT(mLength + incr > mCapacity);
return usingInlineStorage()
? convertToHeapStorage(incr)
: growHeapStorageBy(incr);
}
template <class T, size_t N, class AP>
inline bool
Vector<T,N,AP>::reserve(size_t request)
{
ReentrancyGuard g(*this);
if (usingInlineStorage()) {
if (request > sInlineCapacity)
return convertToHeapStorage(request - inlineLength());
} else {
if (request > heapCapacity())
return growHeapStorageBy(request - heapLength());
}
REENTRANCY_GUARD_ET_AL;
if (request > mCapacity)
return growStorageBy(request - mLength);
return true;
}
@ -563,15 +512,10 @@ template <class T, size_t N, class AP>
inline void
Vector<T,N,AP>::shrinkBy(size_t incr)
{
ReentrancyGuard g(*this);
JS_ASSERT(incr <= length());
if (usingInlineStorage()) {
Impl::destroy(inlineEnd() - incr, inlineEnd());
inlineLength() -= incr;
} else {
Impl::destroy(heapEnd() - incr, heapEnd());
heapEnd() -= incr;
}
REENTRANCY_GUARD_ET_AL;
JS_ASSERT(incr <= mLength);
Impl::destroy(endNoCheck() - incr, endNoCheck());
mLength -= incr;
}
template <class T, size_t N, class AP>
@ -579,35 +523,15 @@ template <bool InitNewElems>
JS_ALWAYS_INLINE bool
Vector<T,N,AP>::growByImpl(size_t incr)
{
ReentrancyGuard g(*this);
if (usingInlineStorage()) {
size_t freespace = sInlineCapacity - inlineLength();
if (incr <= freespace) {
T *newend = inlineEnd() + incr;
if (InitNewElems)
Impl::initialize(inlineEnd(), newend);
inlineLength() += incr;
JS_ASSERT(usingInlineStorage());
return true;
}
if (!convertToHeapStorage(incr))
return false;
}
else {
/* grow if needed */
size_t freespace = heapCapacity() - heapLength();
if (incr > freespace) {
if (!growHeapStorageBy(incr))
return false;
}
}
REENTRANCY_GUARD_ET_AL;
if (incr > mCapacity - mLength && !growStorageBy(incr))
return false;
/* We are !usingInlineStorage(). Initialize new elements. */
JS_ASSERT(heapCapacity() - heapLength() >= incr);
T *newend = heapEnd() + incr;
JS_ASSERT(mLength + incr <= mCapacity);
T *newend = endNoCheck() + incr;
if (InitNewElems)
Impl::initialize(heapEnd(), newend);
heapEnd() = newend;
Impl::initialize(endNoCheck(), newend);
mLength += incr;
return true;
}
@ -630,7 +554,7 @@ STATIC_POSTCONDITION(!return || ubound(this->begin()) >= newLength)
inline bool
Vector<T,N,AP>::resize(size_t newLength)
{
size_t curLength = length();
size_t curLength = mLength;
if (newLength > curLength)
return growBy(newLength - curLength);
shrinkBy(curLength - newLength);
@ -641,7 +565,7 @@ template <class T, size_t N, class AP>
JS_ALWAYS_INLINE bool
Vector<T,N,AP>::resizeUninitialized(size_t newLength)
{
size_t curLength = length();
size_t curLength = mLength;
if (newLength > curLength)
return growByUninitialized(newLength - curLength);
shrinkBy(curLength - newLength);
@ -652,39 +576,22 @@ template <class T, size_t N, class AP>
inline void
Vector<T,N,AP>::clear()
{
ReentrancyGuard g(*this);
if (usingInlineStorage()) {
Impl::destroy(inlineBegin(), inlineEnd());
inlineLength() = 0;
}
else {
Impl::destroy(heapBegin(), heapEnd());
heapEnd() = heapBegin();
}
REENTRANCY_GUARD_ET_AL;
Impl::destroy(beginNoCheck(), endNoCheck());
mLength = 0;
}
template <class T, size_t N, class AP>
JS_ALWAYS_INLINE bool
Vector<T,N,AP>::append(const T &t)
{
ReentrancyGuard g(*this);
if (usingInlineStorage()) {
if (inlineLength() < sInlineCapacity) {
new(inlineEnd()) T(t);
++inlineLength();
JS_ASSERT(usingInlineStorage());
return true;
}
if (!convertToHeapStorage(1))
return false;
} else {
if (heapLength() == heapCapacity() && !growHeapStorageBy(1))
return false;
}
REENTRANCY_GUARD_ET_AL;
if (mLength == mCapacity && !growStorageBy(1))
return false;
/* We are !usingInlineStorage(). Initialize new elements. */
JS_ASSERT(heapLength() <= heapCapacity() && heapCapacity() - heapLength() >= 1);
new(heapEnd()++) T(t);
JS_ASSERT(mLength < mCapacity);
new(endNoCheck()) T(t);
++mLength;
return true;
}
@ -692,27 +599,13 @@ template <class T, size_t N, class AP>
JS_ALWAYS_INLINE bool
Vector<T,N,AP>::appendN(const T &t, size_t needed)
{
ReentrancyGuard g(*this);
if (usingInlineStorage()) {
size_t freespace = sInlineCapacity - inlineLength();
if (needed <= freespace) {
Impl::copyConstructN(inlineEnd(), needed, t);
inlineLength() += needed;
JS_ASSERT(usingInlineStorage());
return true;
}
if (!convertToHeapStorage(needed))
return false;
} else {
size_t freespace = heapCapacity() - heapLength();
if (needed > freespace && !growHeapStorageBy(needed))
return false;
}
REENTRANCY_GUARD_ET_AL;
if (mLength + needed > mCapacity && !growStorageBy(needed))
return false;
/* We are !usingInlineStorage(). Initialize new elements. */
JS_ASSERT(heapLength() <= heapCapacity() && heapCapacity() - heapLength() >= needed);
Impl::copyConstructN(heapEnd(), needed, t);
heapEnd() += needed;
JS_ASSERT(mLength + needed <= mCapacity);
Impl::copyConstructN(endNoCheck(), needed, t);
mLength += needed;
return true;
}
@ -722,8 +615,8 @@ Vector<T,N,AP>::insert(T *p, const T &val)
{
JS_ASSERT(begin() <= p && p <= end());
size_t pos = p - begin();
JS_ASSERT(pos <= length());
size_t oldLength = length();
JS_ASSERT(pos <= mLength);
size_t oldLength = mLength;
if (pos == oldLength)
return append(val);
{
@ -754,28 +647,14 @@ template <class U>
JS_ALWAYS_INLINE bool
Vector<T,N,AP>::append(const U *insBegin, const U *insEnd)
{
ReentrancyGuard g(*this);
REENTRANCY_GUARD_ET_AL;
size_t needed = PointerRangeSize(insBegin, insEnd);
if (usingInlineStorage()) {
size_t freespace = sInlineCapacity - inlineLength();
if (needed <= freespace) {
Impl::copyConstruct(inlineEnd(), insBegin, insEnd);
inlineLength() += needed;
JS_ASSERT(usingInlineStorage());
return true;
}
if (!convertToHeapStorage(needed))
return false;
} else {
size_t freespace = heapCapacity() - heapLength();
if (needed > freespace && !growHeapStorageBy(needed))
return false;
}
if (mLength + needed > mCapacity && !growStorageBy(needed))
return false;
/* We are !usingInlineStorage(). Initialize new elements. */
JS_ASSERT(heapLength() <= heapCapacity() && heapCapacity() - heapLength() >= needed);
Impl::copyConstruct(heapEnd(), insBegin, insEnd);
heapEnd() += needed;
JS_ASSERT(mLength + needed <= mCapacity);
Impl::copyConstruct(endNoCheck(), insBegin, insEnd);
mLength += needed;
return true;
}
@ -799,33 +678,31 @@ template <class T, size_t N, class AP>
JS_ALWAYS_INLINE void
Vector<T,N,AP>::popBack()
{
ReentrancyGuard g(*this);
REENTRANCY_GUARD_ET_AL;
JS_ASSERT(!empty());
if (usingInlineStorage()) {
--inlineLength();
inlineEnd()->~T();
} else {
--heapEnd();
heapEnd()->~T();
}
--mLength;
endNoCheck()->~T();
}
template <class T, size_t N, class AP>
inline T *
Vector<T,N,AP>::extractRawBuffer()
{
T *ret;
if (usingInlineStorage()) {
T *ret = reinterpret_cast<T *>(this->malloc(inlineLength() * sizeof(T)));
ret = reinterpret_cast<T *>(this->malloc(mLength * sizeof(T)));
if (!ret)
return NULL;
Impl::copyConstruct(ret, inlineBegin(), inlineEnd());
Impl::destroy(inlineBegin(), inlineEnd());
inlineLength() = 0;
return ret;
Impl::copyConstruct(ret, beginNoCheck(), endNoCheck());
Impl::destroy(beginNoCheck(), endNoCheck());
/* mBegin, mCapacity are unchanged. */
mLength = 0;
} else {
ret = mBegin;
mBegin = (T *)storage.addr();
mLength = 0;
mCapacity = sInlineCapacity;
}
T *ret = heapBegin();
mLengthOrCapacity = 0; /* marks us as !usingInlineStorage() */
return ret;
}
@ -833,31 +710,30 @@ template <class T, size_t N, class AP>
inline void
Vector<T,N,AP>::replaceRawBuffer(T *p, size_t length)
{
ReentrancyGuard g(*this);
REENTRANCY_GUARD_ET_AL;
/* Destroy what we have. */
if (usingInlineStorage()) {
Impl::destroy(inlineBegin(), inlineEnd());
inlineLength() = 0;
} else {
Impl::destroy(heapBegin(), heapEnd());
this->free(heapBegin());
}
Impl::destroy(beginNoCheck(), endNoCheck());
if (!usingInlineStorage())
this->free(beginNoCheck());
/* Take in the new buffer. */
if (length <= sInlineCapacity) {
/*
* (mLengthOrCapacity <= sInlineCapacity) means inline storage, so we
* MUST use inline storage, even though p might otherwise be acceptable.
* We convert to inline storage if possible, even though p might
* otherwise be acceptable. Maybe this behaviour should be
* specifiable with an argument to this function.
*/
mLengthOrCapacity = length; /* marks us as usingInlineStorage() */
Impl::copyConstruct(inlineBegin(), p, p + length);
mBegin = (T *)storage.addr();
mLength = length;
mCapacity = sInlineCapacity;
Impl::copyConstruct(mBegin, p, p + length);
Impl::destroy(p, p + length);
this->free(p);
} else {
mLengthOrCapacity = length; /* marks us as !usingInlineStorage() */
heapBegin() = p;
heapEnd() = heapBegin() + length;
mBegin = p;
mLength = length;
mCapacity = length;
}
}