1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99 ft=cpp:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
21 * the Mozilla Corporation.
24 * Luke Wagner <lw@mozilla.com>
25 * Nicholas Nethercote <nnethercote@mozilla.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
47 /* Silence dire "bugs in previous versions of MSVC have been fixed" warnings */
50 #pragma warning(disable:4345)
53 /* Gross special case for Gecko, which defines malloc/calloc/free. */
54 #ifdef mozilla_mozalloc_macro_wrappers_h
55 # define JSVECTOR_UNDEFD_MOZALLOC_WRAPPERS
56 # include "mozilla/mozalloc_undef_macro_wrappers.h"
62 * This template class provides a default implementation for vector operations
63 * when the element type is not known to be a POD, as judged by IsPodType.
65 template <class T
, size_t N
, class AP
, bool IsPod
>
68 /* Destroys constructed objects in the range [begin, end). */
69 static inline void destroy(T
*begin
, T
*end
) {
70 for (T
*p
= begin
; p
!= end
; ++p
)
74 /* Constructs objects in the uninitialized range [begin, end). */
75 static inline void initialize(T
*begin
, T
*end
) {
76 for (T
*p
= begin
; p
!= end
; ++p
)
81 * Copy-constructs objects in the uninitialized range
82 * [dst, dst+(srcend-srcbeg)) from the range [srcbeg, srcend).
85 static inline void copyConstruct(T
*dst
, const U
*srcbeg
, const U
*srcend
) {
86 for (const U
*p
= srcbeg
; p
!= srcend
; ++p
, ++dst
)
91 * Copy-constructs objects in the uninitialized range [dst, dst+n) from the
95 static inline void copyConstructN(T
*dst
, size_t n
, const U
&u
) {
96 for (T
*end
= dst
+ n
; dst
!= end
; ++dst
)
101 * Grows the given buffer to have capacity newcap, preserving the objects
102 * constructed in the range [begin, end) and updating v. Assumes that (1)
103 * newcap has not overflowed, and (2) multiplying newcap by sizeof(T) will
106 static inline bool growTo(Vector
<T
,N
,AP
> &v
, size_t newcap
) {
107 JS_ASSERT(!v
.usingInlineStorage());
108 T
*newbuf
= reinterpret_cast<T
*>(v
.malloc(newcap
* sizeof(T
)));
111 for (T
*dst
= newbuf
, *src
= v
.beginNoCheck(); src
!= v
.endNoCheck(); ++dst
, ++src
)
113 VectorImpl::destroy(v
.beginNoCheck(), v
.endNoCheck());
116 /* v.mLength is unchanged. */
117 v
.mCapacity
= newcap
;
123 * This partial template specialization provides a default implementation for
124 * vector operations when the element type is known to be a POD, as judged by
127 template <class T
, size_t N
, class AP
>
128 struct VectorImpl
<T
, N
, AP
, true>
130 static inline void destroy(T
*, T
*) {}
132 static inline void initialize(T
*begin
, T
*end
) {
134 * You would think that memset would be a big win (or even break even)
135 * when we know T is a POD. But currently it's not. This is probably
136 * because |append| tends to be given small ranges and memset requires
137 * a function call that doesn't get inlined.
139 * memset(begin, 0, sizeof(T) * (end-begin));
141 for (T
*p
= begin
; p
!= end
; ++p
)
146 static inline void copyConstruct(T
*dst
, const U
*srcbeg
, const U
*srcend
) {
148 * See above memset comment. Also, notice that copyConstruct is
149 * currently templated (T != U), so memcpy won't work without
152 * memcpy(dst, srcbeg, sizeof(T) * (srcend - srcbeg));
154 for (const U
*p
= srcbeg
; p
!= srcend
; ++p
, ++dst
)
158 static inline void copyConstructN(T
*dst
, size_t n
, const T
&t
) {
159 for (T
*p
= dst
, *end
= dst
+ n
; p
!= end
; ++p
)
163 static inline bool growTo(Vector
<T
,N
,AP
> &v
, size_t newcap
) {
164 JS_ASSERT(!v
.usingInlineStorage());
165 size_t bytes
= sizeof(T
) * newcap
;
166 T
*newbuf
= reinterpret_cast<T
*>(v
.realloc(v
.mBegin
, bytes
));
170 /* v.mLength is unchanged. */
171 v
.mCapacity
= newcap
;
177 * JS-friendly, STL-like container providing a short-lived, dynamic buffer.
178 * Vector calls the constructors/destructors of all elements stored in
179 * its internal buffer, so non-PODs may be safely used. Additionally,
180 * Vector will store the first N elements in-place before resorting to
181 * dynamic allocation.
184 * - default and copy constructible, assignable, destructible
185 * - operations do not throw
187 * - any value, however, N is clamped to min/max values
189 * - see "Allocation policies" in jstl.h (default ContextAllocPolicy)
191 * N.B: Vector is not reentrant: T member functions called during Vector member
192 * functions must not call back into the same object.
194 template <class T
, size_t N
, class AllocPolicy
>
195 class Vector
: AllocPolicy
199 static const bool sElemIsPod
= tl::IsPodType
<T
>::result
;
200 typedef VectorImpl
<T
, N
, AllocPolicy
, sElemIsPod
> Impl
;
201 friend struct VectorImpl
<T
, N
, AllocPolicy
, sElemIsPod
>;
203 bool calculateNewCapacity(size_t curLength
, size_t lengthInc
, size_t &newCap
);
204 bool growStorageBy(size_t lengthInc
);
205 bool growHeapStorageBy(size_t lengthInc
);
206 bool convertToHeapStorage(size_t lengthInc
);
208 template <bool InitNewElems
> inline bool growByImpl(size_t inc
);
210 /* magic constants */
212 static const int sMaxInlineBytes
= 1024;
214 /* compute constants */
216 static const size_t sInlineCapacity
=
217 tl::Min
<N
, sMaxInlineBytes
/ sizeof(T
)>::result
;
219 /* Calculate inline buffer size; avoid 0-sized array. */
220 static const size_t sInlineBytes
=
221 tl::Max
<1, sInlineCapacity
* sizeof(T
)>::result
;
226 * Pointer to the buffer, be it inline or heap-allocated. Only [mBegin,
227 * mBegin + mLength) hold valid constructed T objects. The range [mBegin +
228 * mLength, mBegin + mCapacity) holds uninitialized memory.
231 size_t mLength
; /* Number of elements in the Vector. */
232 size_t mCapacity
; /* Max number of elements storable in the Vector without resizing. */
234 AlignedStorage
<sInlineBytes
> storage
;
237 friend class ReentrancyGuard
;
241 Vector(const Vector
&);
242 Vector
&operator=(const Vector
&);
244 /* private accessors */
246 bool usingInlineStorage() const {
247 return mBegin
== (T
*)storage
.addr();
250 T
*beginNoCheck() const {
255 return mBegin
+ mLength
;
258 const T
*endNoCheck() const {
259 return mBegin
+ mLength
;
263 typedef T ElementType
;
265 Vector(AllocPolicy
= AllocPolicy());
270 const AllocPolicy
&allocPolicy() const {
274 enum { InlineLength
= N
};
276 size_t length() const {
284 size_t capacity() const {
295 return mBegin
+ mLength
;
298 const T
*end() const {
300 return mBegin
+ mLength
;
303 T
&operator[](size_t i
) {
304 JS_ASSERT(!entered
&& i
< mLength
);
308 const T
&operator[](size_t i
) const {
309 JS_ASSERT(!entered
&& i
< mLength
);
314 JS_ASSERT(!entered
&& !empty());
318 const T
&back() const {
319 JS_ASSERT(!entered
&& !empty());
325 /* If reserve(length() + N) succeeds, the N next appends are guaranteed to succeed. */
326 bool reserve(size_t capacity
);
328 /* Destroy elements in the range [begin() + incr, end()). */
329 void shrinkBy(size_t incr
);
331 /* Grow the vector by incr elements. */
332 bool growBy(size_t incr
);
334 /* Call shrinkBy or growBy based on whether newSize > length(). */
335 bool resize(size_t newLength
);
337 /* Leave new elements as uninitialized memory. */
338 bool growByUninitialized(size_t incr
);
339 bool resizeUninitialized(size_t newLength
);
343 bool append(const T
&t
);
344 bool appendN(const T
&t
, size_t n
);
345 template <class U
> bool append(const U
*begin
, const U
*end
);
346 template <class U
> bool append(const U
*begin
, size_t length
);
347 template <class U
, size_t O
, class BP
> bool append(const Vector
<U
,O
,BP
> &other
);
354 * Transfers ownership of the internal buffer used by Vector to the caller.
355 * After this call, the Vector is empty. Since the returned buffer may need
356 * to be allocated (if the elements are currently stored in-place), the
357 * call can fail, returning NULL.
359 * N.B. Although a T*, only the range [0, length()) is constructed.
361 T
*extractRawBuffer();
364 * Transfer ownership of an array of objects into the Vector.
365 * N.B. This call assumes that there are no uninitialized elements in the
368 void replaceRawBuffer(T
*p
, size_t length
);
371 * Places |val| at position |p|, shifting existing elements
372 * from |p| onward one position higher.
374 bool insert(T
*p
, const T
&val
);
377 * Removes the element |t|, which must fall in the bounds [begin, end),
378 * shifting existing elements from |t + 1| onward one position lower.
383 /* This does the re-entrancy check plus several other sanity checks. */
384 #define REENTRANCY_GUARD_ET_AL \
385 ReentrancyGuard g(*this); \
386 JS_ASSERT_IF(usingInlineStorage(), mCapacity == sInlineCapacity); \
387 JS_ASSERT(mLength <= mCapacity)
389 /* Vector Implementation */
391 template <class T
, size_t N
, class AllocPolicy
>
393 Vector
<T
,N
,AllocPolicy
>::Vector(AllocPolicy ap
)
394 : AllocPolicy(ap
), mBegin((T
*)storage
.addr()), mLength(0),
395 mCapacity(sInlineCapacity
)
401 template <class T
, size_t N
, class AP
>
403 Vector
<T
,N
,AP
>::~Vector()
405 REENTRANCY_GUARD_ET_AL
;
406 Impl::destroy(beginNoCheck(), endNoCheck());
407 if (!usingInlineStorage())
408 this->free(beginNoCheck());
412 * Calculate a new capacity that is at least lengthInc greater than
413 * curLength and check for overflow.
415 template <class T
, size_t N
, class AP
>
416 STATIC_POSTCONDITION(!return || newCap
>= curLength
+ lengthInc
)
418 Vector
<T
,N
,AP
>::calculateNewCapacity(size_t curLength
, size_t lengthInc
,
421 size_t newMinCap
= curLength
+ lengthInc
;
424 * Check for overflow in the above addition, below CEILING_LOG2, and later
425 * multiplication by sizeof(T).
427 if (newMinCap
< curLength
||
428 newMinCap
& tl::MulOverflowMask
<2 * sizeof(T
)>::result
) {
429 this->reportAllocOverflow();
433 /* Round up to next power of 2. */
434 newCap
= RoundUpPow2(newMinCap
);
437 * Do not allow a buffer large enough that the expression ((char *)end() -
438 * (char *)begin()) overflows ptrdiff_t. See Bug 510319.
440 if (newCap
& tl::UnsafeRangeSizeMask
<T
>::result
) {
441 this->reportAllocOverflow();
448 * This function will grow the current heap capacity to have capacity
449 * (mLength + lengthInc) and fail on OOM or integer overflow.
451 template <class T
, size_t N
, class AP
>
452 JS_ALWAYS_INLINE
bool
453 Vector
<T
,N
,AP
>::growHeapStorageBy(size_t lengthInc
)
455 JS_ASSERT(!usingInlineStorage());
457 return calculateNewCapacity(mLength
, lengthInc
, newCap
) &&
458 Impl::growTo(*this, newCap
);
462 * This function will create a new heap buffer with capacity (mLength +
463 * lengthInc()), move all elements in the inline buffer to this new buffer,
464 * and fail on OOM or integer overflow.
466 template <class T
, size_t N
, class AP
>
468 Vector
<T
,N
,AP
>::convertToHeapStorage(size_t lengthInc
)
470 JS_ASSERT(usingInlineStorage());
472 if (!calculateNewCapacity(mLength
, lengthInc
, newCap
))
475 /* Allocate buffer. */
476 T
*newBuf
= reinterpret_cast<T
*>(this->malloc(newCap
* sizeof(T
)));
480 /* Copy inline elements into heap buffer. */
481 Impl::copyConstruct(newBuf
, beginNoCheck(), endNoCheck());
482 Impl::destroy(beginNoCheck(), endNoCheck());
484 /* Switch in heap buffer. */
486 /* mLength is unchanged. */
491 template <class T
, size_t N
, class AP
>
493 Vector
<T
,N
,AP
>::growStorageBy(size_t incr
)
495 JS_ASSERT(mLength
+ incr
> mCapacity
);
496 return usingInlineStorage()
497 ? convertToHeapStorage(incr
)
498 : growHeapStorageBy(incr
);
501 template <class T
, size_t N
, class AP
>
503 Vector
<T
,N
,AP
>::reserve(size_t request
)
505 REENTRANCY_GUARD_ET_AL
;
506 if (request
> mCapacity
)
507 return growStorageBy(request
- mLength
);
511 template <class T
, size_t N
, class AP
>
513 Vector
<T
,N
,AP
>::shrinkBy(size_t incr
)
515 REENTRANCY_GUARD_ET_AL
;
516 JS_ASSERT(incr
<= mLength
);
517 Impl::destroy(endNoCheck() - incr
, endNoCheck());
521 template <class T
, size_t N
, class AP
>
522 template <bool InitNewElems
>
523 JS_ALWAYS_INLINE
bool
524 Vector
<T
,N
,AP
>::growByImpl(size_t incr
)
526 REENTRANCY_GUARD_ET_AL
;
527 if (incr
> mCapacity
- mLength
&& !growStorageBy(incr
))
530 JS_ASSERT(mLength
+ incr
<= mCapacity
);
531 T
*newend
= endNoCheck() + incr
;
533 Impl::initialize(endNoCheck(), newend
);
538 template <class T
, size_t N
, class AP
>
539 JS_ALWAYS_INLINE
bool
540 Vector
<T
,N
,AP
>::growBy(size_t incr
)
542 return growByImpl
<true>(incr
);
545 template <class T
, size_t N
, class AP
>
546 JS_ALWAYS_INLINE
bool
547 Vector
<T
,N
,AP
>::growByUninitialized(size_t incr
)
549 return growByImpl
<false>(incr
);
552 template <class T
, size_t N
, class AP
>
553 STATIC_POSTCONDITION(!return || ubound(this->begin()) >= newLength
)
555 Vector
<T
,N
,AP
>::resize(size_t newLength
)
557 size_t curLength
= mLength
;
558 if (newLength
> curLength
)
559 return growBy(newLength
- curLength
);
560 shrinkBy(curLength
- newLength
);
564 template <class T
, size_t N
, class AP
>
565 JS_ALWAYS_INLINE
bool
566 Vector
<T
,N
,AP
>::resizeUninitialized(size_t newLength
)
568 size_t curLength
= mLength
;
569 if (newLength
> curLength
)
570 return growByUninitialized(newLength
- curLength
);
571 shrinkBy(curLength
- newLength
);
575 template <class T
, size_t N
, class AP
>
577 Vector
<T
,N
,AP
>::clear()
579 REENTRANCY_GUARD_ET_AL
;
580 Impl::destroy(beginNoCheck(), endNoCheck());
584 template <class T
, size_t N
, class AP
>
585 JS_ALWAYS_INLINE
bool
586 Vector
<T
,N
,AP
>::append(const T
&t
)
588 REENTRANCY_GUARD_ET_AL
;
589 if (mLength
== mCapacity
&& !growStorageBy(1))
592 JS_ASSERT(mLength
< mCapacity
);
593 new(endNoCheck()) T(t
);
598 template <class T
, size_t N
, class AP
>
599 JS_ALWAYS_INLINE
bool
600 Vector
<T
,N
,AP
>::appendN(const T
&t
, size_t needed
)
602 REENTRANCY_GUARD_ET_AL
;
603 if (mLength
+ needed
> mCapacity
&& !growStorageBy(needed
))
606 JS_ASSERT(mLength
+ needed
<= mCapacity
);
607 Impl::copyConstructN(endNoCheck(), needed
, t
);
612 template <class T
, size_t N
, class AP
>
614 Vector
<T
,N
,AP
>::insert(T
*p
, const T
&val
)
616 JS_ASSERT(begin() <= p
&& p
<= end());
617 size_t pos
= p
- begin();
618 JS_ASSERT(pos
<= mLength
);
619 size_t oldLength
= mLength
;
620 if (pos
== oldLength
)
624 if (!append(oldBack
)) /* Dup the last element. */
627 for (size_t i
= oldLength
; i
> pos
; --i
)
628 (*this)[i
] = (*this)[i
- 1];
633 template<typename T
, size_t N
, class AP
>
635 Vector
<T
,N
,AP
>::erase(T
*it
)
637 JS_ASSERT(begin() <= it
&& it
< end());
638 while (it
+ 1 != end()) {
645 template <class T
, size_t N
, class AP
>
647 JS_ALWAYS_INLINE
bool
648 Vector
<T
,N
,AP
>::append(const U
*insBegin
, const U
*insEnd
)
650 REENTRANCY_GUARD_ET_AL
;
651 size_t needed
= PointerRangeSize(insBegin
, insEnd
);
652 if (mLength
+ needed
> mCapacity
&& !growStorageBy(needed
))
655 JS_ASSERT(mLength
+ needed
<= mCapacity
);
656 Impl::copyConstruct(endNoCheck(), insBegin
, insEnd
);
661 template <class T
, size_t N
, class AP
>
662 template <class U
, size_t O
, class BP
>
664 Vector
<T
,N
,AP
>::append(const Vector
<U
,O
,BP
> &other
)
666 return append(other
.begin(), other
.end());
669 template <class T
, size_t N
, class AP
>
671 JS_ALWAYS_INLINE
bool
672 Vector
<T
,N
,AP
>::append(const U
*insBegin
, size_t length
)
674 return this->append(insBegin
, insBegin
+ length
);
677 template <class T
, size_t N
, class AP
>
678 JS_ALWAYS_INLINE
void
679 Vector
<T
,N
,AP
>::popBack()
681 REENTRANCY_GUARD_ET_AL
;
687 template <class T
, size_t N
, class AP
>
689 Vector
<T
,N
,AP
>::popCopy()
696 template <class T
, size_t N
, class AP
>
698 Vector
<T
,N
,AP
>::extractRawBuffer()
701 if (usingInlineStorage()) {
702 ret
= reinterpret_cast<T
*>(this->malloc(mLength
* sizeof(T
)));
705 Impl::copyConstruct(ret
, beginNoCheck(), endNoCheck());
706 Impl::destroy(beginNoCheck(), endNoCheck());
707 /* mBegin, mCapacity are unchanged. */
711 mBegin
= (T
*)storage
.addr();
713 mCapacity
= sInlineCapacity
;
718 template <class T
, size_t N
, class AP
>
720 Vector
<T
,N
,AP
>::replaceRawBuffer(T
*p
, size_t length
)
722 REENTRANCY_GUARD_ET_AL
;
724 /* Destroy what we have. */
725 Impl::destroy(beginNoCheck(), endNoCheck());
726 if (!usingInlineStorage())
727 this->free(beginNoCheck());
729 /* Take in the new buffer. */
730 if (length
<= sInlineCapacity
) {
732 * We convert to inline storage if possible, even though p might
733 * otherwise be acceptable. Maybe this behaviour should be
734 * specifiable with an argument to this function.
736 mBegin
= (T
*)storage
.addr();
738 mCapacity
= sInlineCapacity
;
739 Impl::copyConstruct(mBegin
, p
, p
+ length
);
740 Impl::destroy(p
, p
+ length
);
755 #ifdef JSVECTOR_UNDEFD_MOZALLOC_WRAPPERS
756 # include "mozilla/mozalloc_macro_wrappers.h"
759 #endif /* jsvector_h_ */