/src/mozilla-central/xpcom/ds/nsTArray-inl.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #ifndef nsTArray_h__ |
8 | | # error "Don't include this file directly" |
9 | | #endif |
10 | | |
11 | | template<class Alloc, class Copy> |
12 | | nsTArray_base<Alloc, Copy>::nsTArray_base() |
13 | | : mHdr(EmptyHdr()) |
14 | 0 | { |
15 | 0 | MOZ_COUNT_CTOR(nsTArray_base); |
16 | 0 | } Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<JS::Heap<JSObject*> > >::nsTArray_base() Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<RefPtr<nsDOMMutationObserver>, 4ul> > >::nsTArray_base() Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<nsINode*, 8ul> > >::nsTArray_base() Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<JS::Heap<JS::Value> > >::nsTArray_base() |
17 | | |
18 | | template<class Alloc, class Copy> |
19 | | nsTArray_base<Alloc, Copy>::~nsTArray_base() |
20 | 0 | { |
21 | 0 | if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) { |
22 | 0 | Alloc::Free(mHdr); |
23 | 0 | } |
24 | 0 | MOZ_COUNT_DTOR(nsTArray_base); |
25 | 0 | } Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<JS::Heap<JSObject*> > >::~nsTArray_base() Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<RefPtr<nsDOMMutationObserver>, 4ul> > >::~nsTArray_base() Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<nsINode*, 8ul> > >::~nsTArray_base() Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<JS::Heap<JS::Value> > >::~nsTArray_base() |
26 | | |
27 | | template<class Alloc, class Copy> |
28 | | const nsTArrayHeader* |
29 | | nsTArray_base<Alloc, Copy>::GetAutoArrayBufferUnsafe(size_t aElemAlign) const |
30 | 0 | { |
31 | 0 | // Assuming |this| points to an nsAutoArray, we want to get a pointer to |
32 | 0 | // mAutoBuf. So just cast |this| to nsAutoArray* and read &mAutoBuf! |
33 | 0 |
|
34 | 0 | const void* autoBuf = |
35 | 0 | &reinterpret_cast<const AutoTArray<nsTArray<uint32_t>, 1>*>(this)->mAutoBuf; |
36 | 0 |
|
37 | 0 | // If we're on a 32-bit system and aElemAlign is 8, we need to adjust our |
38 | 0 | // pointer to take into account the extra alignment in the auto array. |
39 | 0 |
|
40 | 0 | static_assert(sizeof(void*) != 4 || |
41 | 0 | (MOZ_ALIGNOF(mozilla::AlignedElem<8>) == 8 && |
42 | 0 | sizeof(AutoTArray<mozilla::AlignedElem<8>, 1>) == |
43 | 0 | sizeof(void*) + sizeof(nsTArrayHeader) + |
44 | 0 | 4 + sizeof(mozilla::AlignedElem<8>)), |
45 | 0 | "auto array padding wasn't what we expected"); |
46 | 0 |
|
47 | 0 | // We don't support alignments greater than 8 bytes. |
48 | 0 | MOZ_ASSERT(aElemAlign <= 4 || aElemAlign == 8, |
49 | 0 | "unsupported alignment."); |
50 | 0 | if (sizeof(void*) == 4 && aElemAlign == 8) { |
51 | 0 | autoBuf = reinterpret_cast<const char*>(autoBuf) + 4; |
52 | 0 | } |
53 | 0 |
|
54 | 0 | return reinterpret_cast<const Header*>(autoBuf); |
55 | 0 | } Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<JS::Heap<JSObject*> > >::GetAutoArrayBufferUnsafe(unsigned long) const Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<RefPtr<nsDOMMutationObserver>, 4ul> > >::GetAutoArrayBufferUnsafe(unsigned long) const Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<nsINode*, 8ul> > >::GetAutoArrayBufferUnsafe(unsigned long) const Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<JS::Heap<JS::Value> > >::GetAutoArrayBufferUnsafe(unsigned long) const |
56 | | |
57 | | template<class Alloc, class Copy> |
58 | | bool |
59 | | nsTArray_base<Alloc, Copy>::UsesAutoArrayBuffer() const |
60 | 0 | { |
61 | 0 | if (!mHdr->mIsAutoArray) { |
62 | 0 | return false; |
63 | 0 | } |
64 | 0 | |
65 | 0 | // This is nuts. If we were sane, we'd pass aElemAlign as a parameter to |
66 | 0 | // this function. Unfortunately this function is called in nsTArray_base's |
67 | 0 | // destructor, at which point we don't know elem_type's alignment. |
68 | 0 | // |
69 | 0 | // We'll fall on our face and return true when we should say false if |
70 | 0 | // |
71 | 0 | // * we're not using our auto buffer, |
72 | 0 | // * aElemAlign == 4, and |
73 | 0 | // * mHdr == GetAutoArrayBuffer(8). |
74 | 0 | // |
75 | 0 | // This could happen if |*this| lives on the heap and malloc allocated our |
76 | 0 | // buffer on the heap adjacent to |*this|. |
77 | 0 | // |
78 | 0 | // However, we can show that this can't happen. If |this| is an auto array |
79 | 0 | // (as we ensured at the beginning of the method), GetAutoArrayBuffer(8) |
80 | 0 | // always points to memory owned by |*this|, because (as we assert below) |
81 | 0 | // |
82 | 0 | // * GetAutoArrayBuffer(8) is at most 4 bytes past GetAutoArrayBuffer(4), and |
83 | 0 | // * sizeof(nsTArrayHeader) > 4. |
84 | 0 | // |
85 | 0 | // Since AutoTArray always contains an nsTArrayHeader, |
86 | 0 | // GetAutoArrayBuffer(8) will always point inside the auto array object, |
87 | 0 | // even if it doesn't point at the beginning of the header. |
88 | 0 | // |
89 | 0 | // Note that this means that we can't store elements with alignment 16 in an |
90 | 0 | // nsTArray, because GetAutoArrayBuffer(16) could lie outside the memory |
91 | 0 | // owned by this AutoTArray. We statically assert that elem_type's |
92 | 0 | // alignment is 8 bytes or less in AutoTArray. |
93 | 0 | |
94 | 0 | static_assert(sizeof(nsTArrayHeader) > 4, |
95 | 0 | "see comment above"); |
96 | 0 |
|
97 | | #ifdef DEBUG |
98 | | ptrdiff_t diff = reinterpret_cast<const char*>(GetAutoArrayBuffer(8)) - |
99 | | reinterpret_cast<const char*>(GetAutoArrayBuffer(4)); |
100 | | MOZ_ASSERT(diff >= 0 && diff <= 4, |
101 | | "GetAutoArrayBuffer doesn't do what we expect."); |
102 | | #endif |
103 | |
|
104 | 0 | return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8); |
105 | 0 | } Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<JS::Heap<JSObject*> > >::UsesAutoArrayBuffer() const Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<RefPtr<nsDOMMutationObserver>, 4ul> > >::UsesAutoArrayBuffer() const Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<nsINode*, 8ul> > >::UsesAutoArrayBuffer() const Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<JS::Heap<JS::Value> > >::UsesAutoArrayBuffer() const |
106 | | |
107 | | // defined in nsTArray.cpp |
108 | | bool IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity, |
109 | | size_t aElemSize); |
110 | | |
111 | | template<class Alloc, class Copy> |
112 | | template<typename ActualAlloc> |
113 | | typename ActualAlloc::ResultTypeProxy |
114 | | nsTArray_base<Alloc, Copy>::EnsureCapacity(size_type aCapacity, |
115 | | size_type aElemSize) |
116 | 0 | { |
117 | 0 | // This should be the most common case so test this first |
118 | 0 | if (aCapacity <= mHdr->mCapacity) { |
119 | 0 | return ActualAlloc::SuccessResult(); |
120 | 0 | } |
121 | 0 | |
122 | 0 | // If the requested memory allocation exceeds size_type(-1)/2, then |
123 | 0 | // our doubling algorithm may not be able to allocate it. |
124 | 0 | // Additionally, if it exceeds uint32_t(-1) then we couldn't fit in the |
125 | 0 | // Header::mCapacity member. Just bail out in cases like that. We don't want |
126 | 0 | // to be allocating 2 GB+ arrays anyway. |
127 | 0 | if (!IsTwiceTheRequiredBytesRepresentableAsUint32(aCapacity, aElemSize)) { |
128 | 0 | ActualAlloc::SizeTooBig((size_t)aCapacity * aElemSize); |
129 | 0 | return ActualAlloc::FailureResult(); |
130 | 0 | } |
131 | 0 | |
132 | 0 | size_t reqSize = sizeof(Header) + aCapacity * aElemSize; |
133 | 0 |
|
134 | 0 | if (mHdr == EmptyHdr()) { |
135 | 0 | // Malloc() new data |
136 | 0 | Header* header = static_cast<Header*>(ActualAlloc::Malloc(reqSize)); |
137 | 0 | if (!header) { |
138 | 0 | return ActualAlloc::FailureResult(); |
139 | 0 | } |
140 | 0 | header->mLength = 0; |
141 | 0 | header->mCapacity = aCapacity; |
142 | 0 | header->mIsAutoArray = 0; |
143 | 0 | mHdr = header; |
144 | 0 |
|
145 | 0 | return ActualAlloc::SuccessResult(); |
146 | 0 | } |
147 | 0 | |
148 | 0 | // We increase our capacity so that the allocated buffer grows exponentially, |
149 | 0 | // which gives us amortized O(1) appending. Below the threshold, we use |
150 | 0 | // powers-of-two. Above the threshold, we grow by at least 1.125, rounding up |
151 | 0 | // to the nearest MiB. |
152 | 0 | const size_t slowGrowthThreshold = 8 * 1024 * 1024; |
153 | 0 |
|
154 | 0 | size_t bytesToAlloc; |
155 | 0 | if (reqSize >= slowGrowthThreshold) { |
156 | 0 | size_t currSize = sizeof(Header) + Capacity() * aElemSize; |
157 | 0 | size_t minNewSize = currSize + (currSize >> 3); // multiply by 1.125 |
158 | 0 | bytesToAlloc = reqSize > minNewSize ? reqSize : minNewSize; |
159 | 0 |
|
160 | 0 | // Round up to the next multiple of MiB. |
161 | 0 | const size_t MiB = 1 << 20; |
162 | 0 | bytesToAlloc = MiB * ((bytesToAlloc + MiB - 1) / MiB); |
163 | 0 | } else { |
164 | 0 | // Round up to the next power of two. |
165 | 0 | bytesToAlloc = mozilla::RoundUpPow2(reqSize); |
166 | 0 | } |
167 | 0 |
|
168 | 0 | Header* header; |
169 | 0 | if (UsesAutoArrayBuffer() || !Copy::allowRealloc) { |
170 | 0 | // Malloc() and copy |
171 | 0 | header = static_cast<Header*>(ActualAlloc::Malloc(bytesToAlloc)); |
172 | 0 | if (!header) { |
173 | 0 | return ActualAlloc::FailureResult(); |
174 | 0 | } |
175 | 0 | |
176 | 0 | Copy::MoveNonOverlappingRegionWithHeader(header, mHdr, Length(), aElemSize); |
177 | 0 |
|
178 | 0 | if (!UsesAutoArrayBuffer()) { |
179 | 0 | ActualAlloc::Free(mHdr); |
180 | 0 | } |
181 | 0 | } else { |
182 | 0 | // Realloc() existing data |
183 | 0 | header = static_cast<Header*>(ActualAlloc::Realloc(mHdr, bytesToAlloc)); |
184 | 0 | if (!header) { |
185 | 0 | return ActualAlloc::FailureResult(); |
186 | 0 | } |
187 | 0 | } |
188 | 0 | |
189 | 0 | // How many elements can we fit in bytesToAlloc? |
190 | 0 | size_t newCapacity = (bytesToAlloc - sizeof(Header)) / aElemSize; |
191 | 0 | MOZ_ASSERT(newCapacity >= aCapacity, "Didn't enlarge the array enough!"); |
192 | 0 | header->mCapacity = newCapacity; |
193 | 0 |
|
194 | 0 | mHdr = header; |
195 | 0 |
|
196 | 0 | return ActualAlloc::SuccessResult(); |
197 | 0 | } Unexecuted instantiation: nsTArrayInfallibleAllocator::ResultTypeProxy nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<JS::Heap<JSObject*> > >::EnsureCapacity<nsTArrayInfallibleAllocator>(unsigned long, unsigned long) Unexecuted instantiation: nsTArrayInfallibleAllocator::ResultTypeProxy nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<RefPtr<nsDOMMutationObserver>, 4ul> > >::EnsureCapacity<nsTArrayInfallibleAllocator>(unsigned long, unsigned long) Unexecuted instantiation: nsTArrayInfallibleAllocator::ResultTypeProxy nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<nsINode*, 8ul> > >::EnsureCapacity<nsTArrayInfallibleAllocator>(unsigned long, unsigned long) Unexecuted instantiation: nsTArrayInfallibleAllocator::ResultTypeProxy nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<JS::Heap<JS::Value> > >::EnsureCapacity<nsTArrayInfallibleAllocator>(unsigned long, unsigned long) Unexecuted instantiation: nsTArrayFallibleAllocator::ResultTypeProxy nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<JS::Heap<JS::Value> > >::EnsureCapacity<nsTArrayFallibleAllocator>(unsigned long, unsigned long) |
198 | | |
199 | | // We don't need use Alloc template parameter specified here because failure to |
200 | | // shrink the capacity will leave the array unchanged. |
201 | | template<class Alloc, class Copy> |
202 | | void |
203 | | nsTArray_base<Alloc, Copy>::ShrinkCapacity(size_type aElemSize, |
204 | | size_t aElemAlign) |
205 | 0 | { |
206 | 0 | if (mHdr == EmptyHdr() || UsesAutoArrayBuffer()) { |
207 | 0 | return; |
208 | 0 | } |
209 | 0 | |
210 | 0 | if (mHdr->mLength >= mHdr->mCapacity) { // should never be greater than... |
211 | 0 | return; |
212 | 0 | } |
213 | 0 | |
214 | 0 | size_type length = Length(); |
215 | 0 |
|
216 | 0 | if (IsAutoArray() && GetAutoArrayBuffer(aElemAlign)->mCapacity >= length) { |
217 | 0 | Header* header = GetAutoArrayBuffer(aElemAlign); |
218 | 0 |
|
219 | 0 | // Move the data, but don't copy the header to avoid overwriting mCapacity. |
220 | 0 | header->mLength = length; |
221 | 0 | Copy::MoveNonOverlappingRegion(header + 1, mHdr + 1, length, aElemSize); |
222 | 0 |
|
223 | 0 | nsTArrayFallibleAllocator::Free(mHdr); |
224 | 0 | mHdr = header; |
225 | 0 | return; |
226 | 0 | } |
227 | 0 | |
228 | 0 | if (length == 0) { |
229 | 0 | MOZ_ASSERT(!IsAutoArray(), "autoarray should have fit 0 elements"); |
230 | 0 | nsTArrayFallibleAllocator::Free(mHdr); |
231 | 0 | mHdr = EmptyHdr(); |
232 | 0 | return; |
233 | 0 | } |
234 | 0 |
|
235 | 0 | size_type size = sizeof(Header) + length * aElemSize; |
236 | 0 | void* ptr = nsTArrayFallibleAllocator::Realloc(mHdr, size); |
237 | 0 | if (!ptr) { |
238 | 0 | return; |
239 | 0 | } |
240 | 0 | mHdr = static_cast<Header*>(ptr); |
241 | 0 | mHdr->mCapacity = length; |
242 | 0 | } Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<mozilla::dom::ipc::StructuredCloneData> >::ShrinkCapacity(unsigned long, unsigned long) Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<JS::Heap<JSObject*> > >::ShrinkCapacity(unsigned long, unsigned long) Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<RefPtr<nsDOMMutationObserver>, 4ul> > >::ShrinkCapacity(unsigned long, unsigned long) Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<nsINode*, 8ul> > >::ShrinkCapacity(unsigned long, unsigned long) Unexecuted instantiation: nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<JS::Heap<JS::Value> > >::ShrinkCapacity(unsigned long, unsigned long) |
243 | | |
244 | | template<class Alloc, class Copy> |
245 | | template<typename ActualAlloc> |
246 | | void |
247 | | nsTArray_base<Alloc, Copy>::ShiftData(index_type aStart, |
248 | | size_type aOldLen, size_type aNewLen, |
249 | | size_type aElemSize, size_t aElemAlign) |
250 | 0 | { |
251 | 0 | if (aOldLen == aNewLen) { |
252 | 0 | return; |
253 | 0 | } |
254 | 0 | |
255 | 0 | // Determine how many elements need to be shifted |
256 | 0 | size_type num = mHdr->mLength - (aStart + aOldLen); |
257 | 0 |
|
258 | 0 | // Compute the resulting length of the array |
259 | 0 | mHdr->mLength += aNewLen - aOldLen; |
260 | 0 | if (mHdr->mLength == 0) { |
261 | 0 | ShrinkCapacity(aElemSize, aElemAlign); |
262 | 0 | } else { |
263 | 0 | // Maybe nothing needs to be shifted |
264 | 0 | if (num == 0) { |
265 | 0 | return; |
266 | 0 | } |
267 | 0 | // Perform shift (change units to bytes first) |
268 | 0 | aStart *= aElemSize; |
269 | 0 | aNewLen *= aElemSize; |
270 | 0 | aOldLen *= aElemSize; |
271 | 0 | char* baseAddr = reinterpret_cast<char*>(mHdr + 1) + aStart; |
272 | 0 | Copy::MoveOverlappingRegion(baseAddr + aNewLen, baseAddr + aOldLen, num, aElemSize); |
273 | 0 | } |
274 | 0 | } Unexecuted instantiation: void nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<mozilla::dom::ipc::StructuredCloneData> >::ShiftData<nsTArrayInfallibleAllocator>(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long) Unexecuted instantiation: void nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<RefPtr<nsDOMMutationObserver>, 4ul> > >::ShiftData<nsTArrayInfallibleAllocator>(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long) Unexecuted instantiation: void nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithConstructors<AutoTArray<nsINode*, 8ul> > >::ShiftData<nsTArrayInfallibleAllocator>(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long) |
275 | | |
276 | | template<class Alloc, class Copy> |
277 | | template<typename ActualAlloc> |
278 | | void |
279 | | nsTArray_base<Alloc, Copy>::SwapFromEnd(index_type aStart, |
280 | | size_type aCount, |
281 | | size_type aElemSize, |
282 | | size_t aElemAlign) |
283 | | { |
284 | | // This method is part of the implementation of |
285 | | // nsTArray::SwapRemoveElement{s,}At. For more information, read the |
286 | | // documentation on that method. |
287 | | if (aCount == 0) { |
288 | | return; |
289 | | } |
290 | | |
291 | | // We are going to be removing aCount elements. Update our length to point to |
292 | | // the new end of the array. |
293 | | size_type oldLength = mHdr->mLength; |
294 | | mHdr->mLength -= aCount; |
295 | | |
296 | | if (mHdr->mLength == 0) { |
297 | | // If we have no elements remaining in the array, we can free our buffer. |
298 | | ShrinkCapacity(aElemSize, aElemAlign); |
299 | | return; |
300 | | } |
301 | | |
302 | | // Determine how many elements we need to move from the end of the array into |
303 | | // the now-removed section. This will either be the number of elements which |
304 | | // were removed (if there are more elements in the tail of the array), or the |
305 | | // entire tail of the array, whichever is smaller. |
306 | | size_type relocCount = std::min(aCount, mHdr->mLength - aStart); |
307 | | if (relocCount == 0) { |
308 | | return; |
309 | | } |
310 | | |
311 | | // Move the elements which are now stranded after the end of the array back |
312 | | // into the now-vacated memory. |
313 | | index_type sourceBytes = (oldLength - relocCount) * aElemSize; |
314 | | index_type destBytes = aStart * aElemSize; |
315 | | |
316 | | // Perform the final copy. This is guaranteed to be a non-overlapping copy |
317 | | // as our source contains only still-valid entries, and the destination |
318 | | // contains only invalid entries which need to be overwritten. |
319 | | MOZ_ASSERT(sourceBytes >= destBytes, |
320 | | "The source should be after the destination."); |
321 | | MOZ_ASSERT(sourceBytes - destBytes >= relocCount * aElemSize, |
322 | | "The range should be nonoverlapping"); |
323 | | |
324 | | char* baseAddr = reinterpret_cast<char*>(mHdr + 1); |
325 | | Copy::MoveNonOverlappingRegion(baseAddr + destBytes, |
326 | | baseAddr + sourceBytes, |
327 | | relocCount, |
328 | | aElemSize); |
329 | | } |
330 | | |
331 | | template<class Alloc, class Copy> |
332 | | template<typename ActualAlloc> |
333 | | bool |
334 | | nsTArray_base<Alloc, Copy>::InsertSlotsAt(index_type aIndex, size_type aCount, |
335 | | size_type aElemSize, |
336 | | size_t aElemAlign) |
337 | 33 | { |
338 | 33 | if (MOZ_UNLIKELY(aIndex > Length())) { |
339 | 0 | InvalidArrayIndex_CRASH(aIndex, Length()); |
340 | 0 | } |
341 | 33 | |
342 | 33 | size_type newLen = Length() + aCount; |
343 | 33 | |
344 | 33 | EnsureCapacity<ActualAlloc>(newLen, aElemSize); |
345 | 33 | |
346 | 33 | // Check for out of memory conditions |
347 | 33 | if (Capacity() < newLen) { |
348 | 0 | return false; |
349 | 0 | } |
350 | 33 | |
351 | 33 | // Move the existing elements as needed. Note that this will |
352 | 33 | // change our mLength, so no need to call IncrementLength. |
353 | 33 | ShiftData<ActualAlloc>(aIndex, 0, aCount, aElemSize, aElemAlign); |
354 | 33 | |
355 | 33 | return true; |
356 | 33 | } |
357 | | |
358 | | // nsTArray_base::IsAutoArrayRestorer is an RAII class which takes |
359 | | // |nsTArray_base &array| in its constructor. When it's destructed, it ensures |
360 | | // that |
361 | | // |
362 | | // * array.mIsAutoArray has the same value as it did when we started, and |
363 | | // * if array has an auto buffer and mHdr would otherwise point to |
364 | | // sEmptyTArrayHeader, array.mHdr points to array's auto buffer. |
365 | | |
366 | | template<class Alloc, class Copy> |
367 | | nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::IsAutoArrayRestorer( |
368 | | nsTArray_base<Alloc, Copy>& aArray, |
369 | | size_t aElemAlign) |
370 | | : mArray(aArray) |
371 | | , mElemAlign(aElemAlign) |
372 | | , mIsAuto(aArray.IsAutoArray()) |
373 | 0 | { |
374 | 0 | } |
375 | | |
376 | | template<class Alloc, class Copy> |
377 | | nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::~IsAutoArrayRestorer() |
378 | 0 | { |
379 | 0 | // Careful: We don't want to set mIsAutoArray = 1 on sEmptyTArrayHeader. |
380 | 0 | if (mIsAuto && mArray.mHdr == mArray.EmptyHdr()) { |
381 | 0 | // Call GetAutoArrayBufferUnsafe() because GetAutoArrayBuffer() asserts |
382 | 0 | // that mHdr->mIsAutoArray is true, which surely isn't the case here. |
383 | 0 | mArray.mHdr = mArray.GetAutoArrayBufferUnsafe(mElemAlign); |
384 | 0 | mArray.mHdr->mLength = 0; |
385 | 0 | } else if (mArray.mHdr != mArray.EmptyHdr()) { |
386 | 0 | mArray.mHdr->mIsAutoArray = mIsAuto; |
387 | 0 | } |
388 | 0 | } |
389 | | |
390 | | template<class Alloc, class Copy> |
391 | | template<typename ActualAlloc, class Allocator> |
392 | | typename ActualAlloc::ResultTypeProxy |
393 | | nsTArray_base<Alloc, Copy>::SwapArrayElements(nsTArray_base<Allocator, |
394 | | Copy>& aOther, |
395 | | size_type aElemSize, |
396 | | size_t aElemAlign) |
397 | 0 | { |
398 | 0 |
|
399 | 0 | // EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyTArrayHeader even if we |
400 | 0 | // have an auto buffer. We need to point mHdr back to our auto buffer before |
401 | 0 | // we return, otherwise we'll forget that we have an auto buffer at all! |
402 | 0 | // IsAutoArrayRestorer takes care of this for us. |
403 | 0 |
|
404 | 0 | IsAutoArrayRestorer ourAutoRestorer(*this, aElemAlign); |
405 | 0 | typename nsTArray_base<Allocator, Copy>::IsAutoArrayRestorer |
406 | 0 | otherAutoRestorer(aOther, aElemAlign); |
407 | 0 |
|
408 | 0 | // If neither array uses an auto buffer which is big enough to store the |
409 | 0 | // other array's elements, then ensure that both arrays use malloc'ed storage |
410 | 0 | // and swap their mHdr pointers. |
411 | 0 | if ((!UsesAutoArrayBuffer() || Capacity() < aOther.Length()) && |
412 | 0 | (!aOther.UsesAutoArrayBuffer() || aOther.Capacity() < Length())) { |
413 | 0 |
|
414 | 0 | if (!EnsureNotUsingAutoArrayBuffer<ActualAlloc>(aElemSize) || |
415 | 0 | !aOther.template EnsureNotUsingAutoArrayBuffer<ActualAlloc>(aElemSize)) { |
416 | 0 | return ActualAlloc::FailureResult(); |
417 | 0 | } |
418 | 0 | |
419 | 0 | Header* temp = mHdr; |
420 | 0 | mHdr = aOther.mHdr; |
421 | 0 | aOther.mHdr = temp; |
422 | 0 |
|
423 | 0 | return ActualAlloc::SuccessResult(); |
424 | 0 | } |
425 | 0 | |
426 | 0 | // Swap the two arrays by copying, since at least one is using an auto |
427 | 0 | // buffer which is large enough to hold all of the aOther's elements. We'll |
428 | 0 | // copy the shorter array into temporary storage. |
429 | 0 | // |
430 | 0 | // (We could do better than this in some circumstances. Suppose we're |
431 | 0 | // swapping arrays X and Y. X has space for 2 elements in its auto buffer, |
432 | 0 | // but currently has length 4, so it's using malloc'ed storage. Y has length |
433 | 0 | // 2. When we swap X and Y, we don't need to use a temporary buffer; we can |
434 | 0 | // write Y straight into X's auto buffer, write X's malloc'ed buffer on top |
435 | 0 | // of Y, and then switch X to using its auto buffer.) |
436 | 0 | |
437 | 0 | if (!ActualAlloc::Successful(EnsureCapacity<ActualAlloc>(aOther.Length(), aElemSize)) || |
438 | 0 | !Allocator::Successful(aOther.template EnsureCapacity<Allocator>(Length(), aElemSize))) { |
439 | 0 | return ActualAlloc::FailureResult(); |
440 | 0 | } |
441 | 0 | |
442 | 0 | // The EnsureCapacity calls above shouldn't have caused *both* arrays to |
443 | 0 | // switch from their auto buffers to malloc'ed space. |
444 | 0 | MOZ_ASSERT(UsesAutoArrayBuffer() || aOther.UsesAutoArrayBuffer(), |
445 | 0 | "One of the arrays should be using its auto buffer."); |
446 | 0 |
|
447 | 0 | size_type smallerLength = XPCOM_MIN(Length(), aOther.Length()); |
448 | 0 | size_type largerLength = XPCOM_MAX(Length(), aOther.Length()); |
449 | 0 | void* smallerElements; |
450 | 0 | void* largerElements; |
451 | 0 | if (Length() <= aOther.Length()) { |
452 | 0 | smallerElements = Hdr() + 1; |
453 | 0 | largerElements = aOther.Hdr() + 1; |
454 | 0 | } else { |
455 | 0 | smallerElements = aOther.Hdr() + 1; |
456 | 0 | largerElements = Hdr() + 1; |
457 | 0 | } |
458 | 0 |
|
459 | 0 | // Allocate temporary storage for the smaller of the two arrays. We want to |
460 | 0 | // allocate this space on the stack, if it's not too large. Sounds like a |
461 | 0 | // job for AutoTArray! (One of the two arrays we're swapping is using an |
462 | 0 | // auto buffer, so we're likely not allocating a lot of space here. But one |
463 | 0 | // could, in theory, allocate a huge AutoTArray on the heap.) |
464 | 0 | AutoTArray<uint8_t, 64 * sizeof(void*)> temp; |
465 | 0 | if (!ActualAlloc::Successful(temp.template EnsureCapacity<ActualAlloc>(smallerLength * aElemSize, sizeof(uint8_t)))) { |
466 | 0 | return ActualAlloc::FailureResult(); |
467 | 0 | } |
468 | 0 | |
469 | 0 | Copy::MoveNonOverlappingRegion(temp.Elements(), smallerElements, smallerLength, aElemSize); |
470 | 0 | Copy::MoveNonOverlappingRegion(smallerElements, largerElements, largerLength, aElemSize); |
471 | 0 | Copy::MoveNonOverlappingRegion(largerElements, temp.Elements(), smallerLength, aElemSize); |
472 | 0 |
|
473 | 0 | // Swap the arrays' lengths. |
474 | 0 | MOZ_ASSERT((aOther.Length() == 0 || mHdr != EmptyHdr()) && |
475 | 0 | (Length() == 0 || aOther.mHdr != EmptyHdr()), |
476 | 0 | "Don't set sEmptyTArrayHeader's length."); |
477 | 0 | size_type tempLength = Length(); |
478 | 0 |
|
479 | 0 | // Avoid writing to EmptyHdr, since it can trigger false |
480 | 0 | // positives with TSan. |
481 | 0 | if (mHdr != EmptyHdr()) { |
482 | 0 | mHdr->mLength = aOther.Length(); |
483 | 0 | } |
484 | 0 | if (aOther.mHdr != EmptyHdr()) { |
485 | 0 | aOther.mHdr->mLength = tempLength; |
486 | 0 | } |
487 | 0 |
|
488 | 0 | return ActualAlloc::SuccessResult(); |
489 | 0 | } |
490 | | |
491 | | template<class Alloc, class Copy> |
492 | | template<typename ActualAlloc> |
493 | | bool |
494 | | nsTArray_base<Alloc, Copy>::EnsureNotUsingAutoArrayBuffer(size_type aElemSize) |
495 | 0 | { |
496 | 0 | if (UsesAutoArrayBuffer()) { |
497 | 0 |
|
498 | 0 | // If you call this on a 0-length array, we'll set that array's mHdr to |
499 | 0 | // sEmptyTArrayHeader, in flagrant violation of the AutoTArray invariants. |
500 | 0 | // It's up to you to set it back! (If you don't, the AutoTArray will |
501 | 0 | // forget that it has an auto buffer.) |
502 | 0 | if (Length() == 0) { |
503 | 0 | mHdr = EmptyHdr(); |
504 | 0 | return true; |
505 | 0 | } |
506 | 0 | |
507 | 0 | size_type size = sizeof(Header) + Length() * aElemSize; |
508 | 0 |
|
509 | 0 | Header* header = static_cast<Header*>(ActualAlloc::Malloc(size)); |
510 | 0 | if (!header) { |
511 | 0 | return false; |
512 | 0 | } |
513 | 0 | |
514 | 0 | Copy::MoveNonOverlappingRegionWithHeader(header, mHdr, Length(), aElemSize); |
515 | 0 | header->mCapacity = Length(); |
516 | 0 | mHdr = header; |
517 | 0 | } |
518 | 0 |
|
519 | 0 | return true; |
520 | 0 | } |