| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef js_Vector_h |
| #define js_Vector_h |
| |
| #include "mozilla/Attributes.h" |
| #include "mozilla/TypeTraits.h" |
| |
| #include "TemplateLib.h" |
| #include "Utility.h" |
| |
| /* Silence dire "bugs in previous versions of MSVC have been fixed" warnings */ |
| #ifdef _MSC_VER |
| #pragma warning(push) |
| #pragma warning(disable:4345) |
| #endif |
| |
| namespace js { |
| |
| class TempAllocPolicy; |
| |
| template <class T, |
| size_t MinInlineCapacity = 0, |
| class AllocPolicy = TempAllocPolicy> |
| class Vector; |
| |
| /* |
| * Check that the given capacity wastes the minimal amount of space if |
| * allocated on the heap. This means that cap*sizeof(T) is as close to a |
| * power-of-two as possible. growStorageBy() is responsible for ensuring |
| * this. |
| */ |
| template <typename T> |
| static bool CapacityHasExcessSpace(size_t cap) |
| { |
| size_t size = cap * sizeof(T); |
| return RoundUpPow2(size) - size >= sizeof(T); |
| } |
| |
| /* |
| * This template class provides a default implementation for vector operations |
| * when the element type is not known to be a POD, as judged by IsPod. |
| */ |
| template <class T, size_t N, class AP, bool IsPod> |
| struct VectorImpl |
| { |
| /* Destroys constructed objects in the range [begin, end). */ |
| static inline void destroy(T *begin, T *end) { |
| for (T *p = begin; p != end; ++p) |
| p->~T(); |
| } |
| |
| /* Constructs objects in the uninitialized range [begin, end). */ |
| static inline void initialize(T *begin, T *end) { |
| for (T *p = begin; p != end; ++p) |
| new(p) T(); |
| } |
| |
| /* |
| * Copy-constructs objects in the uninitialized range |
| * [dst, dst+(srcend-srcbeg)) from the range [srcbeg, srcend). |
| */ |
| template <class U> |
| static inline void copyConstruct(T *dst, const U *srcbeg, const U *srcend) { |
| for (const U *p = srcbeg; p != srcend; ++p, ++dst) |
| new(dst) T(*p); |
| } |
| |
| /* |
| * Move-constructs objects in the uninitialized range |
| * [dst, dst+(srcend-srcbeg)) from the range [srcbeg, srcend). |
| */ |
| template <class U> |
| static inline void moveConstruct(T *dst, const U *srcbeg, const U *srcend) { |
| for (const U *p = srcbeg; p != srcend; ++p, ++dst) |
| new(dst) T(Move(*p)); |
| } |
| |
| /* |
| * Copy-constructs objects in the uninitialized range [dst, dst+n) from the |
| * same object u. |
| */ |
| template <class U> |
| static inline void copyConstructN(T *dst, size_t n, const U &u) { |
| for (T *end = dst + n; dst != end; ++dst) |
| new(dst) T(u); |
| } |
| |
| /* |
| * Grows the given buffer to have capacity newCap, preserving the objects |
| * constructed in the range [begin, end) and updating v. Assumes that (1) |
| * newCap has not overflowed, and (2) multiplying newCap by sizeof(T) will |
| * not overflow. |
| */ |
| static inline bool growTo(Vector<T,N,AP> &v, size_t newCap) { |
| JS_ASSERT(!v.usingInlineStorage()); |
| JS_ASSERT(!CapacityHasExcessSpace<T>(newCap)); |
| T *newbuf = reinterpret_cast<T *>(v.malloc_(newCap * sizeof(T))); |
| if (!newbuf) |
| return false; |
| for (T *dst = newbuf, *src = v.beginNoCheck(); src != v.endNoCheck(); ++dst, ++src) |
| new(dst) T(Move(*src)); |
| VectorImpl::destroy(v.beginNoCheck(), v.endNoCheck()); |
| v.free_(v.mBegin); |
| v.mBegin = newbuf; |
| /* v.mLength is unchanged. */ |
| v.mCapacity = newCap; |
| return true; |
| } |
| }; |
| |
| /* |
| * This partial template specialization provides a default implementation for |
| * vector operations when the element type is known to be a POD, as judged by |
| * IsPod. |
| */ |
| template <class T, size_t N, class AP> |
| struct VectorImpl<T, N, AP, true> |
| { |
| static inline void destroy(T *, T *) {} |
| |
| static inline void initialize(T *begin, T *end) { |
| /* |
| * You would think that memset would be a big win (or even break even) |
| * when we know T is a POD. But currently it's not. This is probably |
| * because |append| tends to be given small ranges and memset requires |
| * a function call that doesn't get inlined. |
| * |
| * memset(begin, 0, sizeof(T) * (end-begin)); |
| */ |
| for (T *p = begin; p != end; ++p) |
| new(p) T(); |
| } |
| |
| template <class U> |
| static inline void copyConstruct(T *dst, const U *srcbeg, const U *srcend) { |
| /* |
| * See above memset comment. Also, notice that copyConstruct is |
| * currently templated (T != U), so memcpy won't work without |
| * requiring T == U. |
| * |
| * memcpy(dst, srcbeg, sizeof(T) * (srcend - srcbeg)); |
| */ |
| for (const U *p = srcbeg; p != srcend; ++p, ++dst) |
| *dst = *p; |
| } |
| |
| template <class U> |
| static inline void moveConstruct(T *dst, const U *srcbeg, const U *srcend) { |
| copyConstruct(dst, srcbeg, srcend); |
| } |
| |
| static inline void copyConstructN(T *dst, size_t n, const T &t) { |
| for (T *p = dst, *end = dst + n; p != end; ++p) |
| *p = t; |
| } |
| |
| static inline bool growTo(Vector<T,N,AP> &v, size_t newCap) { |
| JS_ASSERT(!v.usingInlineStorage()); |
| JS_ASSERT(!CapacityHasExcessSpace<T>(newCap)); |
| size_t oldSize = sizeof(T) * v.mCapacity; |
| size_t newSize = sizeof(T) * newCap; |
| T *newbuf = reinterpret_cast<T *>(v.realloc_(v.mBegin, oldSize, newSize)); |
| if (!newbuf) |
| return false; |
| v.mBegin = newbuf; |
| /* v.mLength is unchanged. */ |
| v.mCapacity = newCap; |
| return true; |
| } |
| }; |
| |
| /* |
| * JS-friendly, STL-like container providing a short-lived, dynamic buffer. |
| * Vector calls the constructors/destructors of all elements stored in |
| * its internal buffer, so non-PODs may be safely used. Additionally, |
| * Vector will store the first N elements in-place before resorting to |
| * dynamic allocation. |
| * |
| * T requirements: |
| * - default and copy constructible, assignable, destructible |
| * - operations do not throw |
| * N requirements: |
| * - any value, however, N is clamped to min/max values |
| * AllocPolicy: |
| * - see "Allocation policies" in jsalloc.h (default js::TempAllocPolicy) |
| * |
| * N.B: Vector is not reentrant: T member functions called during Vector member |
| * functions must not call back into the same object. |
| */ |
| template <class T, size_t N, class AllocPolicy> |
| class Vector : private AllocPolicy |
| { |
| // typedef typename tl::StaticAssert<!tl::IsPostBarrieredType<T>::result>::result _; |
| |
| /* utilities */ |
| |
| static const bool sElemIsPod = mozilla::IsPod<T>::value; |
| typedef VectorImpl<T, N, AllocPolicy, sElemIsPod> Impl; |
| friend struct VectorImpl<T, N, AllocPolicy, sElemIsPod>; |
| |
| bool growStorageBy(size_t incr); |
| bool convertToHeapStorage(size_t newCap); |
| |
| template <bool InitNewElems> inline bool growByImpl(size_t inc); |
| |
| /* magic constants */ |
| |
| static const int sMaxInlineBytes = 1024; |
| |
| /* compute constants */ |
| |
| /* |
| * Consider element size to be 1 for buffer sizing if there are |
| * 0 inline elements. This allows us to compile when the definition |
| * of the element type is not visible here. |
| * |
| * Explicit specialization is only allowed at namespace scope, so |
| * in order to keep everything here, we use a dummy template |
| * parameter with partial specialization. |
| */ |
| template <int M, int Dummy> |
| struct ElemSize { |
| static const size_t result = sizeof(T); |
| }; |
| template <int Dummy> |
| struct ElemSize<0, Dummy> { |
| static const size_t result = 1; |
| }; |
| |
| static const size_t sInlineCapacity = |
| tl::Min<N, sMaxInlineBytes / ElemSize<N, 0>::result>::result; |
| |
| /* Calculate inline buffer size; avoid 0-sized array. */ |
| static const size_t sInlineBytes = |
| tl::Max<1, sInlineCapacity * ElemSize<N, 0>::result>::result; |
| |
| /* member data */ |
| |
| /* |
| * 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. The range |
| * [mBegin + mLength, mBegin + mReserved) also holds uninitialized memory |
| * previously allocated by a call to reserve(). |
| */ |
| T *mBegin; |
| size_t mLength; /* Number of elements in the Vector. */ |
| size_t mCapacity; /* Max number of elements storable in the Vector without resizing. */ |
| #ifdef JS_DEBUG |
| size_t mReserved; /* Max elements of reserved or used space in this vector. */ |
| #endif |
| |
| mozilla::AlignedStorage<sInlineBytes> storage; |
| |
| #ifdef JS_DEBUG |
| friend class ReentrancyGuard; |
| bool entered; |
| #endif |
| |
| Vector(const Vector &) MOZ_DELETE; |
| Vector &operator=(const Vector &) MOZ_DELETE; |
| |
| /* private accessors */ |
| |
| bool usingInlineStorage() const { |
| return mBegin == inlineStorage(); |
| } |
| |
| T *inlineStorage() const { |
| return (T *)storage.addr(); |
| } |
| |
| T *beginNoCheck() const { |
| return mBegin; |
| } |
| |
| T *endNoCheck() { |
| return mBegin + mLength; |
| } |
| |
| const T *endNoCheck() const { |
| return mBegin + mLength; |
| } |
| |
| #ifdef JS_DEBUG |
| size_t reserved() const { |
| JS_ASSERT(mReserved <= mCapacity); |
| JS_ASSERT(mLength <= mReserved); |
| return mReserved; |
| } |
| #endif |
| |
| /* Append operations guaranteed to succeed due to pre-reserved space. */ |
| template <class U> void internalAppend(U u); |
| void internalAppendN(const T &t, size_t n); |
| template <class U> void internalAppend(const U *begin, size_t length); |
| template <class U, size_t O, class BP> void internalAppend(const Vector<U,O,BP> &other); |
| |
| public: |
| static const size_t sMaxInlineStorage = N; |
| |
| typedef T ElementType; |
| |
| Vector(AllocPolicy = AllocPolicy()); |
| Vector(MoveRef<Vector>); /* Move constructor. */ |
| Vector &operator=(MoveRef<Vector>); /* Move assignment. */ |
| ~Vector(); |
| |
| /* accessors */ |
| |
| const AllocPolicy &allocPolicy() const { |
| return *this; |
| } |
| |
| AllocPolicy &allocPolicy() { |
| return *this; |
| } |
| |
| enum { InlineLength = N }; |
| |
| size_t length() const { |
| return mLength; |
| } |
| |
| bool empty() const { |
| return mLength == 0; |
| } |
| |
| size_t capacity() const { |
| return mCapacity; |
| } |
| |
| T *begin() { |
| JS_ASSERT(!entered); |
| return mBegin; |
| } |
| |
| const T *begin() const { |
| JS_ASSERT(!entered); |
| return mBegin; |
| } |
| |
| T *end() { |
| JS_ASSERT(!entered); |
| return mBegin + mLength; |
| } |
| |
| const T *end() const { |
| JS_ASSERT(!entered); |
| return mBegin + mLength; |
| } |
| |
| T &operator[](size_t i) { |
| JS_ASSERT(!entered && i < mLength); |
| return begin()[i]; |
| } |
| |
| const T &operator[](size_t i) const { |
| JS_ASSERT(!entered && i < mLength); |
| return begin()[i]; |
| } |
| |
| T &back() { |
| JS_ASSERT(!entered && !empty()); |
| return *(end() - 1); |
| } |
| |
| const T &back() const { |
| JS_ASSERT(!entered && !empty()); |
| return *(end() - 1); |
| } |
| |
| class Range { |
| friend class Vector; |
| T *cur_, *end_; |
| Range(T *cur, T *end) : cur_(cur), end_(end) {} |
| public: |
| Range() {} |
| bool empty() const { return cur_ == end_; } |
| size_t remain() const { return end_ - cur_; } |
| T &front() const { return *cur_; } |
| void popFront() { JS_ASSERT(!empty()); ++cur_; } |
| T popCopyFront() { JS_ASSERT(!empty()); return *cur_++; } |
| }; |
| |
| Range all() { |
| return Range(begin(), end()); |
| } |
| |
| /* mutators */ |
| |
| /* Given that the Vector is empty and has no inline storage, grow to |capacity|. */ |
| bool initCapacity(size_t request); |
| |
| /* If reserve(length() + N) succeeds, the N next appends are guaranteed to succeed. */ |
| bool reserve(size_t request); |
| |
| /* |
| * Destroy elements in the range [end() - incr, end()). Does not deallocate |
| * or unreserve storage for those elements. |
| */ |
| void shrinkBy(size_t incr); |
| |
| /* Grow the vector by incr elements. */ |
| bool growBy(size_t incr); |
| |
| /* Call shrinkBy or growBy based on whether newSize > length(). */ |
| bool resize(size_t newLength); |
| |
| /* Leave new elements as uninitialized memory. */ |
| bool growByUninitialized(size_t incr); |
| bool resizeUninitialized(size_t newLength); |
| |
| /* Shorthand for shrinkBy(length()). */ |
| void clear(); |
| |
| /* Clears and releases any heap-allocated storage. */ |
| void clearAndFree(); |
| |
| /* If true, appending |needed| elements will not call realloc(). */ |
| bool canAppendWithoutRealloc(size_t needed) const; |
| |
| /* |
| * Potentially fallible append operations. |
| * |
| * The function templates that take an unspecified type U require a |
| * const T & or a MoveRef<T>. The MoveRef<T> variants move their |
| * operands into the vector, instead of copying them. If they fail, the |
| * operand is left unmoved. |
| */ |
| template <class U> bool append(U t); |
| bool appendN(const T &t, size_t n); |
| template <class U> bool append(const U *begin, const U *end); |
| template <class U> bool append(const U *begin, size_t length); |
| template <class U, size_t O, class BP> bool append(const Vector<U,O,BP> &other); |
| |
| /* |
| * Guaranteed-infallible append operations for use upon vectors whose |
| * memory has been pre-reserved. |
| */ |
| template <class U> void infallibleAppend(const U &u) { |
| internalAppend(u); |
| } |
| void infallibleAppendN(const T &t, size_t n) { |
| internalAppendN(t, n); |
| } |
| template <class U> void infallibleAppend(const U *aBegin, const U *aEnd) { |
| internalAppend(aBegin, mozilla::PointerRangeSize(aBegin, aEnd)); |
| } |
| template <class U> void infallibleAppend(const U *aBegin, size_t aLength) { |
| internalAppend(aBegin, aLength); |
| } |
| template <class U, size_t O, class BP> void infallibleAppend(const Vector<U,O,BP> &other) { |
| internalAppend(other); |
| } |
| |
| void popBack(); |
| |
| T popCopy(); |
| |
| /* |
| * Transfers ownership of the internal buffer used by Vector to the caller. |
| * After this call, the Vector is empty. Since the returned buffer may need |
| * to be allocated (if the elements are currently stored in-place), the |
| * call can fail, returning NULL. |
| * |
| * N.B. Although a T*, only the range [0, length()) is constructed. |
| */ |
| T *extractRawBuffer(); |
| |
| /* |
| * Transfer ownership of an array of objects into the Vector. |
| * N.B. This call assumes that there are no uninitialized elements in the |
| * passed array. |
| */ |
| void replaceRawBuffer(T *p, size_t length); |
| |
| /* |
| * Places |val| at position |p|, shifting existing elements from |p| |
| * onward one position higher. On success, |p| should not be reused |
| * because it will be a dangling pointer if reallocation of the vector |
| * storage occurred; the return value should be used instead. On failure, |
| * NULL is returned. |
| * |
| * Example usage: |
| * |
| * if (!(p = vec.insert(p, val))) |
| * <handle failure> |
| * <keep working with p> |
| */ |
| T *insert(T *p, const T &val); |
| |
| /* |
| * Removes the element |t|, which must fall in the bounds [begin, end), |
| * shifting existing elements from |t + 1| onward one position lower. |
| */ |
| void erase(T *t); |
| |
| /* |
| * Measure the size of the Vector's heap-allocated storage. |
| */ |
| size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const; |
| |
| /* |
| * Like sizeOfExcludingThis, but also measures the size of the Vector |
| * object (which must be heap-allocated) itself. |
| */ |
| size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const; |
| |
| void swap(Vector &other); |
| }; |
| |
| /* 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(reserved() <= mCapacity); \ |
| JS_ASSERT(mLength <= reserved()); \ |
| 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), mBegin((T *)storage.addr()), mLength(0), |
| mCapacity(sInlineCapacity) |
| #ifdef JS_DEBUG |
| , mReserved(sInlineCapacity), entered(false) |
| #endif |
| {} |
| |
| /* Move constructor. */ |
| template <class T, size_t N, class AllocPolicy> |
| JS_ALWAYS_INLINE |
| Vector<T, N, AllocPolicy>::Vector(MoveRef<Vector> rhs) |
| : AllocPolicy(rhs) |
| #ifdef JS_DEBUG |
| , entered(false) |
| #endif |
| { |
| mLength = rhs->mLength; |
| mCapacity = rhs->mCapacity; |
| #ifdef JS_DEBUG |
| mReserved = rhs->mReserved; |
| #endif |
| |
| if (rhs->usingInlineStorage()) { |
| /* We can't move the buffer over in this case, so copy elements. */ |
| mBegin = (T *)storage.addr(); |
| Impl::moveConstruct(mBegin, rhs->beginNoCheck(), rhs->endNoCheck()); |
| /* |
| * Leave rhs's mLength, mBegin, mCapacity, and mReserved as they are. |
| * The elements in its in-line storage still need to be destroyed. |
| */ |
| } else { |
| /* |
| * Take src's buffer, and turn src into an empty vector using |
| * in-line storage. |
| */ |
| mBegin = rhs->mBegin; |
| rhs->mBegin = (T *) rhs->storage.addr(); |
| rhs->mCapacity = sInlineCapacity; |
| rhs->mLength = 0; |
| #ifdef JS_DEBUG |
| rhs->mReserved = sInlineCapacity; |
| #endif |
| } |
| } |
| |
| /* Move assignment. */ |
| template <class T, size_t N, class AP> |
| JS_ALWAYS_INLINE |
| Vector<T, N, AP> & |
| Vector<T, N, AP>::operator=(MoveRef<Vector> rhs) |
| { |
| this->~Vector(); |
| new(this) Vector(rhs); |
| return *this; |
| } |
| |
| template <class T, size_t N, class AP> |
| JS_ALWAYS_INLINE |
| Vector<T,N,AP>::~Vector() |
| { |
| REENTRANCY_GUARD_ET_AL; |
| Impl::destroy(beginNoCheck(), endNoCheck()); |
| if (!usingInlineStorage()) |
| this->free_(beginNoCheck()); |
| } |
| |
| /* |
| * This function will create a new heap buffer with capacity newCap, |
| * move all elements in the inline buffer to this new buffer, |
| * and fail on OOM. |
| */ |
| template <class T, size_t N, class AP> |
| inline bool |
| Vector<T,N,AP>::convertToHeapStorage(size_t newCap) |
| { |
| JS_ASSERT(usingInlineStorage()); |
| |
| /* Allocate buffer. */ |
| JS_ASSERT(!CapacityHasExcessSpace<T>(newCap)); |
| T *newBuf = reinterpret_cast<T *>(this->malloc_(newCap * sizeof(T))); |
| if (!newBuf) |
| return false; |
| |
| /* Copy inline elements into heap buffer. */ |
| Impl::moveConstruct(newBuf, beginNoCheck(), endNoCheck()); |
| Impl::destroy(beginNoCheck(), endNoCheck()); |
| |
| /* Switch in heap buffer. */ |
| mBegin = newBuf; |
| /* mLength is unchanged. */ |
| mCapacity = newCap; |
| return true; |
| } |
| |
| template <class T, size_t N, class AP> |
| JS_NEVER_INLINE bool |
| Vector<T,N,AP>::growStorageBy(size_t incr) |
| { |
| JS_ASSERT(mLength + incr > mCapacity); |
| JS_ASSERT_IF(!usingInlineStorage(), !CapacityHasExcessSpace<T>(mCapacity)); |
| |
| /* |
| * When choosing a new capacity, its size should is as close to 2^N bytes |
| * as possible. 2^N-sized requests are best because they are unlikely to |
| * be rounded up by the allocator. Asking for a 2^N number of elements |
| * isn't as good, because if sizeof(T) is not a power-of-two that would |
| * result in a non-2^N request size. |
| */ |
| |
| size_t newCap; |
| |
| if (incr == 1) { |
| if (usingInlineStorage()) { |
| /* This case occurs in ~70--80% of the calls to this function. */ |
| size_t newSize = tl::RoundUpPow2<(sInlineCapacity + 1) * sizeof(T)>::result; |
| newCap = newSize / sizeof(T); |
| goto convert; |
| } |
| |
| if (mLength == 0) { |
| /* This case occurs in ~0--10% of the calls to this function. */ |
| newCap = 1; |
| goto grow; |
| } |
| |
| /* This case occurs in ~15--20% of the calls to this function. */ |
| |
| /* |
| * Will mLength*4*sizeof(T) overflow? This condition limits a Vector |
| * to 1GB of memory on a 32-bit system, which is a reasonable limit. |
| * It also ensures that the ((char *)end() - (char *)begin()) does not |
| * overflow ptrdiff_t (see Bug 510319). |
| */ |
| if (mLength & tl::MulOverflowMask<4 * sizeof(T)>::result) { |
| this->reportAllocOverflow(); |
| return false; |
| } |
| |
| /* |
| * If we reach here, the existing capacity will have a size that is |
| * already as close to 2^N as sizeof(T) will allow. Just double the |
| * capacity, and then there might be space for one more element. |
| */ |
| newCap = mLength * 2; |
| if (CapacityHasExcessSpace<T>(newCap)) |
| newCap += 1; |
| |
| } else { |
| /* This case occurs in ~2% of the calls to this function. */ |
| size_t newMinCap = mLength + incr; |
| |
| /* Did mLength+incr overflow? Will newCap*sizeof(T) overflow? */ |
| if (newMinCap < mLength || |
| newMinCap & tl::MulOverflowMask<2 * sizeof(T)>::result) |
| { |
| this->reportAllocOverflow(); |
| return false; |
| } |
| |
| size_t newMinSize = newMinCap * sizeof(T); |
| size_t newSize = RoundUpPow2(newMinSize); |
| newCap = newSize / sizeof(T); |
| } |
| |
| if (usingInlineStorage()) { |
| convert: |
| return convertToHeapStorage(newCap); |
| } |
| |
| grow: |
| return Impl::growTo(*this, newCap); |
| } |
| |
| template <class T, size_t N, class AP> |
| inline bool |
| Vector<T,N,AP>::initCapacity(size_t request) |
| { |
| JS_ASSERT(empty()); |
| JS_ASSERT(usingInlineStorage()); |
| if (request == 0) |
| return true; |
| T *newbuf = reinterpret_cast<T *>(this->malloc_(request * sizeof(T))); |
| if (!newbuf) |
| return false; |
| mBegin = newbuf; |
| mCapacity = request; |
| #ifdef JS_DEBUG |
| mReserved = request; |
| #endif |
| return true; |
| } |
| |
| template <class T, size_t N, class AP> |
| inline bool |
| Vector<T,N,AP>::reserve(size_t request) |
| { |
| REENTRANCY_GUARD_ET_AL; |
| if (request > mCapacity && !growStorageBy(request - mLength)) |
| return false; |
| |
| #ifdef JS_DEBUG |
| if (request > mReserved) |
| mReserved = request; |
| JS_ASSERT(mLength <= mReserved); |
| JS_ASSERT(mReserved <= mCapacity); |
| #endif |
| return true; |
| } |
| |
| template <class T, size_t N, class AP> |
| inline void |
| Vector<T,N,AP>::shrinkBy(size_t incr) |
| { |
| REENTRANCY_GUARD_ET_AL; |
| JS_ASSERT(incr <= mLength); |
| Impl::destroy(endNoCheck() - incr, endNoCheck()); |
| mLength -= incr; |
| } |
| |
| template <class T, size_t N, class AP> |
| template <bool InitNewElems> |
| JS_ALWAYS_INLINE bool |
| Vector<T,N,AP>::growByImpl(size_t incr) |
| { |
| REENTRANCY_GUARD_ET_AL; |
| if (incr > mCapacity - mLength && !growStorageBy(incr)) |
| return false; |
| |
| JS_ASSERT(mLength + incr <= mCapacity); |
| T *newend = endNoCheck() + incr; |
| if (InitNewElems) |
| Impl::initialize(endNoCheck(), newend); |
| mLength += incr; |
| #ifdef JS_DEBUG |
| if (mLength > mReserved) |
| mReserved = mLength; |
| #endif |
| return true; |
| } |
| |
| template <class T, size_t N, class AP> |
| JS_ALWAYS_INLINE bool |
| Vector<T,N,AP>::growBy(size_t incr) |
| { |
| return growByImpl<true>(incr); |
| } |
| |
| template <class T, size_t N, class AP> |
| JS_ALWAYS_INLINE bool |
| Vector<T,N,AP>::growByUninitialized(size_t incr) |
| { |
| return growByImpl<false>(incr); |
| } |
| |
| template <class T, size_t N, class AP> |
| STATIC_POSTCONDITION(!return || ubound(this->begin()) >= newLength) |
| inline bool |
| Vector<T,N,AP>::resize(size_t newLength) |
| { |
| size_t curLength = mLength; |
| if (newLength > curLength) |
| return growBy(newLength - curLength); |
| shrinkBy(curLength - newLength); |
| return true; |
| } |
| |
| template <class T, size_t N, class AP> |
| JS_ALWAYS_INLINE bool |
| Vector<T,N,AP>::resizeUninitialized(size_t newLength) |
| { |
| size_t curLength = mLength; |
| if (newLength > curLength) |
| return growByUninitialized(newLength - curLength); |
| shrinkBy(curLength - newLength); |
| return true; |
| } |
| |
| template <class T, size_t N, class AP> |
| inline void |
| Vector<T,N,AP>::clear() |
| { |
| REENTRANCY_GUARD_ET_AL; |
| Impl::destroy(beginNoCheck(), endNoCheck()); |
| mLength = 0; |
| } |
| |
| template <class T, size_t N, class AP> |
| inline void |
| Vector<T,N,AP>::clearAndFree() |
| { |
| clear(); |
| |
| if (usingInlineStorage()) |
| return; |
| |
| this->free_(beginNoCheck()); |
| mBegin = (T *)storage.addr(); |
| mCapacity = sInlineCapacity; |
| #ifdef JS_DEBUG |
| mReserved = sInlineCapacity; |
| #endif |
| } |
| |
| template <class T, size_t N, class AP> |
| inline bool |
| Vector<T,N,AP>::canAppendWithoutRealloc(size_t needed) const |
| { |
| return mLength + needed <= mCapacity; |
| } |
| |
| template <class T, size_t N, class AP> |
| template <class U> |
| JS_ALWAYS_INLINE bool |
| Vector<T,N,AP>::append(U t) |
| { |
| REENTRANCY_GUARD_ET_AL; |
| if (mLength == mCapacity && !growStorageBy(1)) |
| return false; |
| |
| #ifdef JS_DEBUG |
| if (mLength + 1 > mReserved) |
| mReserved = mLength + 1; |
| #endif |
| internalAppend(t); |
| return true; |
| } |
| |
| template <class T, size_t N, class AP> |
| template <class U> |
| JS_ALWAYS_INLINE void |
| Vector<T,N,AP>::internalAppend(U u) |
| { |
| JS_ASSERT(mLength + 1 <= mReserved); |
| JS_ASSERT(mReserved <= mCapacity); |
| new(endNoCheck()) T(u); |
| ++mLength; |
| } |
| |
| template <class T, size_t N, class AP> |
| JS_ALWAYS_INLINE bool |
| Vector<T,N,AP>::appendN(const T &t, size_t needed) |
| { |
| REENTRANCY_GUARD_ET_AL; |
| if (mLength + needed > mCapacity && !growStorageBy(needed)) |
| return false; |
| |
| #ifdef JS_DEBUG |
| if (mLength + needed > mReserved) |
| mReserved = mLength + needed; |
| #endif |
| internalAppendN(t, needed); |
| return true; |
| } |
| |
| template <class T, size_t N, class AP> |
| JS_ALWAYS_INLINE void |
| Vector<T,N,AP>::internalAppendN(const T &t, size_t needed) |
| { |
| JS_ASSERT(mLength + needed <= mReserved); |
| JS_ASSERT(mReserved <= mCapacity); |
| Impl::copyConstructN(endNoCheck(), needed, t); |
| mLength += needed; |
| } |
| |
| template <class T, size_t N, class AP> |
| inline T * |
| Vector<T,N,AP>::insert(T *p, const T &val) |
| { |
| JS_ASSERT(begin() <= p && p <= end()); |
| size_t pos = p - begin(); |
| JS_ASSERT(pos <= mLength); |
| size_t oldLength = mLength; |
| if (pos == oldLength) { |
| if (!append(val)) |
| return NULL; |
| } else { |
| T oldBack = back(); |
| if (!append(oldBack)) /* Dup the last element. */ |
| return NULL; |
| for (size_t i = oldLength; i > pos; --i) |
| (*this)[i] = (*this)[i - 1]; |
| (*this)[pos] = val; |
| } |
| return begin() + pos; |
| } |
| |
| template<typename T, size_t N, class AP> |
| inline void |
| Vector<T,N,AP>::erase(T *it) |
| { |
| JS_ASSERT(begin() <= it && it < end()); |
| while (it + 1 != end()) { |
| *it = *(it + 1); |
| ++it; |
| } |
| popBack(); |
| } |
| |
| template <class T, size_t N, class AP> |
| template <class U> |
| JS_ALWAYS_INLINE bool |
| Vector<T,N,AP>::append(const U *insBegin, const U *insEnd) |
| { |
| REENTRANCY_GUARD_ET_AL; |
| size_t needed = mozilla::PointerRangeSize(insBegin, insEnd); |
| if (mLength + needed > mCapacity && !growStorageBy(needed)) |
| return false; |
| |
| #ifdef JS_DEBUG |
| if (mLength + needed > mReserved) |
| mReserved = mLength + needed; |
| #endif |
| internalAppend(insBegin, needed); |
| return true; |
| } |
| |
| template <class T, size_t N, class AP> |
| template <class U> |
| JS_ALWAYS_INLINE void |
| Vector<T,N,AP>::internalAppend(const U *insBegin, size_t insLength) |
| { |
| JS_ASSERT(mLength + insLength <= mReserved); |
| JS_ASSERT(mReserved <= mCapacity); |
| Impl::copyConstruct(endNoCheck(), insBegin, insBegin + insLength); |
| mLength += insLength; |
| } |
| |
| template <class T, size_t N, class AP> |
| template <class U, size_t O, class BP> |
| inline bool |
| Vector<T,N,AP>::append(const Vector<U,O,BP> &other) |
| { |
| return append(other.begin(), other.end()); |
| } |
| |
| template <class T, size_t N, class AP> |
| template <class U, size_t O, class BP> |
| inline void |
| Vector<T,N,AP>::internalAppend(const Vector<U,O,BP> &other) |
| { |
| internalAppend(other.begin(), other.length()); |
| } |
| |
| template <class T, size_t N, class AP> |
| template <class U> |
| JS_ALWAYS_INLINE bool |
| Vector<T,N,AP>::append(const U *insBegin, size_t insLength) |
| { |
| return this->append(insBegin, insBegin + insLength); |
| } |
| |
| template <class T, size_t N, class AP> |
| JS_ALWAYS_INLINE void |
| Vector<T,N,AP>::popBack() |
| { |
| REENTRANCY_GUARD_ET_AL; |
| JS_ASSERT(!empty()); |
| --mLength; |
| endNoCheck()->~T(); |
| } |
| |
| template <class T, size_t N, class AP> |
| JS_ALWAYS_INLINE T |
| Vector<T,N,AP>::popCopy() |
| { |
| T ret = back(); |
| popBack(); |
| return ret; |
| } |
| |
| template <class T, size_t N, class AP> |
| inline T * |
| Vector<T,N,AP>::extractRawBuffer() |
| { |
| T *ret; |
| if (usingInlineStorage()) { |
| ret = reinterpret_cast<T *>(this->malloc_(mLength * sizeof(T))); |
| if (!ret) |
| return NULL; |
| 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; |
| #ifdef JS_DEBUG |
| mReserved = sInlineCapacity; |
| #endif |
| } |
| return ret; |
| } |
| |
| template <class T, size_t N, class AP> |
| inline void |
| Vector<T,N,AP>::replaceRawBuffer(T *p, size_t aLength) |
| { |
| REENTRANCY_GUARD_ET_AL; |
| |
| /* Destroy what we have. */ |
| Impl::destroy(beginNoCheck(), endNoCheck()); |
| if (!usingInlineStorage()) |
| this->free_(beginNoCheck()); |
| |
| /* Take in the new buffer. */ |
| if (aLength <= sInlineCapacity) { |
| /* |
| * 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. |
| */ |
| mBegin = (T *)storage.addr(); |
| mLength = aLength; |
| mCapacity = sInlineCapacity; |
| Impl::moveConstruct(mBegin, p, p + aLength); |
| Impl::destroy(p, p + aLength); |
| this->free_(p); |
| } else { |
| mBegin = p; |
| mLength = aLength; |
| mCapacity = aLength; |
| } |
| #ifdef JS_DEBUG |
| mReserved = aLength; |
| #endif |
| } |
| |
| template <class T, size_t N, class AP> |
| inline size_t |
| Vector<T,N,AP>::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const |
| { |
| return usingInlineStorage() ? 0 : mallocSizeOf(beginNoCheck()); |
| } |
| |
| template <class T, size_t N, class AP> |
| inline size_t |
| Vector<T,N,AP>::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const |
| { |
| return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); |
| } |
| |
| template <class T, size_t N, class AP> |
| inline void |
| Vector<T,N,AP>::swap(Vector &other) |
| { |
| // TODO Implement N != 0 |
| JS_STATIC_ASSERT(N == 0); |
| |
| // This only works when inline storage is always empty. |
| if (!usingInlineStorage() && other.usingInlineStorage()) { |
| other.mBegin = mBegin; |
| mBegin = inlineStorage(); |
| } else if (usingInlineStorage() && !other.usingInlineStorage()) { |
| mBegin = other.mBegin; |
| other.mBegin = other.inlineStorage(); |
| } else if (!usingInlineStorage() && !other.usingInlineStorage()) { |
| Swap(mBegin, other.mBegin); |
| } else { |
| // This case is a no-op, since we'd set both to use their inline storage. |
| } |
| |
| Swap(mLength, other.mLength); |
| Swap(mCapacity, other.mCapacity); |
| #ifdef JS_DEBUG |
| Swap(mReserved, other.mReserved); |
| #endif |
| } |
| |
| } /* namespace js */ |
| |
| #ifdef _MSC_VER |
| #pragma warning(pop) |
| #endif |
| |
| #endif /* js_Vector_h */ |