StructDictionary.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Reflection;
  6. namespace Jint.Runtime
  7. {
  8. internal enum InsertionBehavior : byte
  9. {
  10. /// <summary>
  11. /// Specifies that an existing entry with the same key should be overwritten if encountered.
  12. /// </summary>
  13. OverwriteExisting = 1,
  14. /// <summary>
  15. /// Specifies that if an existing entry with the same key is encountered, an exception should be thrown.
  16. /// </summary>
  17. ThrowOnExisting = 2,
  18. /// <summary>
  19. /// Specifies that if existing entry with the same key is encountered, the update will be skipped.
  20. /// </summary>
  21. SkipIfExists = 3
  22. }
  23. /// <summary>
  24. /// Taken from .NET source to create performant specialized dictionary containing structs for Jint.
  25. /// </summary>
  26. internal sealed class StructDictionary<TValue> where TValue : struct
  27. {
  28. private static readonly EqualityComparer<string> _comparer;
  29. static StructDictionary()
  30. {
  31. // we want to use same comparer as default dictionary impl that is hidden from us
  32. // .NET Core uses non-randomized hash code generation that is faster than default
  33. try
  34. {
  35. Dictionary<string, TValue> dictionary = new Dictionary<string, TValue>();
  36. var field = dictionary.GetType().GetField("_comparer", BindingFlags.Instance | BindingFlags.NonPublic);
  37. field = field ?? dictionary.GetType().GetField("comparer", BindingFlags.Instance | BindingFlags.NonPublic);
  38. _comparer = field?.GetValue(dictionary) as EqualityComparer<string> ?? EqualityComparer<string>.Default;
  39. }
  40. catch
  41. {
  42. _comparer = EqualityComparer<string>.Default;
  43. }
  44. }
  45. private struct Entry
  46. {
  47. public int hashCode; // Lower 31 bits of hash code, -1 if unused
  48. public int next; // Index of next entry, -1 if last
  49. public string key; // Key of entry
  50. public TValue value; // Value of entry
  51. }
  52. private int[] _buckets;
  53. private Entry[] _entries;
  54. private int _count;
  55. private int _freeList;
  56. private int _freeCount;
  57. private KeyCollection _keys;
  58. private ValueCollection _values;
  59. public StructDictionary() : this(0)
  60. {
  61. }
  62. public StructDictionary(int capacity)
  63. {
  64. if (capacity > 0) Initialize(capacity);
  65. }
  66. public int Count => _count - _freeCount;
  67. public KeyCollection Keys
  68. {
  69. get
  70. {
  71. if (_keys == null) _keys = new KeyCollection(this);
  72. return _keys;
  73. }
  74. }
  75. public ValueCollection Values
  76. {
  77. get
  78. {
  79. if (_values == null) _values = new ValueCollection(this);
  80. return _values;
  81. }
  82. }
  83. public ref TValue GetItem(string key)
  84. {
  85. int i = FindEntry(key);
  86. if (i >= 0) return ref _entries[i].value;
  87. ExceptionHelper.ThrowArgumentException("key " + key + " not part of dictionary");
  88. return ref _entries[0].value;
  89. }
  90. public void Clear()
  91. {
  92. int count = _count;
  93. if (count > 0)
  94. {
  95. Array.Clear(_buckets, 0, _buckets.Length);
  96. _count = 0;
  97. _freeList = -1;
  98. _freeCount = 0;
  99. Array.Clear(_entries, 0, count);
  100. }
  101. }
  102. public bool ContainsKey(string key)
  103. => FindEntry(key) >= 0;
  104. public Enumerator GetEnumerator()
  105. => new Enumerator(this, Enumerator.KeyValuePair);
  106. private int FindEntry(string key)
  107. {
  108. int i = -1;
  109. int[] buckets = _buckets;
  110. Entry[] entries = _entries;
  111. int collisionCount = 0;
  112. if (buckets != null)
  113. {
  114. int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
  115. // Value in _buckets is 1-based
  116. i = buckets[hashCode % buckets.Length] - 1;
  117. do
  118. {
  119. // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
  120. // Test in if to drop range check for following array access
  121. if ((uint) i >= (uint) entries.Length ||
  122. (entries[i].hashCode == hashCode && entries[i].key == key))
  123. {
  124. break;
  125. }
  126. i = entries[i].next;
  127. if (collisionCount >= entries.Length)
  128. {
  129. // The chain of entries forms a loop; which means a concurrent update has happened.
  130. // Break out of the loop and throw, rather than looping forever.
  131. ExceptionHelper.ThrowInvalidOperationException();
  132. }
  133. collisionCount++;
  134. } while (true);
  135. }
  136. return i;
  137. }
  138. private int Initialize(int capacity)
  139. {
  140. int size = HashHelpers.GetPrime(capacity);
  141. _freeList = -1;
  142. _buckets = new int[size];
  143. _entries = new Entry[size];
  144. return size;
  145. }
  146. public bool TryInsert(string key, in TValue value, InsertionBehavior behavior)
  147. {
  148. if (_buckets == null)
  149. {
  150. Initialize(0);
  151. }
  152. Entry[] entries = _entries;
  153. int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
  154. int collisionCount = 0;
  155. ref int bucket = ref _buckets[hashCode % _buckets.Length];
  156. // Value in _buckets is 1-based
  157. int i = bucket - 1;
  158. do
  159. {
  160. // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
  161. // Test uint in if rather than loop condition to drop range check for following array access
  162. if ((uint) i >= (uint) entries.Length)
  163. {
  164. break;
  165. }
  166. if (entries[i].hashCode == hashCode && entries[i].key == key)
  167. {
  168. if (behavior == InsertionBehavior.OverwriteExisting)
  169. {
  170. entries[i].value = value;
  171. return true;
  172. }
  173. if (behavior == InsertionBehavior.SkipIfExists)
  174. {
  175. return true;
  176. }
  177. if (behavior == InsertionBehavior.ThrowOnExisting)
  178. {
  179. ExceptionHelper.ThrowArgumentException("key already exists");
  180. }
  181. return false;
  182. }
  183. i = entries[i].next;
  184. if (collisionCount >= entries.Length)
  185. {
  186. // The chain of entries forms a loop; which means a concurrent update has happened.
  187. // Break out of the loop and throw, rather than looping forever.
  188. ExceptionHelper.ThrowInvalidOperationException();
  189. }
  190. collisionCount++;
  191. } while (true);
  192. bool updateFreeList = false;
  193. int index;
  194. if (_freeCount > 0)
  195. {
  196. index = _freeList;
  197. updateFreeList = true;
  198. _freeCount--;
  199. }
  200. else
  201. {
  202. int count = _count;
  203. if (count == entries.Length)
  204. {
  205. Resize();
  206. bucket = ref _buckets[hashCode % _buckets.Length];
  207. }
  208. index = count;
  209. _count = count + 1;
  210. entries = _entries;
  211. }
  212. ref Entry entry = ref entries[index];
  213. if (updateFreeList)
  214. {
  215. _freeList = entry.next;
  216. }
  217. entry.hashCode = hashCode;
  218. // Value in _buckets is 1-based
  219. entry.next = bucket - 1;
  220. entry.key = key;
  221. entry.value = value;
  222. // Value in _buckets is 1-based
  223. bucket = index + 1;
  224. return true;
  225. }
  226. private void Resize()
  227. => Resize(HashHelpers.ExpandPrime(_count), false);
  228. private void Resize(int newSize, bool forceNewHashCodes)
  229. {
  230. // Value types never rehash
  231. Debug.Assert(!forceNewHashCodes || default(string) == null);
  232. Debug.Assert(newSize >= _entries.Length);
  233. int[] buckets = new int[newSize];
  234. Entry[] entries = new Entry[newSize];
  235. int count = _count;
  236. Array.Copy(_entries, 0, entries, 0, count);
  237. if (forceNewHashCodes)
  238. {
  239. for (int i = 0; i < count; i++)
  240. {
  241. if (entries[i].hashCode >= 0)
  242. {
  243. entries[i].hashCode = (_comparer.GetHashCode(entries[i].key) & 0x7FFFFFFF);
  244. }
  245. }
  246. }
  247. for (int i = 0; i < count; i++)
  248. {
  249. if (entries[i].hashCode >= 0)
  250. {
  251. int bucket = entries[i].hashCode % newSize;
  252. // Value in _buckets is 1-based
  253. entries[i].next = buckets[bucket] - 1;
  254. // Value in _buckets is 1-based
  255. buckets[bucket] = i + 1;
  256. }
  257. }
  258. _buckets = buckets;
  259. _entries = entries;
  260. }
  261. // The overload Remove(string key, out TValue value) is a copy of this method with one additional
  262. // statement to copy the value for entry being removed into the output parameter.
  263. // Code has been intentionally duplicated for performance reasons.
  264. public bool Remove(string key)
  265. {
  266. int[] buckets = _buckets;
  267. Entry[] entries = _entries;
  268. int collisionCount = 0;
  269. if (buckets != null)
  270. {
  271. int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
  272. int bucket = hashCode % buckets.Length;
  273. int last = -1;
  274. // Value in buckets is 1-based
  275. int i = buckets[bucket] - 1;
  276. while (i >= 0)
  277. {
  278. ref Entry entry = ref entries[i];
  279. if (entry.hashCode == hashCode && entry.key == key)
  280. {
  281. if (last < 0)
  282. {
  283. // Value in buckets is 1-based
  284. buckets[bucket] = entry.next + 1;
  285. }
  286. else
  287. {
  288. entries[last].next = entry.next;
  289. }
  290. entry.hashCode = -1;
  291. entry.next = _freeList;
  292. entry.key = null;
  293. entry.value = default;
  294. _freeList = i;
  295. _freeCount++;
  296. return true;
  297. }
  298. last = i;
  299. i = entry.next;
  300. if (collisionCount >= entries.Length)
  301. {
  302. // The chain of entries forms a loop; which means a concurrent update has happened.
  303. // Break out of the loop and throw, rather than looping forever.
  304. ExceptionHelper.ThrowInvalidOperationException();
  305. }
  306. collisionCount++;
  307. }
  308. }
  309. return false;
  310. }
  311. // This overload is a copy of the overload Remove(string key) with one additional
  312. // statement to copy the value for entry being removed into the output parameter.
  313. // Code has been intentionally duplicated for performance reasons.
  314. public bool Remove(string key, out TValue value)
  315. {
  316. int[] buckets = _buckets;
  317. Entry[] entries = _entries;
  318. int collisionCount = 0;
  319. if (buckets != null)
  320. {
  321. int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
  322. int bucket = hashCode % buckets.Length;
  323. int last = -1;
  324. // Value in buckets is 1-based
  325. int i = buckets[bucket] - 1;
  326. while (i >= 0)
  327. {
  328. ref Entry entry = ref entries[i];
  329. if (entry.hashCode == hashCode && entry.key == key)
  330. {
  331. if (last < 0)
  332. {
  333. // Value in buckets is 1-based
  334. buckets[bucket] = entry.next + 1;
  335. }
  336. else
  337. {
  338. entries[last].next = entry.next;
  339. }
  340. value = entry.value;
  341. entry.hashCode = -1;
  342. entry.next = _freeList;
  343. entry.key = null;
  344. entry.value = default;
  345. _freeList = i;
  346. _freeCount++;
  347. return true;
  348. }
  349. last = i;
  350. i = entry.next;
  351. if (collisionCount >= entries.Length)
  352. {
  353. // The chain of entries forms a loop; which means a concurrent update has happened.
  354. // Break out of the loop and throw, rather than looping forever.
  355. ExceptionHelper.ThrowInvalidOperationException();
  356. }
  357. collisionCount++;
  358. }
  359. }
  360. value = default;
  361. return false;
  362. }
  363. public bool TryGetValue(string key, out TValue value)
  364. {
  365. int i = FindEntry(key);
  366. if (i >= 0)
  367. {
  368. value = _entries[i].value;
  369. return true;
  370. }
  371. value = default;
  372. return false;
  373. }
  374. /// <summary>
  375. /// Ensures that the dictionary can hold up to 'capacity' entries without any further expansion of its backing storage
  376. /// </summary>
  377. public int EnsureCapacity(int capacity)
  378. {
  379. int currentCapacity = _entries == null ? 0 : _entries.Length;
  380. if (currentCapacity >= capacity)
  381. return currentCapacity;
  382. if (_buckets == null)
  383. return Initialize(capacity);
  384. int newSize = HashHelpers.GetPrime(capacity);
  385. Resize(newSize, forceNewHashCodes: false);
  386. return newSize;
  387. }
  388. public struct Enumerator : IEnumerator<KeyValuePair<string, TValue>>,
  389. IDictionaryEnumerator
  390. {
  391. private readonly StructDictionary<TValue> _dictionary;
  392. private int _index;
  393. private KeyValuePair<string, TValue> _current;
  394. private readonly int _getEnumeratorRetType; // What should Enumerator.Current return?
  395. internal const int DictEntry = 1;
  396. internal const int KeyValuePair = 2;
  397. internal Enumerator(StructDictionary<TValue> dictionary, int getEnumeratorRetType)
  398. {
  399. _dictionary = dictionary;
  400. _index = 0;
  401. _getEnumeratorRetType = getEnumeratorRetType;
  402. _current = new KeyValuePair<string, TValue>();
  403. }
  404. public bool MoveNext()
  405. {
  406. // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
  407. // dictionary.count+1 could be negative if dictionary.count is int.MaxValue
  408. while ((uint) _index < (uint) _dictionary._count)
  409. {
  410. ref Entry entry = ref _dictionary._entries[_index++];
  411. if (entry.hashCode >= 0)
  412. {
  413. _current = new KeyValuePair<string, TValue>(entry.key, entry.value);
  414. return true;
  415. }
  416. }
  417. _index = _dictionary._count + 1;
  418. _current = new KeyValuePair<string, TValue>();
  419. return false;
  420. }
  421. public KeyValuePair<string, TValue> Current => _current;
  422. public void Dispose()
  423. {
  424. }
  425. object IEnumerator.Current
  426. {
  427. get
  428. {
  429. if (_index == 0 || (_index == _dictionary._count + 1))
  430. {
  431. ExceptionHelper.ThrowInvalidOperationException();
  432. }
  433. if (_getEnumeratorRetType == DictEntry)
  434. {
  435. return new DictionaryEntry(_current.Key, _current.Value);
  436. }
  437. else
  438. {
  439. return new KeyValuePair<string, TValue>(_current.Key, _current.Value);
  440. }
  441. }
  442. }
  443. void IEnumerator.Reset()
  444. {
  445. _index = 0;
  446. _current = new KeyValuePair<string, TValue>();
  447. }
  448. DictionaryEntry IDictionaryEnumerator.Entry
  449. {
  450. get
  451. {
  452. if (_index == 0 || (_index == _dictionary._count + 1))
  453. {
  454. ExceptionHelper.ThrowInvalidOperationException();
  455. }
  456. return new DictionaryEntry(_current.Key, _current.Value);
  457. }
  458. }
  459. object IDictionaryEnumerator.Key
  460. {
  461. get
  462. {
  463. if (_index == 0 || (_index == _dictionary._count + 1))
  464. {
  465. ExceptionHelper.ThrowInvalidOperationException();
  466. }
  467. return _current.Key;
  468. }
  469. }
  470. object IDictionaryEnumerator.Value
  471. {
  472. get
  473. {
  474. if (_index == 0 || (_index == _dictionary._count + 1))
  475. {
  476. ExceptionHelper.ThrowInvalidOperationException();
  477. }
  478. return _current.Value;
  479. }
  480. }
  481. }
  482. public sealed class KeyCollection
  483. {
  484. private readonly StructDictionary<TValue> _dictionary;
  485. public KeyCollection(StructDictionary<TValue> dictionary)
  486. {
  487. if (dictionary == null)
  488. {
  489. ExceptionHelper.ThrowArgumentNullException(nameof(dictionary));
  490. }
  491. _dictionary = dictionary;
  492. }
  493. public Enumerator GetEnumerator()
  494. => new Enumerator(_dictionary);
  495. public int Count => _dictionary.Count;
  496. public struct Enumerator : IEnumerator<string>, IEnumerator
  497. {
  498. private readonly StructDictionary<TValue> _dictionary;
  499. private int _index;
  500. private string _currenstring;
  501. internal Enumerator(StructDictionary<TValue> dictionary)
  502. {
  503. _dictionary = dictionary;
  504. _index = 0;
  505. _currenstring = default;
  506. }
  507. public void Dispose()
  508. {
  509. }
  510. public bool MoveNext()
  511. {
  512. while ((uint) _index < (uint) _dictionary._count)
  513. {
  514. ref Entry entry = ref _dictionary._entries[_index++];
  515. if (entry.hashCode >= 0)
  516. {
  517. _currenstring = entry.key;
  518. return true;
  519. }
  520. }
  521. _index = _dictionary._count + 1;
  522. _currenstring = default;
  523. return false;
  524. }
  525. public string Current => _currenstring;
  526. object IEnumerator.Current
  527. {
  528. get
  529. {
  530. if (_index == 0 || (_index == _dictionary._count + 1))
  531. {
  532. ExceptionHelper.ThrowInvalidOperationException();
  533. }
  534. return _currenstring;
  535. }
  536. }
  537. void IEnumerator.Reset()
  538. {
  539. _index = 0;
  540. _currenstring = default;
  541. }
  542. }
  543. }
  544. public sealed class ValueCollection
  545. {
  546. private readonly StructDictionary<TValue> _dictionary;
  547. public ValueCollection(StructDictionary<TValue> dictionary)
  548. {
  549. _dictionary = dictionary;
  550. }
  551. public Enumerator GetEnumerator()
  552. => new Enumerator(_dictionary);
  553. public int Count => _dictionary.Count;
  554. public struct Enumerator : IEnumerator<TValue>
  555. {
  556. private readonly StructDictionary<TValue> _dictionary;
  557. private int _index;
  558. private TValue _currentValue;
  559. internal Enumerator(StructDictionary<TValue> dictionary)
  560. {
  561. _dictionary = dictionary;
  562. _index = 0;
  563. _currentValue = default;
  564. }
  565. public void Dispose()
  566. {
  567. }
  568. public bool MoveNext()
  569. {
  570. while ((uint) _index < (uint) _dictionary._count)
  571. {
  572. ref Entry entry = ref _dictionary._entries[_index++];
  573. if (entry.hashCode >= 0)
  574. {
  575. _currentValue = entry.value;
  576. return true;
  577. }
  578. }
  579. _index = _dictionary._count + 1;
  580. _currentValue = default;
  581. return false;
  582. }
  583. public TValue Current => _currentValue;
  584. object IEnumerator.Current
  585. {
  586. get
  587. {
  588. if (_index == 0 || (_index == _dictionary._count + 1))
  589. {
  590. ExceptionHelper.ThrowInvalidOperationException();
  591. }
  592. return _currentValue;
  593. }
  594. }
  595. void IEnumerator.Reset()
  596. {
  597. _index = 0;
  598. _currentValue = default;
  599. }
  600. }
  601. }
  602. private static class HashHelpers
  603. {
  604. public const int HashCollisionThreshold = 100;
  605. // This is the maximum prime smaller than Array.MaxArrayLength
  606. public const int MaxPrimeArrayLength = 0x7FEFFFFD;
  607. public const int HashPrime = 101;
  608. // Table of prime numbers to use as hash table sizes.
  609. // A typical resize algorithm would pick the smallest prime number in this array
  610. // that is larger than twice the previous capacity.
  611. // Suppose our Hashtable currently has capacity x and enough elements are added
  612. // such that a resize needs to occur. Resizing first computes 2x then finds the
  613. // first prime in the table greater than 2x, i.e. if primes are ordered
  614. // p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n.
  615. // Doubling is important for preserving the asymptotic complexity of the
  616. // hashtable operations such as add. Having a prime guarantees that double
  617. // hashing does not lead to infinite loops. IE, your hash function will be
  618. // h1(key) + i*h2(key), 0 <= i < size. h2 and the size must be relatively prime.
  619. // We prefer the low computation costs of higher prime numbers over the increased
  620. // memory allocation of a fixed prime number i.e. when right sizing a HashSet.
  621. public static readonly int[] primes =
  622. {
  623. 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
  624. 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
  625. 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
  626. 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
  627. 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369
  628. };
  629. public static bool IsPrime(int candidate)
  630. {
  631. if ((candidate & 1) != 0)
  632. {
  633. int limit = (int) Math.Sqrt(candidate);
  634. for (int divisor = 3; divisor <= limit; divisor += 2)
  635. {
  636. if ((candidate % divisor) == 0)
  637. return false;
  638. }
  639. return true;
  640. }
  641. return (candidate == 2);
  642. }
  643. public static int GetPrime(int min)
  644. {
  645. if (min < 0)
  646. throw new ArgumentException();
  647. for (int i = 0; i < primes.Length; i++)
  648. {
  649. int prime = primes[i];
  650. if (prime >= min)
  651. return prime;
  652. }
  653. //outside of our predefined table.
  654. //compute the hard way.
  655. for (int i = (min | 1); i < int.MaxValue; i += 2)
  656. {
  657. if (IsPrime(i) && ((i - 1) % HashPrime != 0))
  658. return i;
  659. }
  660. return min;
  661. }
  662. // Returns size of hashtable to grow to.
  663. public static int ExpandPrime(int oldSize)
  664. {
  665. int newSize = 2 * oldSize;
  666. // Allow the hashtables to grow to maximum possible size (~2G elements) before encountering capacity overflow.
  667. // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
  668. if ((uint) newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
  669. {
  670. Debug.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
  671. return MaxPrimeArrayLength;
  672. }
  673. return GetPrime(newSize);
  674. }
  675. }
  676. }
  677. }