nsTArray-inl.h 17 KB

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