SparseArraySegment.inl 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. namespace Js
  7. {
  8. template<typename T>
  9. uint32 SparseArraySegment<T>::GetAlignedSize(uint32 size)
  10. {
  11. return (uint32)(HeapInfo::GetAlignedSize(UInt32Math::MulAdd<sizeof(T), sizeof(SparseArraySegmentBase)>(size)) - sizeof(SparseArraySegmentBase)) / sizeof(T);
  12. }
  13. template<typename T>
  14. template<bool isLeaf>
  15. SparseArraySegment<T> * SparseArraySegment<T>::Allocate(Recycler* recycler, uint32 left, uint32 length, uint32 size, uint32 fillStart /*= 0*/)
  16. {
  17. Assert(length <= size);
  18. Assert(size <= JavascriptArray::MaxArrayLength - left);
  19. uint32 bufferSize = UInt32Math::Mul<sizeof(T)>(size);
  20. SparseArraySegment<T> *seg =
  21. isLeaf ?
  22. RecyclerNewPlusLeafZ(recycler, bufferSize, SparseArraySegment<T>, left, length, size) :
  23. RecyclerNewPlusZ(recycler, bufferSize, SparseArraySegment<T>, left, length, size);
  24. seg->FillSegmentBuffer(fillStart, size);
  25. return seg;
  26. }
  27. template<>
  28. inline SparseArraySegment<int> *SparseArraySegment<int>::AllocateLiteralHeadSegment(
  29. Recycler *const recycler,
  30. const uint32 length)
  31. {
  32. if (DoNativeArrayLeafSegment())
  33. {
  34. return SparseArraySegment<int>::AllocateLiteralHeadSegmentImpl<true>(recycler, length);
  35. }
  36. return SparseArraySegment<int>::AllocateLiteralHeadSegmentImpl<false>(recycler, length);
  37. }
  38. template<>
  39. inline SparseArraySegment<double> *SparseArraySegment<double>::AllocateLiteralHeadSegment(
  40. Recycler *const recycler,
  41. const uint32 length)
  42. {
  43. if (DoNativeArrayLeafSegment())
  44. {
  45. return SparseArraySegment<double>::AllocateLiteralHeadSegmentImpl<true>(recycler, length);
  46. }
  47. return SparseArraySegment<double>::AllocateLiteralHeadSegmentImpl<false>(recycler, length);
  48. }
  49. template<typename T>
  50. SparseArraySegment<T> *SparseArraySegment<T>::AllocateLiteralHeadSegment(
  51. Recycler *const recycler,
  52. const uint32 length)
  53. {
  54. return SparseArraySegment<T>::AllocateLiteralHeadSegmentImpl<false>(recycler, length);
  55. }
  56. template<typename T>
  57. template<bool isLeaf>
  58. SparseArraySegment<T> *SparseArraySegment<T>::AllocateLiteralHeadSegmentImpl(
  59. Recycler *const recycler,
  60. const uint32 length)
  61. {
  62. Assert(length != 0);
  63. const uint32 size = GetAlignedSize(length);
  64. return SparseArraySegment<T>::Allocate<isLeaf>(recycler, 0, length, size, length);
  65. }
  66. template<>
  67. inline SparseArraySegment<int> * SparseArraySegment<int>::AllocateSegment(Recycler* recycler, uint32 left, uint32 length, SparseArraySegmentBase *nextSeg)
  68. {
  69. if (DoNativeArrayLeafSegment() && nextSeg == nullptr)
  70. {
  71. return AllocateSegmentImpl<true>(recycler, left, length, nextSeg);
  72. }
  73. return AllocateSegmentImpl<false>(recycler, left, length, nextSeg);
  74. }
  75. template<>
  76. inline SparseArraySegment<double> * SparseArraySegment<double>::AllocateSegment(Recycler* recycler, uint32 left, uint32 length, SparseArraySegmentBase *nextSeg)
  77. {
  78. if (DoNativeArrayLeafSegment() && nextSeg == nullptr)
  79. {
  80. return AllocateSegmentImpl<true>(recycler, left, length, nextSeg);
  81. }
  82. return AllocateSegmentImpl<false>(recycler, left, length, nextSeg);
  83. }
  84. template<typename T>
  85. SparseArraySegment<T> * SparseArraySegment<T>::AllocateSegment(Recycler* recycler, uint32 left, uint32 length, SparseArraySegmentBase *nextSeg)
  86. {
  87. return AllocateSegmentImpl<false>(recycler, left, length, nextSeg);
  88. }
  89. template<typename T>
  90. template<bool isLeaf>
  91. SparseArraySegment<T> * SparseArraySegment<T>::AllocateSegmentImpl(Recycler* recycler, uint32 left, uint32 length, SparseArraySegmentBase *nextSeg)
  92. {
  93. Assert(!isLeaf || nextSeg == nullptr);
  94. uint32 size;
  95. if ((length <= CHUNK_SIZE) && (left < BigLeft))
  96. {
  97. size = GetAlignedSize(CHUNK_SIZE);
  98. }
  99. else
  100. {
  101. size = GetAlignedSize(length);
  102. }
  103. //But don't overshoot next segment
  104. EnsureSizeInBound(left, length, size, nextSeg);
  105. return SparseArraySegment<T>::Allocate<isLeaf>(recycler, left, length, size);
  106. }
  107. template<>
  108. inline SparseArraySegment<int> * SparseArraySegment<int>::AllocateSegment(Recycler* recycler, uint32 left, uint32 length, uint32 size, SparseArraySegmentBase *nextSeg)
  109. {
  110. if (DoNativeArrayLeafSegment() && nextSeg == nullptr)
  111. {
  112. return AllocateSegmentImpl<true>(recycler, left, length, size, nextSeg);
  113. }
  114. return AllocateSegmentImpl<false>(recycler, left, length, size, nextSeg);
  115. }
  116. template<>
  117. inline SparseArraySegment<double> * SparseArraySegment<double>::AllocateSegment(Recycler* recycler, uint32 left, uint32 length, uint32 size, SparseArraySegmentBase *nextSeg)
  118. {
  119. if (DoNativeArrayLeafSegment() && nextSeg == nullptr)
  120. {
  121. return AllocateSegmentImpl<true>(recycler, left, length, size, nextSeg);
  122. }
  123. return AllocateSegmentImpl<false>(recycler, left, length, size, nextSeg);
  124. }
  125. template<typename T>
  126. inline SparseArraySegment<T> * SparseArraySegment<T>::AllocateSegment(Recycler* recycler, uint32 left, uint32 length, uint32 size, SparseArraySegmentBase *nextSeg)
  127. {
  128. return AllocateSegmentImpl<false>(recycler, left, length, size, nextSeg);
  129. }
  130. template<typename T>
  131. template<bool isLeaf>
  132. SparseArraySegment<T> * SparseArraySegment<T>::AllocateSegmentImpl(Recycler* recycler, uint32 left, uint32 length, uint32 size, SparseArraySegmentBase *nextSeg)
  133. {
  134. Assert(!isLeaf || nextSeg == nullptr);
  135. AssertMsg(size > 0, "size too small");
  136. if ((size <= CHUNK_SIZE) && (size < BigLeft))
  137. {
  138. size = GetAlignedSize(CHUNK_SIZE);
  139. }
  140. else
  141. {
  142. size = GetAlignedSize(size);
  143. }
  144. //But don't overshoot next segment
  145. EnsureSizeInBound(left, length, size, nextSeg);
  146. return SparseArraySegment<T>::Allocate<isLeaf>(recycler, left, length, size);
  147. }
  148. template<>
  149. inline SparseArraySegment<int>* SparseArraySegment<int>::AllocateSegment(Recycler* recycler, SparseArraySegmentBase* prev, uint32 index)
  150. {
  151. if (DoNativeArrayLeafSegment() && prev->next == nullptr)
  152. {
  153. return AllocateSegmentImpl<true>(recycler, prev, index);
  154. }
  155. return AllocateSegmentImpl<false>(recycler, prev, index);
  156. }
  157. template<>
  158. inline SparseArraySegment<double>* SparseArraySegment<double>::AllocateSegment(Recycler* recycler, SparseArraySegmentBase* prev, uint32 index)
  159. {
  160. if (DoNativeArrayLeafSegment() && prev->next == nullptr)
  161. {
  162. return AllocateSegmentImpl<true>(recycler, prev, index);
  163. }
  164. return AllocateSegmentImpl<false>(recycler, prev, index);
  165. }
  166. template<typename T>
  167. SparseArraySegment<T>* SparseArraySegment<T>::AllocateSegment(Recycler* recycler, SparseArraySegmentBase* prev, uint32 index)
  168. {
  169. return AllocateSegmentImpl<false>(recycler, prev, index);
  170. }
  171. // Allocate a segment in between (prev, next) to contain an index
  172. template<typename T>
  173. template<bool isLeaf>
  174. SparseArraySegment<T>* SparseArraySegment<T>::AllocateSegmentImpl(Recycler* recycler, SparseArraySegmentBase* prev, uint32 index)
  175. {
  176. Assert(prev);
  177. Assert(index > prev->left && index - prev->left >= prev->size);
  178. SparseArraySegmentBase* next = prev->next;
  179. Assert(!next || index < next->left);
  180. Assert(!next || !isLeaf);
  181. uint32 left = index;
  182. uint32 size = (left < BigLeft ? GetAlignedSize(CHUNK_SIZE) : GetAlignedSize(1));
  183. // Try to move the segment leftwards if it overshoots next segment
  184. if (next && size > next->left - left)
  185. {
  186. size = min(size, next->left - (prev->left + prev->size));
  187. left = next->left - size;
  188. }
  189. Assert(index >= left && index - left < size);
  190. uint32 length = index - left + 1;
  191. EnsureSizeInBound(left, length, size, next);
  192. return SparseArraySegment<T>::Allocate<isLeaf>(recycler, left, length, size);
  193. }
  194. template<typename T>
  195. void SparseArraySegment<T>::FillSegmentBuffer(uint32 start, uint32 size)
  196. {
  197. // Fill the segment buffer using gp-register-sized stores. Avoid using the FPU for the sake
  198. // of perf (especially x86).
  199. Var fill = JavascriptArray::MissingItem;
  200. if (sizeof(Var) > sizeof(T))
  201. {
  202. // Pointer size is greater than the element (int32 buffer on x64).
  203. // Fill as much as we can and do one int32-sized store at the end if necessary.
  204. uint32 i, step = sizeof(Var) / sizeof(T);
  205. if (start & 1)
  206. {
  207. Assert(sizeof(T) == sizeof(int32));
  208. ((int32*)(this->elements))[start] = JavascriptNativeIntArray::MissingItem;
  209. }
  210. for (i = (start + step-1)/step; i < (size/step); i++)
  211. {
  212. ((Var*)(this->elements))[i] = fill;
  213. }
  214. if ((i *= step) < size)
  215. {
  216. Assert(sizeof(T) == sizeof(int32));
  217. ((int32*)(this->elements))[i] = JavascriptNativeIntArray::MissingItem;
  218. }
  219. }
  220. else
  221. {
  222. // Pointer size <= element size. Fill with pointer-sized stores.
  223. Assert(sizeof(T) % sizeof(Var) == 0);
  224. uint step = sizeof(T) / sizeof(Var);
  225. for (uint i = start; i < size * step; i++)
  226. {
  227. ((Var*)(this->elements))[i] = fill;
  228. }
  229. }
  230. }
  231. template<typename T>
  232. void SparseArraySegment<T>::SetElement(Recycler *recycler, uint32 index, T value)
  233. {
  234. AssertMsg(index >= left && index - left < size, "Index out of range");
  235. uint32 offset = index - left;
  236. elements[offset] = value;
  237. if ((offset + 1) > length)
  238. {
  239. length = offset + 1;
  240. }
  241. Assert(length <= size);
  242. Assert(left + length > left);
  243. }
  244. template<typename T>
  245. SparseArraySegment<T> *SparseArraySegment<T>::SetElementGrow(Recycler *recycler, SparseArraySegmentBase* prev, uint32 index, T value)
  246. {
  247. AssertMsg((index + 1) == left || index == (left + size), "Index out of range");
  248. uint32 offset = index - left;
  249. SparseArraySegment<T> *current = this;
  250. if (index + 1 == left)
  251. {
  252. Assert(prev && prev->next == current);
  253. Assert(left > prev->left && left - prev->left > prev->size);
  254. Assert(left - prev->left - prev->size > 1); // Otherwise we would be growing/merging prev
  255. // Grow front up to (prev->left + prev->size + 1), so that setting (prev->left + prev->size)
  256. // later would trigger segment merging.
  257. current = GrowFrontByMax(recycler, left - prev->left - prev->size - 1);
  258. current->SetElement(recycler, index, value);
  259. }
  260. else if (offset == size)
  261. {
  262. if (next == nullptr)
  263. {
  264. current = GrowByMin(recycler, offset + 1 - size);
  265. }
  266. else
  267. {
  268. current = GrowByMinMax(recycler, offset + 1 - size, next->left - left - size);
  269. }
  270. current->elements[offset] = value;
  271. current->length = offset + 1;
  272. }
  273. else
  274. {
  275. AssertMsg(false, "Invalid call to SetElementGrow");
  276. }
  277. return current;
  278. }
  279. template<typename T>
  280. T SparseArraySegment<T>::GetElement(uint32 index)
  281. {
  282. AssertMsg(index >= left && index <= left + length - 1, "Index is out of the segment range");
  283. return elements[index - left];
  284. }
  285. // This is a very inefficient function, we have to move element
  286. template<typename T>
  287. void SparseArraySegment<T>::RemoveElement(Recycler *recycler, uint32 index)
  288. {
  289. AssertMsg(index >= left && index < left + length, "Index is out of the segment range");
  290. if (index + 1 < left + length)
  291. {
  292. MoveArray(elements + index - left, elements + index + 1 - left, length - (index - left) - 1);
  293. }
  294. Assert(length);
  295. length--;
  296. elements[length] = SparseArraySegment<T>::GetMissingItem();
  297. }
  298. template<typename T>
  299. SparseArraySegment<T>* SparseArraySegment<T>::CopySegment(Recycler *recycler, SparseArraySegment<T>* dst, uint32 dstIndex, SparseArraySegment<T>* src, uint32 srcIndex, uint32 inputLen)
  300. {
  301. AssertMsg(src != nullptr && dst != nullptr, "Null input!");
  302. uint32 newLen = dstIndex - dst->left + inputLen;
  303. if (newLen > dst->size)
  304. {
  305. dst = dst->GrowBy(recycler, newLen - dst->size);
  306. }
  307. dst->length = newLen;
  308. Assert(dst->length <= dst->size);
  309. AssertMsg(srcIndex >= src->left,"src->left > srcIndex resulting in negative indexing of src->elements");
  310. CopyArray(dst->elements + dstIndex - dst->left, inputLen, src->elements + srcIndex - src->left, inputLen);
  311. return dst;
  312. }
  313. template<typename T>
  314. uint32 SparseArraySegment<T>::GetGrowByFactor()
  315. {
  316. if (size < CHUNK_SIZE/2)
  317. {
  318. return (GetAlignedSize(size * 4) - size);
  319. }
  320. else if (size < 1024)
  321. {
  322. return (GetAlignedSize(size * 2) - size);
  323. }
  324. return (GetAlignedSize(UInt32Math::Mul(size, 5) / 3) - size);
  325. }
  326. template<typename T>
  327. SparseArraySegment<T>* SparseArraySegment<T>::GrowByMin(Recycler *recycler, uint32 minValue)
  328. {
  329. Assert(size <= JavascriptArray::MaxArrayLength - left);
  330. uint32 maxGrow = JavascriptArray::MaxArrayLength - (left + size);
  331. return GrowByMinMax(recycler, minValue, maxGrow);
  332. }
  333. template<typename T>
  334. SparseArraySegment<T>* SparseArraySegment<T>::GrowByMinMax(Recycler *recycler, uint32 minValue, uint32 maxValue)
  335. {
  336. Assert(size <= JavascriptArray::MaxArrayLength - left);
  337. Assert(maxValue <= JavascriptArray::MaxArrayLength - (left + size));
  338. AssertMsg(minValue <= maxValue, "Invalid values to GrowByMinMax");
  339. return GrowBy(recycler, max(minValue,min(maxValue, GetGrowByFactor())));
  340. }
  341. template<>
  342. inline SparseArraySegment<int>* SparseArraySegment<int>::GrowBy(Recycler *recycler, uint32 n)
  343. {
  344. if (!DoNativeArrayLeafSegment() || this->next != nullptr)
  345. {
  346. return GrowByImpl<false>(recycler, n);
  347. }
  348. return GrowByImpl<true>(recycler, n);
  349. }
  350. template<>
  351. inline SparseArraySegment<double>* SparseArraySegment<double>::GrowBy(Recycler *recycler, uint32 n)
  352. {
  353. if (!DoNativeArrayLeafSegment() || this->next != nullptr)
  354. {
  355. return GrowByImpl<false>(recycler, n);
  356. }
  357. return GrowByImpl<true>(recycler, n);
  358. }
  359. template<typename T>
  360. SparseArraySegment<T>* SparseArraySegment<T>::GrowBy(Recycler *recycler, uint32 n)
  361. {
  362. return GrowByImpl<false>(recycler, n);
  363. }
  364. template<typename T>
  365. template<bool isLeaf>
  366. SparseArraySegment<T>* SparseArraySegment<T>::GrowByImpl(Recycler *recycler, uint32 n)
  367. {
  368. Assert(length <= size);
  369. Assert(n != 0);
  370. uint32 newSize = size + n;
  371. if (newSize <= size)
  372. {
  373. Throw::OutOfMemory();
  374. }
  375. SparseArraySegment<T> *newSeg = Allocate<isLeaf>(recycler, left, length, newSize);
  376. newSeg->next = this->next;
  377. // (sizeof(T) * newSize) will throw OOM in Allocate if it overflows.
  378. CopyArray(newSeg->elements, newSize, this->elements, length);
  379. return newSeg;
  380. }
  381. //
  382. // Grows segment in the front. Note that the result new segment's left is changed.
  383. //
  384. template<>
  385. inline SparseArraySegment<int>* SparseArraySegment<int>::GrowFrontByMax(Recycler *recycler, uint32 n)
  386. {
  387. if (DoNativeArrayLeafSegment() && this->next == nullptr)
  388. {
  389. return GrowFrontByMaxImpl<true>(recycler, n);
  390. }
  391. return GrowFrontByMaxImpl<false>(recycler, n);
  392. }
  393. template<>
  394. inline SparseArraySegment<double>* SparseArraySegment<double>::GrowFrontByMax(Recycler *recycler, uint32 n)
  395. {
  396. if (DoNativeArrayLeafSegment() && this->next == nullptr)
  397. {
  398. return GrowFrontByMaxImpl<true>(recycler, n);
  399. }
  400. return GrowFrontByMaxImpl<false>(recycler, n);
  401. }
  402. template<typename T>
  403. SparseArraySegment<T>* SparseArraySegment<T>::GrowFrontByMax(Recycler *recycler, uint32 n)
  404. {
  405. return GrowFrontByMaxImpl<false>(recycler, n);
  406. }
  407. template<typename T>
  408. template<bool isLeaf>
  409. SparseArraySegment<T>* SparseArraySegment<T>::GrowFrontByMaxImpl(Recycler *recycler, uint32 n)
  410. {
  411. Assert(length <= size);
  412. Assert(n > 0);
  413. Assert(n <= left);
  414. Assert(size + n > size);
  415. n = min(n, GetGrowByFactor());
  416. if (size + n <= size)
  417. {
  418. Throw::OutOfMemory();
  419. }
  420. SparseArraySegment<T> *newSeg = Allocate<isLeaf>(recycler, left - n, length + n, size + n);
  421. newSeg->next = this->next;
  422. CopyArray(newSeg->elements + n, length, this->elements, length);
  423. return newSeg;
  424. }
  425. template<typename T>
  426. void SparseArraySegment<T>::ClearElements(__out_ecount(len) Field(T)* elements, uint32 len)
  427. {
  428. T fill = SparseArraySegment<T>::GetMissingItem();
  429. for (uint i = 0; i < len; i++)
  430. {
  431. elements[i] = fill;
  432. }
  433. }
  434. template<typename T>
  435. void SparseArraySegment<T>::Truncate(uint32 index)
  436. {
  437. AssertMsg(index >= left && (index - left) < size, "Index out of range");
  438. ClearElements(elements + (index - left), size - (index - left));
  439. if (index - left < length)
  440. {
  441. length = index - left;
  442. }
  443. Assert(length <= size);
  444. }
  445. template<typename T>
  446. void SparseArraySegment<T>::ReverseSegment(Recycler *recycler)
  447. {
  448. if (length <= 1)
  449. {
  450. return;
  451. }
  452. T temp;
  453. uint32 lower = 0;
  454. uint32 upper = length - 1;
  455. while (lower < upper)
  456. {
  457. temp = elements[lower];
  458. elements[lower] = elements[upper];
  459. elements[upper] = temp;
  460. ++lower;
  461. --upper;
  462. }
  463. }
  464. }