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_ */