ImmutableList.h 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  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. #define IfNullThrowOutOfMemory(result) if(result == nullptr) { Js::Throw::OutOfMemory(); }
  7. namespace regex
  8. {
  9. template<class T>
  10. class ImmutableList
  11. {
  12. T value;
  13. ImmutableList<T> * next;
  14. ImmutableList(T value, ImmutableList<T> * next)
  15. : value(value), next(next)
  16. { }
  17. public:
  18. // Delete all the nodes in the list (if any).
  19. void FreeList(ArenaAllocator * a)
  20. {
  21. if (this == nullptr)
  22. {
  23. return;
  24. }
  25. auto nextToDelete = this->next;
  26. Adelete(a, this);
  27. nextToDelete->FreeList(a);
  28. }
  29. // Info: Return a list with the given value prepended to this one.
  30. // Parameters: value - the value to prepend
  31. ImmutableList<T> * Prepend(T value, ArenaAllocator * a)
  32. {
  33. return Anew(a, ImmutableList, value, this);
  34. }
  35. // Info: Return a list with the given value appended to this one <Note this modifies existing list>
  36. // It is required that the tail passed is empty if this is empty list otherwise its the last node of this list
  37. // Parameters: value - the value to append
  38. // tail - last node of the new list
  39. ImmutableList<T> * Append(T value, ArenaAllocator * a, ImmutableList<T> **tail)
  40. {
  41. #if DBG
  42. Assert(tail != nullptr);
  43. Assert((this->IsEmpty() && (*tail)->IsEmpty()) || (*tail)->next->IsEmpty());
  44. if (!this->IsEmpty())
  45. {
  46. auto current = this;
  47. while(!current->next->IsEmpty())
  48. {
  49. current = current->next;
  50. }
  51. Assert(current == (*tail));
  52. }
  53. #endif
  54. if (IsEmpty())
  55. {
  56. *tail = Anew(a, ImmutableList, value, ImmutableList<T>::Empty());
  57. return *tail;
  58. }
  59. (*tail)->next = Anew(a, ImmutableList, value, ImmutableList<T>::Empty());
  60. *tail = (*tail)->next;
  61. return this;
  62. }
  63. // Info: Return a list with the given array prepended to this one.
  64. // Parameters: arr - the array to prepend
  65. // count - the elements in the array
  66. ImmutableList<T> * PrependArray(T * arr, size_t count, ArenaAllocator * a)
  67. {
  68. Assert(count > 0);
  69. // Create the list
  70. // Using Append here instead of prepend so that we dont need to make another pass just to get the tail and attach the current list at the end of array list
  71. auto out = ImmutableList<T>::Empty();
  72. auto tail = out;
  73. out = out->AppendArrayToCurrentList(arr, count, a, &tail);
  74. Assert(!tail->IsEmpty());
  75. Assert(tail->next->IsEmpty());
  76. tail->next = this;
  77. return out;
  78. }
  79. // Info: Return the list which is resulted by appending array to the current list<Note this modifies existing list>
  80. // It is required that the tail passed is empty if this is empty list otherwise its the last node of this list
  81. // Parameters: arr - the array to append
  82. // count - the elements in the array
  83. // tail - last node of the new list
  84. ImmutableList<T> * AppendArrayToCurrentList(T * arr, size_t count, ArenaAllocator * a, ImmutableList<T> **tail)
  85. {
  86. auto out = this;
  87. for(size_t i=0; i<count; i++)
  88. {
  89. out = out->Append(arr[i],a,tail);
  90. }
  91. return out;
  92. }
  93. // Info: Return a list with the given list appended to this one <Note this modifies existing lists>
  94. // returns the list that looks like thisList -> list
  95. // Parameters: list - the list to append
  96. // tail - optional end ptr so we dont need to traverse the list to find the tail node
  97. ImmutableList<T> * AppendListToCurrentList(ImmutableList<T> * list, ImmutableList<T> * tail = ImmutableList<T>::Empty()) // TODO : figure out all the tail scenarios
  98. {
  99. if (list->IsEmpty())
  100. {
  101. return this;
  102. }
  103. auto out = this;
  104. if (out->IsEmpty())
  105. {
  106. return list;
  107. }
  108. if (tail->IsEmpty())
  109. {
  110. // We dont have tail node, find it
  111. tail = this;
  112. auto current = this;
  113. while(!current->IsEmpty())
  114. {
  115. tail = current;
  116. current = current->next;
  117. }
  118. Assert(tail->next->IsEmpty());
  119. }
  120. #if DBG
  121. else
  122. {
  123. auto current = this;
  124. while(!current->next->IsEmpty())
  125. {
  126. current = current->next;
  127. }
  128. Assert(current == tail);
  129. }
  130. #endif
  131. Assert(!tail->IsEmpty() && tail->next->IsEmpty());
  132. tail->next = list;
  133. return out;
  134. }
  135. // Info: Return a new list by calling function f called on each element of this list
  136. // Parameters: f - the mapping function
  137. template<class TOut, class F>
  138. ImmutableList<TOut> * Select(F f, ArenaAllocator * a)
  139. {
  140. auto out = ImmutableList<TOut>::Empty();
  141. auto tail = out;
  142. auto current = this;
  143. while(!current->IsEmpty())
  144. {
  145. out = out->Append(f(current->First()), a, &tail);
  146. current = current->next;
  147. }
  148. return out;
  149. }
  150. // Info: Changes the values of current list with the values returned by calling function f on each element <Note this modifies values in the list>
  151. // Parameters: f - the mapping function
  152. template<class F>
  153. ImmutableList<T> * SelectInPlace(F f)
  154. {
  155. auto current = this;
  156. while(!current->IsEmpty())
  157. {
  158. current->value = f(current->First());
  159. current = current->next;
  160. }
  161. return this;
  162. }
  163. // Info: Return true if any element returns true for the given predicate
  164. // Parameters: f - the predicate
  165. template<class F>
  166. bool Any(F f)
  167. {
  168. return WhereFirst(f).HasValue();
  169. }
  170. // Info: Return a new list by calling function f called on each element of this list. Remove any elements where f returned nullptr.
  171. // Parameters: f - the mapping function
  172. template<class TOut, class F>
  173. ImmutableList<TOut> * SelectNotNull(F f, ArenaAllocator * a)
  174. {
  175. auto out = ImmutableList<TOut>::Empty();
  176. auto tail = out;
  177. auto current = this;
  178. while(!current->IsEmpty())
  179. {
  180. auto result = f(current->First());
  181. if (result!=nullptr)
  182. {
  183. out = out->Append(result, a, &tail);
  184. }
  185. current = current->next;
  186. }
  187. return out;
  188. }
  189. // Info: Statically cast one list to another. The elements must be statically related
  190. template<class TOut>
  191. ImmutableList<TOut> * Cast()
  192. {
  193. #if DBG
  194. // Ensure static_cast is valid
  195. T t = T();
  196. TOut to = static_cast<TOut>(t);
  197. to;
  198. #endif
  199. return reinterpret_cast<ImmutableList<TOut>*>(this);
  200. }
  201. // Info: Call function f for each element of the list
  202. // Parameters: f - the function to call
  203. template<class F>
  204. void Iterate(F f)
  205. {
  206. auto current = this;
  207. while(!current->IsEmpty())
  208. {
  209. f(current->First());
  210. current = current->next;
  211. }
  212. }
  213. // Info: Call function f for each element of the list with an index for each
  214. // Parameters: f - the function to call
  215. // returns true to continue iterating, false to stop.
  216. template<class F>
  217. void IterateWhile(F f)
  218. {
  219. auto current = this;
  220. while(!current->IsEmpty())
  221. {
  222. bool shouldContinue = f(current->First());
  223. if (!shouldContinue)
  224. {
  225. break;
  226. }
  227. current = current->next;
  228. }
  229. }
  230. // Info: Call function f for each element of the list with an index for each
  231. // Parameters: f - the function to call
  232. template<class F>
  233. void IterateN(F f)
  234. {
  235. auto current = this;
  236. auto index = 0;
  237. while(!current->IsEmpty())
  238. {
  239. f(index, current->First());
  240. current = current->next;
  241. ++index;
  242. }
  243. }
  244. // Info: Call function f for first N elements of the list
  245. // Parameters: f - the function to call
  246. template<class F>
  247. void IterateFirstN(size_t N, F f)
  248. {
  249. Assert(Count() >= N);
  250. auto current = this;
  251. while(N > 0)
  252. {
  253. f(current->First());
  254. current = current->next;
  255. N--;
  256. }
  257. }
  258. // Info: Call function f for first N elements of the list, f2 on the next element and f3 for remaining elements
  259. // Parameters: N - number of elements to call f with
  260. // f - the function to call on first N elements
  261. // f2 - the function to call on N+1th element
  262. // f3 - the function to call on remaining elements
  263. template<class F, class F2, class F3>
  264. void IterateIn3Sets(size_t N, F f, F2 f2, F3 f3)
  265. {
  266. Assert(Count() > N);
  267. auto current = this;
  268. for (size_t i = 0; i < N; i++)
  269. {
  270. f(current->First());
  271. current = current->next;
  272. }
  273. Assert(current != nullptr);
  274. f2(current->First());
  275. current = current->next;
  276. while(current)
  277. {
  278. f3(current->First());
  279. current = current->next;
  280. }
  281. }
  282. // Info: Iterate two lists at once. Stop when either list runs out.
  283. // Parameters: f - the function to call on each pair
  284. template <class T2, class F> void IterateWith(ImmutableList<T2> *e2,F f)
  285. {
  286. auto en1 = this;
  287. auto en2 = e2;
  288. while(!en1->IsEmpty() && !en2->IsEmpty())
  289. {
  290. f(en1->First(),en2->First());
  291. en1 = en1->GetTail();
  292. en2 = en2->GetTail();
  293. }
  294. }
  295. // Info: Iterate over the elements of the enumerable and accumulate a result
  296. // Parameters: f - the function to call for each element
  297. template <class TAccumulator, class F> TAccumulator Accumulate(TAccumulator seed, F f)
  298. {
  299. auto current = this;
  300. auto accumulated = seed;
  301. while(!current->IsEmpty())
  302. {
  303. accumulated = f(accumulated, current->value);
  304. current = current->next;
  305. }
  306. return accumulated;
  307. }
  308. // Info: Sum the elements of the list using values returned from the given function
  309. // Parameters: f - the function to call for each element
  310. template <class TSize, class F> TSize Sum(F f)
  311. {
  312. TSize sum = TSize();
  313. auto current = this;
  314. while(!current->IsEmpty())
  315. {
  316. sum += f(current->value);
  317. current = current->next;
  318. }
  319. return sum;
  320. }
  321. // Info: Return true if f returns true for all elements
  322. // Parameters: f - the function to call for each element
  323. template <class F> bool TrueForAll(F f)
  324. {
  325. auto current = this;
  326. auto stillTrue = true;
  327. while(!current->IsEmpty() && stillTrue)
  328. {
  329. stillTrue = stillTrue && f(current->value);
  330. current = current->next;
  331. }
  332. return stillTrue;
  333. }
  334. // Info: Return this list reversed
  335. ImmutableList<T> * Reverse(ArenaAllocator * a)
  336. {
  337. auto out = ImmutableList<T>::Empty();
  338. auto current = this;
  339. while(!current->IsEmpty())
  340. {
  341. out = out->Prepend(current->First(),a);
  342. current = current->next;
  343. }
  344. return out;
  345. }
  346. // Info: Return this list reversed <Reverses the current list>
  347. ImmutableList<T> * ReverseCurrentList()
  348. {
  349. auto out = ImmutableList<T>::Empty();
  350. auto current = this;
  351. while(!current->IsEmpty())
  352. {
  353. auto next = current->next;
  354. current->next = out;
  355. out = current;
  356. current = next;
  357. }
  358. return out;
  359. }
  360. // Info: Filter this list by removing the elements where function f returns false
  361. // Parameters: f - the predicate function to filter by
  362. template<class F>
  363. ImmutableList<T> * Where(F f, ArenaAllocator * a)
  364. {
  365. auto out = ImmutableList<T>::Empty();
  366. auto tail = out;
  367. auto current = this;
  368. while(!current->IsEmpty())
  369. {
  370. auto head = current->First();
  371. if (f(head))
  372. {
  373. out = out->Append(head, a, &tail);
  374. }
  375. current = current->next;
  376. }
  377. return out;
  378. }
  379. // Info: Filter this list by removing the elements where function f returns false <Modifies existing list with removal of nodes that dont satisfy the condition f()>
  380. // Parameters: f - the predicate function to filter by
  381. template<class F>
  382. ImmutableList<T> * WhereInPlace(F f)
  383. {
  384. auto out = ImmutableList<T>::Empty();
  385. auto tail = out;
  386. auto current = this;
  387. while(!current->IsEmpty())
  388. {
  389. auto head = current->First();
  390. if (f(head))
  391. {
  392. if (out->IsEmpty())
  393. {
  394. // Need to add to the head
  395. out = current;
  396. }
  397. else
  398. {
  399. // Add to the tail
  400. Assert(!tail->IsEmpty());
  401. tail->next = current;
  402. }
  403. tail = current;
  404. }
  405. current = current->next;
  406. }
  407. if (!tail->IsEmpty())
  408. {
  409. tail->next = ImmutableList<T>::Empty();
  410. }
  411. return out;
  412. }
  413. // Info: Return a new list by calling function f called on element of this list that satisfy predicate wf
  414. // Parameters: wf - the predicate function to filter by
  415. // f - the mapping function
  416. template<class TOut, class selectF, class whereF>
  417. ImmutableList<TOut> * WhereSelect(whereF wf, selectF sf, ArenaAllocator * a)
  418. {
  419. auto out = ImmutableList<TOut>::Empty();
  420. auto tail = out;
  421. auto current = this;
  422. while(!current->IsEmpty())
  423. {
  424. auto head = current->First();
  425. if (wf(head))
  426. {
  427. out = out->Append(sf(current->First()), a, &tail);
  428. }
  429. current = current->next;
  430. }
  431. return out;
  432. }
  433. // Info: Run through current list to find the 1 or 0 item that satisfied predicate f
  434. // Parameters: f - the predicate function to filter by
  435. // Info: If there are 0 or 1 elements, return an option. Throw otherwise.
  436. template<class TOption, class F>
  437. Option<TOption> WhereToOption(F f)
  438. {
  439. auto out = ImmutableList<T>::Empty();
  440. auto current = this;
  441. while(!current->IsEmpty())
  442. {
  443. auto head = current->First();
  444. if (f(head))
  445. {
  446. if (out->IsEmpty())
  447. {
  448. out = current;
  449. }
  450. else
  451. {
  452. // Cannot convert Enumerable to Option because there is more than 1 item in the Enumerable
  453. Js::Throw::FatalProjectionError();
  454. }
  455. }
  456. current = current->next;
  457. }
  458. if (out->IsEmpty())
  459. {
  460. return nullptr;
  461. }
  462. return out->First();
  463. }
  464. // Info: Filter the list down to exactly one item.
  465. // Parameters: f - the predicate function to filter by
  466. template<class F>
  467. T WhereSingle(F f)
  468. {
  469. auto current = this;
  470. T * result = nullptr;
  471. while(!current->IsEmpty())
  472. {
  473. auto head = current->First();
  474. if (f(head))
  475. {
  476. Js::VerifyCatastrophic(result==nullptr); // There are multiple matching items.
  477. result = &current->value;
  478. }
  479. current = current->next;
  480. }
  481. Js::VerifyCatastrophic(result!=nullptr); // There are no matching items.
  482. return *result;
  483. }
  484. // Info: Return the first matching item
  485. // Parameters: f - the predicate function to filter by
  486. template<class F>
  487. Option<T> WhereFirst(F f)
  488. {
  489. auto current = this;
  490. while(!current->IsEmpty())
  491. {
  492. auto head = current->First();
  493. if (f(head))
  494. {
  495. return &current->value;
  496. }
  497. current = current->next;
  498. }
  499. return Option<T>();
  500. }
  501. // Info: Return the count of matching items
  502. // Parameters: f - the predicate function to filter by
  503. template<class F>
  504. size_t CountWhere(F f)
  505. {
  506. auto current = this;
  507. size_t count = 0;
  508. while(!current->IsEmpty())
  509. {
  510. auto head = current->First();
  511. if (f(head))
  512. {
  513. ++count;
  514. }
  515. current = current->next;
  516. }
  517. return count;
  518. }
  519. // Info: Return true if any item returns true for f
  520. // Parameters: f - the predicate function to filter by
  521. template<class F>
  522. bool ContainsWhere(F f)
  523. {
  524. return WhereFirst(f).HasValue();
  525. }
  526. // Info: Remove all instances of the given value from current list <Modifies existing list with removal of nodes that has 'value'>
  527. // Parameters: value - the value to remove from the list
  528. ImmutableList<T> * RemoveValueInPlace(T value)
  529. {
  530. return WhereInPlace([&](T seen) {return seen != value;});
  531. }
  532. // Info: Return the value from a list with exactly one element. Throw if there are 0 or 2+ elements.
  533. T ToSingle()
  534. {
  535. Js::VerifyCatastrophic(Count()==1);
  536. return value;
  537. }
  538. // Info: If there are 0 or 1 elements, return an option. Throw otherwise.
  539. template<class TOption>
  540. Option<TOption> ToOption()
  541. {
  542. auto en = this;
  543. if (en == Empty())
  544. {
  545. return nullptr;
  546. }
  547. Js::VerifyCatastrophic(en->next == Empty()); // Cannot convert Enumerable to Option because there is more than 1 item in the Enumerable
  548. return First();
  549. }
  550. // Info: Return the first element
  551. T& First()
  552. {
  553. Js::VerifyCatastrophic(!IsEmpty());
  554. return value;
  555. }
  556. // Info: Return the last item. Throw if there are none.
  557. T& Last()
  558. {
  559. Js::VerifyCatastrophic(!IsEmpty());
  560. if (next->IsEmpty())
  561. {
  562. return value;
  563. }
  564. return next->Last(); // Warning: possible stack usage
  565. }
  566. // Info: Return the nth item from the list. Throw if this list isn't long enough.
  567. T& Nth(size_t n)
  568. {
  569. Js::VerifyCatastrophic(!IsEmpty());
  570. if(n==0)
  571. {
  572. return value;
  573. }
  574. return next->Nth(n-1); // Warning: possible stack usage
  575. }
  576. // Info: Return the rest of the list
  577. ImmutableList<T> * GetTail()
  578. {
  579. return next;
  580. }
  581. // Info: Return the empty list
  582. static ImmutableList<T> * Empty()
  583. {
  584. return nullptr;
  585. }
  586. // Info: Iterate over the elements of an enumerable calling f1 for each and f2 between each.
  587. // Parameters: value - the value to remove from the list
  588. template <class F1, class F2>
  589. void IterateBetween(F1 f1, F2 f2)
  590. {
  591. auto en = this;
  592. bool more = en!=Empty();
  593. while(more)
  594. {
  595. auto last = en->First();
  596. f1(last);
  597. en = en->next;
  598. more = en!=Empty();
  599. if (more)
  600. {
  601. auto next = en->First();
  602. f2(last,next);
  603. }
  604. }
  605. }
  606. // Info: Return the size of the list
  607. size_t Count()
  608. {
  609. size_t count = 0;
  610. auto current = this;
  611. while(!current->IsEmpty())
  612. {
  613. ++count;
  614. current = current->next;
  615. }
  616. return count;
  617. }
  618. // Info: Return this list converted to an array in the heap. Get the size by calling Count().
  619. T ** ToReferenceArrayInHeap(size_t size)
  620. {
  621. Assert(size == Count());
  622. auto result = new T *[size];
  623. IfNullThrowOutOfMemory(result);
  624. auto current = this;
  625. for(size_t index = 0; index<size; ++index)
  626. {
  627. #pragma warning(push)
  628. #pragma warning(disable:22102)
  629. result[index] = &current->value;
  630. #pragma warning(pop)
  631. current = current->next;
  632. }
  633. Js::VerifyCatastrophic(current->IsEmpty());
  634. return result;
  635. }
  636. // Info: Sort the list by the given reference comparer <Modifies existing list such that it is ordered using comparer>
  637. // Parameters: comparer - comparer to sort with
  638. ImmutableList<T> * SortCurrentList(regex::Comparer<T *> * comparer)
  639. {
  640. auto size = Count();
  641. if (size == 0 || size == 1)
  642. {
  643. return this;
  644. }
  645. auto arr = ToReferenceArrayInHeap(size);
  646. QuickSort<T *, true>::Sort(arr, arr+(size-1), comparer);
  647. // Convert the reference Array into the List
  648. auto result = (ImmutableList<T>*)(arr[0] - offsetof(ImmutableList<T>, value));
  649. auto current = result;
  650. for(size_t i = 1; i<size; ++i)
  651. {
  652. current->next = (ImmutableList<T>*)(arr[i] - offsetof(ImmutableList<T>, value));
  653. current = current->next;
  654. }
  655. current->next = nullptr;
  656. delete []arr;
  657. return result;
  658. }
  659. // Info: Sort the list by the given reference comparer in reverse order <Modifies existing list such that it is ordered using comparer>
  660. // Parameters: comparer - comparer to sort with
  661. ImmutableList<T> * ReverseSortCurrentList(regex::Comparer<T *> * comparer)
  662. {
  663. auto size = Count();
  664. if (size == 0 || size == 1)
  665. {
  666. return this;
  667. }
  668. auto arr = ToReferenceArrayInHeap(size);
  669. QuickSort<T *, true>::Sort(arr, arr+(size-1), comparer);
  670. // Convert the reference Array into the List
  671. auto result = (ImmutableList<T>*)(arr[0] - offsetof(ImmutableList<T>, value));
  672. result->next = nullptr;
  673. for(size_t i = 1; i<size; ++i)
  674. {
  675. auto current = (ImmutableList<T>*)(arr[i] - offsetof(ImmutableList<T>, value));
  676. current->next = result;
  677. result = current;
  678. }
  679. delete []arr;
  680. return result;
  681. }
  682. // Info: Return true if the list is empty.
  683. bool IsEmpty()
  684. {
  685. return this==Empty();
  686. }
  687. // Info: Return a list containing the given single value
  688. // Parameters: value - the value
  689. static ImmutableList<T> * OfSingle(T value, ArenaAllocator * a)
  690. {
  691. return Anew(a, ImmutableList, value, nullptr);
  692. }
  693. // Info: Group elements that are equal to each other into sublists.
  694. // NOTE: This function is N^2 and is currently only suitable for small
  695. // lists. If larger lists are needed, please change this function
  696. // to group into an intermediate hash table.
  697. // Parameters: equals - the value
  698. template<class FEquals>
  699. ImmutableList<ImmutableList<T> *> * GroupBy(FEquals equals, ArenaAllocator * a)
  700. {
  701. auto out = ImmutableList<ImmutableList<T>*>::Empty();
  702. auto currentIn = this;
  703. while(!currentIn->IsEmpty())
  704. {
  705. auto currentOut = out;
  706. bool found = false;
  707. while(!currentOut->IsEmpty())
  708. {
  709. ImmutableList<T>* currentOutValue = currentOut->First();
  710. if (equals(currentOutValue->First(),currentIn->value))
  711. {
  712. found = true;
  713. currentOut->First() = currentOutValue->Prepend(currentIn->First(),a);
  714. break;
  715. }
  716. currentOut = currentOut->GetTail();
  717. }
  718. if (!found)
  719. {
  720. out = out->Prepend(OfSingle(currentIn->value,a),a);
  721. }
  722. currentIn = currentIn->next;
  723. }
  724. return out;
  725. }
  726. // Info: Create groups where FEquals returns true for sets of adjacent element. If the list is sorted, this is equivalent to GroupBy.
  727. // Parameters: equals - the value
  728. template<class FEquals>
  729. ImmutableList<ImmutableList<T>*> * GroupByAdjacentOnCurrentList(FEquals equals, ArenaAllocator * a)
  730. {
  731. auto out = ImmutableList<ImmutableList<T>*>::Empty();
  732. auto tail = out;
  733. if(!IsEmpty())
  734. {
  735. auto current = this;
  736. auto set = current;
  737. auto setTail = set;
  738. while(current)
  739. {
  740. if (!equals(current->First(), set->First()))
  741. {
  742. Assert(!set->IsEmpty() && !setTail->IsEmpty());
  743. setTail->next = nullptr;
  744. out = out->Append(set, a, &tail);
  745. set = current;
  746. }
  747. setTail = current;
  748. current = current->GetTail();
  749. }
  750. Assert(!set->IsEmpty() && !setTail->IsEmpty());
  751. setTail->next = nullptr;
  752. out = out->Append(set, a, &tail);
  753. }
  754. return out;
  755. }
  756. };
  757. // Info: Return a list containing the given single value
  758. // Parameters: value - the value
  759. template<typename T>
  760. ImmutableList<const T *> * ToImmutableList(const T * value, ArenaAllocator * a)
  761. {
  762. return ImmutableList<const T *>::OfSingle(value,a);
  763. }
  764. template<class T, class TAllocator, int chunkSize = 4>
  765. class ImmutableArrayBuilder
  766. {
  767. private:
  768. TAllocator *allocator;
  769. T * arrayData;
  770. size_t currentIndex;
  771. size_t arraySize;
  772. bool fAutoDeleteArray;
  773. public:
  774. ImmutableArrayBuilder(TAllocator *alloc) : allocator(alloc), arrayData(nullptr), currentIndex(0), arraySize(0), fAutoDeleteArray(true)
  775. {
  776. }
  777. ~ImmutableArrayBuilder()
  778. {
  779. if (fAutoDeleteArray)
  780. {
  781. AllocatorDeleteArray(TAllocator, allocator, arraySize, arrayData);
  782. }
  783. }
  784. void Append(T newEntry)
  785. {
  786. // Generate new chunk
  787. if (currentIndex == arraySize)
  788. {
  789. T * newChunk = AllocatorNewArray(TAllocator, allocator, T, arraySize + chunkSize);
  790. memcpy_s(newChunk, (arraySize + chunkSize) * sizeof(T), arrayData, arraySize * sizeof(T));
  791. if (arrayData)
  792. {
  793. AllocatorDeleteArray(TAllocator, allocator, arraySize, arrayData);
  794. }
  795. arrayData = newChunk;
  796. arraySize = arraySize + chunkSize;
  797. }
  798. Assert(arrayData != nullptr);
  799. arrayData[currentIndex] = newEntry;
  800. currentIndex++;
  801. }
  802. size_t GetCount()
  803. {
  804. return currentIndex;
  805. }
  806. T * Get()
  807. {
  808. return arrayData;
  809. }
  810. void DisableAutoDelete()
  811. {
  812. fAutoDeleteArray = false;
  813. }
  814. };
  815. template<int chunkSize>
  816. class ImmutableStringBuilder
  817. {
  818. private:
  819. struct StringChunk
  820. {
  821. LPCWSTR dataPtr[chunkSize];
  822. StringChunk *next;
  823. StringChunk() : next(nullptr)
  824. {
  825. }
  826. };
  827. StringChunk *head;
  828. StringChunk *tail;
  829. int currentIndex;
  830. size_t stringSize;
  831. // tracking allocated strings based on non-Append(String) calls
  832. struct AllocatedStringChunk
  833. {
  834. LPCWSTR dataPtr;
  835. AllocatedStringChunk *next;
  836. AllocatedStringChunk() : next(nullptr)
  837. {
  838. }
  839. };
  840. AllocatedStringChunk* allocatedStringChunksHead;
  841. public:
  842. ImmutableStringBuilder() : head(nullptr), tail(nullptr), currentIndex(chunkSize), stringSize(1), allocatedStringChunksHead(nullptr)
  843. {
  844. }
  845. ~ImmutableStringBuilder()
  846. {
  847. // unallocate strings
  848. AllocatedStringChunk* allocatedStringChunk = this->allocatedStringChunksHead;
  849. AllocatedStringChunk* nextAllocatedStringChunk;
  850. while (allocatedStringChunk != nullptr)
  851. {
  852. nextAllocatedStringChunk = allocatedStringChunk->next;
  853. delete[] allocatedStringChunk->dataPtr;
  854. delete allocatedStringChunk;
  855. allocatedStringChunk = nextAllocatedStringChunk;
  856. }
  857. while (head != nullptr)
  858. {
  859. auto current = head;
  860. head = head->next;
  861. delete current;
  862. }
  863. }
  864. void AppendInt32(int32 value);
  865. void AppendUInt64(uint64 value);
  866. void AppendWithCopy(_In_z_ LPCWSTR str);
  867. void AppendBool(bool value)
  868. {
  869. this->Append(value ? _u("true") : _u("false"));
  870. }
  871. void Append(LPCWSTR str)
  872. {
  873. // silently ignore nullptr usage pattern, to avoid cluttering codebase
  874. if (str == nullptr)
  875. return;
  876. size_t newStrSize = stringSize + wcslen(str);
  877. if (newStrSize < stringSize)
  878. {
  879. // Overflow
  880. Js::Throw::OutOfMemory();
  881. }
  882. // Generate new chunk
  883. if (currentIndex == chunkSize)
  884. {
  885. StringChunk *newChunk = new StringChunk();
  886. IfNullThrowOutOfMemory(newChunk);
  887. if (tail == nullptr)
  888. {
  889. Assert(head == nullptr);
  890. head = newChunk;
  891. tail = newChunk;
  892. }
  893. else
  894. {
  895. tail->next = newChunk;
  896. tail = newChunk;
  897. }
  898. currentIndex = 0;
  899. }
  900. Assert(tail != nullptr);
  901. tail->dataPtr[currentIndex] = str;
  902. currentIndex++;
  903. stringSize = newStrSize;
  904. }
  905. template<class TAllocator>
  906. LPCWSTR Get(TAllocator *allocator)
  907. {
  908. char16 *str = AllocatorNewArray(TAllocator, allocator, char16, stringSize);
  909. str[0] = _u('\0');
  910. auto current = head;
  911. while (current != nullptr)
  912. {
  913. int lastIndex = (current == tail) ? currentIndex : chunkSize;
  914. for (int index = 0; index < lastIndex; index++)
  915. {
  916. wcscat_s(str, stringSize, current->dataPtr[index]);
  917. }
  918. current = current->next;
  919. }
  920. return str;
  921. }
  922. // Free a string returned by Get()
  923. template<class TAllocator>
  924. void FreeString(LPCWSTR str)
  925. {
  926. ImmutableList<chunkSize>::FreeString(allocator, str, stringSize);
  927. }
  928. template<class TAllocator>
  929. static void FreeString(TAllocator *allocator, LPCWSTR str, size_t strLength)
  930. {
  931. AssertMsg(allocator != nullptr, "allocator != nullptr");
  932. AssertMsg(str != nullptr, "str != nullptr");
  933. AllocatorDeleteArray(TAllocator, allocator, strLength, str);
  934. }
  935. };
  936. typedef ImmutableStringBuilder<8> DefaultImmutableStringBuilder;
  937. }