CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / js / src / jsvector.h
blobab432bae7647618150882abb4c53983c4a7ec656
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
15 * License.
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 * June 12, 2009.
20 * The Initial Developer of the Original Code is
21 * the Mozilla Corporation.
23 * Contributor(s):
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 ***** */
41 #ifndef jsvector_h_
42 #define jsvector_h_
44 #include "jstl.h"
45 #include "jsprvtd.h"
47 /* Silence dire "bugs in previous versions of MSVC have been fixed" warnings */
48 #ifdef _MSC_VER
49 #pragma warning(push)
50 #pragma warning(disable:4345)
51 #endif
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"
57 #endif
59 namespace js {
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>
66 struct VectorImpl
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)
71 p->~T();
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)
77 new(p) T();
81 * Copy-constructs objects in the uninitialized range
82 * [dst, dst+(srcend-srcbeg)) from the range [srcbeg, srcend).
84 template <class U>
85 static inline void copyConstruct(T *dst, const U *srcbeg, const U *srcend) {
86 for (const U *p = srcbeg; p != srcend; ++p, ++dst)
87 new(dst) T(*p);
91 * Copy-constructs objects in the uninitialized range [dst, dst+n) from the
92 * same object u.
94 template <class U>
95 static inline void copyConstructN(T *dst, size_t n, const U &u) {
96 for (T *end = dst + n; dst != end; ++dst)
97 new(dst) T(u);
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
104 * not overflow.
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)));
109 if (!newbuf)
110 return false;
111 for (T *dst = newbuf, *src = v.beginNoCheck(); src != v.endNoCheck(); ++dst, ++src)
112 new(dst) T(*src);
113 VectorImpl::destroy(v.beginNoCheck(), v.endNoCheck());
114 v.free(v.mBegin);
115 v.mBegin = newbuf;
116 /* v.mLength is unchanged. */
117 v.mCapacity = newcap;
118 return true;
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
125 * IsPodType.
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)
142 new(p) T();
145 template <class U>
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
150 * requiring T == U.
152 * memcpy(dst, srcbeg, sizeof(T) * (srcend - srcbeg));
154 for (const U *p = srcbeg; p != srcend; ++p, ++dst)
155 *dst = *p;
158 static inline void copyConstructN(T *dst, size_t n, const T &t) {
159 for (T *p = dst, *end = dst + n; p != end; ++p)
160 *p = t;
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));
167 if (!newbuf)
168 return false;
169 v.mBegin = newbuf;
170 /* v.mLength is unchanged. */
171 v.mCapacity = newcap;
172 return true;
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.
183 * T requirements:
184 * - default and copy constructible, assignable, destructible
185 * - operations do not throw
186 * N requirements:
187 * - any value, however, N is clamped to min/max values
188 * AllocPolicy:
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
197 /* utilities */
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;
223 /* member data */
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.
230 T *mBegin;
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;
236 #ifdef DEBUG
237 friend class ReentrancyGuard;
238 bool entered;
239 #endif
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 {
251 return mBegin;
254 T *endNoCheck() {
255 return mBegin + mLength;
258 const T *endNoCheck() const {
259 return mBegin + mLength;
262 public:
263 typedef T ElementType;
265 Vector(AllocPolicy = AllocPolicy());
266 ~Vector();
268 /* accessors */
270 const AllocPolicy &allocPolicy() const {
271 return *this;
274 enum { InlineLength = N };
276 size_t length() const {
277 return mLength;
280 bool empty() const {
281 return mLength == 0;
284 size_t capacity() const {
285 return mCapacity;
288 T *begin() const {
289 JS_ASSERT(!entered);
290 return mBegin;
293 T *end() {
294 JS_ASSERT(!entered);
295 return mBegin + mLength;
298 const T *end() const {
299 JS_ASSERT(!entered);
300 return mBegin + mLength;
303 T &operator[](size_t i) {
304 JS_ASSERT(!entered && i < mLength);
305 return begin()[i];
308 const T &operator[](size_t i) const {
309 JS_ASSERT(!entered && i < mLength);
310 return begin()[i];
313 T &back() {
314 JS_ASSERT(!entered && !empty());
315 return *(end() - 1);
318 const T &back() const {
319 JS_ASSERT(!entered && !empty());
320 return *(end() - 1);
323 /* mutators */
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);
341 void clear();
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);
349 void popBack();
351 T popCopy();
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
366 * passed array.
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.
380 void erase(T *t);
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>
392 JS_ALWAYS_INLINE
393 Vector<T,N,AllocPolicy>::Vector(AllocPolicy ap)
394 : AllocPolicy(ap), mBegin((T *)storage.addr()), mLength(0),
395 mCapacity(sInlineCapacity)
396 #ifdef DEBUG
397 , entered(false)
398 #endif
401 template <class T, size_t N, class AP>
402 JS_ALWAYS_INLINE
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)
417 inline bool
418 Vector<T,N,AP>::calculateNewCapacity(size_t curLength, size_t lengthInc,
419 size_t &newCap)
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();
430 return false;
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();
442 return false;
444 return true;
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());
456 size_t newCap;
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>
467 inline bool
468 Vector<T,N,AP>::convertToHeapStorage(size_t lengthInc)
470 JS_ASSERT(usingInlineStorage());
471 size_t newCap;
472 if (!calculateNewCapacity(mLength, lengthInc, newCap))
473 return false;
475 /* Allocate buffer. */
476 T *newBuf = reinterpret_cast<T *>(this->malloc(newCap * sizeof(T)));
477 if (!newBuf)
478 return false;
480 /* Copy inline elements into heap buffer. */
481 Impl::copyConstruct(newBuf, beginNoCheck(), endNoCheck());
482 Impl::destroy(beginNoCheck(), endNoCheck());
484 /* Switch in heap buffer. */
485 mBegin = newBuf;
486 /* mLength is unchanged. */
487 mCapacity = newCap;
488 return true;
491 template <class T, size_t N, class AP>
492 JS_NEVER_INLINE bool
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>
502 inline bool
503 Vector<T,N,AP>::reserve(size_t request)
505 REENTRANCY_GUARD_ET_AL;
506 if (request > mCapacity)
507 return growStorageBy(request - mLength);
508 return true;
511 template <class T, size_t N, class AP>
512 inline void
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());
518 mLength -= incr;
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))
528 return false;
530 JS_ASSERT(mLength + incr <= mCapacity);
531 T *newend = endNoCheck() + incr;
532 if (InitNewElems)
533 Impl::initialize(endNoCheck(), newend);
534 mLength += incr;
535 return true;
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)
554 inline bool
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);
561 return true;
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);
572 return true;
575 template <class T, size_t N, class AP>
576 inline void
577 Vector<T,N,AP>::clear()
579 REENTRANCY_GUARD_ET_AL;
580 Impl::destroy(beginNoCheck(), endNoCheck());
581 mLength = 0;
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))
590 return false;
592 JS_ASSERT(mLength < mCapacity);
593 new(endNoCheck()) T(t);
594 ++mLength;
595 return true;
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))
604 return false;
606 JS_ASSERT(mLength + needed <= mCapacity);
607 Impl::copyConstructN(endNoCheck(), needed, t);
608 mLength += needed;
609 return true;
612 template <class T, size_t N, class AP>
613 inline bool
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)
621 return append(val);
623 T oldBack = back();
624 if (!append(oldBack)) /* Dup the last element. */
625 return false;
627 for (size_t i = oldLength; i > pos; --i)
628 (*this)[i] = (*this)[i - 1];
629 (*this)[pos] = val;
630 return true;
633 template<typename T, size_t N, class AP>
634 inline void
635 Vector<T,N,AP>::erase(T *it)
637 JS_ASSERT(begin() <= it && it < end());
638 while (it + 1 != end()) {
639 *it = *(it + 1);
640 ++it;
642 popBack();
645 template <class T, size_t N, class AP>
646 template <class U>
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))
653 return false;
655 JS_ASSERT(mLength + needed <= mCapacity);
656 Impl::copyConstruct(endNoCheck(), insBegin, insEnd);
657 mLength += needed;
658 return true;
661 template <class T, size_t N, class AP>
662 template <class U, size_t O, class BP>
663 inline bool
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>
670 template <class U>
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;
682 JS_ASSERT(!empty());
683 --mLength;
684 endNoCheck()->~T();
687 template <class T, size_t N, class AP>
688 JS_ALWAYS_INLINE T
689 Vector<T,N,AP>::popCopy()
691 T ret = back();
692 popBack();
693 return ret;
696 template <class T, size_t N, class AP>
697 inline T *
698 Vector<T,N,AP>::extractRawBuffer()
700 T *ret;
701 if (usingInlineStorage()) {
702 ret = reinterpret_cast<T *>(this->malloc(mLength * sizeof(T)));
703 if (!ret)
704 return NULL;
705 Impl::copyConstruct(ret, beginNoCheck(), endNoCheck());
706 Impl::destroy(beginNoCheck(), endNoCheck());
707 /* mBegin, mCapacity are unchanged. */
708 mLength = 0;
709 } else {
710 ret = mBegin;
711 mBegin = (T *)storage.addr();
712 mLength = 0;
713 mCapacity = sInlineCapacity;
715 return ret;
718 template <class T, size_t N, class AP>
719 inline void
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();
737 mLength = length;
738 mCapacity = sInlineCapacity;
739 Impl::copyConstruct(mBegin, p, p + length);
740 Impl::destroy(p, p + length);
741 this->free(p);
742 } else {
743 mBegin = p;
744 mLength = length;
745 mCapacity = length;
749 } /* namespace js */
751 #ifdef _MSC_VER
752 #pragma warning(pop)
753 #endif
755 #ifdef JSVECTOR_UNDEFD_MOZALLOC_WRAPPERS
756 # include "mozilla/mozalloc_macro_wrappers.h"
757 #endif
759 #endif /* jsvector_h_ */