PageRenderTime 34ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/Languages/Ruby/Libraries/Extensions/IListOps.cs

http://github.com/IronLanguages/main
C# | 2079 lines | 1607 code | 370 blank | 102 comment | 394 complexity | af5de6fed43864d6c2511959b65451c8 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.Reflection;
  20. using System.Runtime.CompilerServices;
  21. using System.Runtime.InteropServices;
  22. using Microsoft.Scripting;
  23. using Microsoft.Scripting.Generation;
  24. using Microsoft.Scripting.Runtime;
  25. using Microsoft.Scripting.Utils;
  26. using IronRuby.Runtime;
  27. using IronRuby.Runtime.Calls;
  28. using IronRuby.Runtime.Conversions;
  29. namespace IronRuby.Builtins {
  30. using EachSite = Func<CallSite, object, Proc, object>;
  31. [RubyModule(Extends = typeof(IList), Restrictions = ModuleRestrictions.None)]
  32. [Includes(typeof(Enumerable))]
  33. public static class IListOps {
  34. #region Helpers
  35. // MRI: Some operations check frozen flag even if they don't change the array content.
  36. private static void RequireNotFrozen(IList/*!*/ self) {
  37. RubyArray array = self as RubyArray;
  38. if (array != null && array.IsFrozen) {
  39. throw RubyExceptions.CreateObjectFrozenError();
  40. }
  41. }
  42. internal static int NormalizeIndex(IList/*!*/ list, int index) {
  43. return NormalizeIndex(list.Count, index);
  44. }
  45. internal static int NormalizeIndexThrowIfNegative(IList/*!*/ list, int index) {
  46. index = NormalizeIndex(list.Count, index);
  47. if (index < 0) {
  48. throw RubyExceptions.CreateIndexError("index {0} out of array", index);
  49. }
  50. return index;
  51. }
  52. internal static int NormalizeIndex(int count, int index) {
  53. return index < 0 ? index + count : index;
  54. }
  55. internal static bool NormalizeRange(int listCount, ref int start, ref int count) {
  56. start = NormalizeIndex(listCount, start);
  57. if (start < 0 || start > listCount || count < 0) {
  58. return false;
  59. }
  60. if (count != 0) {
  61. count = start + count > listCount ? listCount - start : count;
  62. }
  63. return true;
  64. }
  65. internal static bool NormalizeRange(ConversionStorage<int>/*!*/ fixnumCast, int listCount, Range/*!*/ range, out int begin, out int count) {
  66. begin = Protocols.CastToFixnum(fixnumCast, range.Begin);
  67. int end = Protocols.CastToFixnum(fixnumCast, range.End);
  68. begin = NormalizeIndex(listCount, begin);
  69. if (begin < 0 || begin > listCount) {
  70. count = 0;
  71. return false;
  72. }
  73. end = NormalizeIndex(listCount, end);
  74. count = range.ExcludeEnd ? end - begin : end - begin + 1;
  75. return true;
  76. }
  77. private static bool InRangeNormalized(IList/*!*/ list, ref int index) {
  78. if (index < 0) {
  79. index = index + list.Count;
  80. }
  81. return index >= 0 && index < list.Count;
  82. }
  83. private static IList/*!*/ GetResultRange(UnaryOpStorage/*!*/ allocateStorage, IList/*!*/ list, int index, int count) {
  84. IList result = CreateResultArray(allocateStorage, list);
  85. int stop = index + count;
  86. for (int i = index; i < stop; i++) {
  87. result.Add(list[i]);
  88. }
  89. return result;
  90. }
  91. private static void InsertRange(IList/*!*/ list, int index, IList/*!*/ items, int start, int count) {
  92. RubyArray array;
  93. List<object> listOfObject;
  94. ICollection<object> collection;
  95. if ((array = list as RubyArray) != null) {
  96. array.InsertRange(index, items, start, count);
  97. } else if ((listOfObject = list as List<object>) != null && ((collection = items as ICollection<object>) != null) && start == 0 && count == collection.Count) {
  98. listOfObject.InsertRange(index, collection);
  99. } else {
  100. for (int i = 0; i < count; i++) {
  101. list.Insert(index + i, items[start + i]);
  102. }
  103. }
  104. }
  105. internal static void RemoveRange(IList/*!*/ collection, int index, int count) {
  106. if (count <= 1) {
  107. if (count > 0) {
  108. collection.RemoveAt(index);
  109. }
  110. return;
  111. }
  112. List<object> list;
  113. RubyArray array;
  114. if ((array = collection as RubyArray) != null) {
  115. array.RemoveRange(index, count);
  116. } else if ((list = collection as List<object>) != null) {
  117. list.RemoveRange(index, count);
  118. } else {
  119. for (int i = index + count - 1; i >= index; i--) {
  120. collection.RemoveAt(i);
  121. }
  122. }
  123. }
  124. internal static void AddRange(IList/*!*/ collection, IList/*!*/ items) {
  125. RubyArray array;
  126. int count = items.Count;
  127. if (count <= 1) {
  128. if (count > 0) {
  129. collection.Add(items[0]);
  130. } else if ((array = collection as RubyArray) != null) {
  131. array.RequireNotFrozen();
  132. }
  133. return;
  134. }
  135. array = collection as RubyArray;
  136. if (array != null) {
  137. array.AddRange(items);
  138. } else {
  139. for (int i = 0; i < count; i++) {
  140. collection.Add(items[i]);
  141. }
  142. }
  143. }
  144. private static IList/*!*/ CreateResultArray(UnaryOpStorage/*!*/ allocateStorage, IList/*!*/ list) {
  145. // RubyArray:
  146. var array = list as RubyArray;
  147. if (array != null) {
  148. return array.CreateInstance();
  149. }
  150. // interop - call a default ctor to get an instance:
  151. var allocate = allocateStorage.GetCallSite("allocate", 0);
  152. var cls = allocateStorage.Context.GetClassOf(list);
  153. var result = allocate.Target(allocate, cls) as IList;
  154. if (result != null) {
  155. return result;
  156. }
  157. throw RubyExceptions.CreateTypeError("{0}#allocate should return IList", cls.Name);
  158. }
  159. internal static IEnumerable<Int32>/*!*/ ReverseEnumerateIndexes(IList/*!*/ collection) {
  160. for (int originalSize = collection.Count, i = originalSize - 1; i >= 0; i--) {
  161. yield return i;
  162. if (collection.Count < originalSize) {
  163. i = originalSize - (originalSize - collection.Count);
  164. originalSize = collection.Count;
  165. }
  166. }
  167. }
  168. #endregion
  169. #region initialize_copy, replace, clear, to_a, to_ary
  170. [RubyMethod("replace")]
  171. [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)]
  172. public static IList/*!*/ Replace(IList/*!*/ self, [NotNull, DefaultProtocol]IList/*!*/ other) {
  173. self.Clear();
  174. AddRange(self, other);
  175. return self;
  176. }
  177. [RubyMethod("clear")]
  178. public static IList Clear(IList/*!*/ self) {
  179. self.Clear();
  180. return self;
  181. }
  182. [RubyMethod("to_a")]
  183. [RubyMethod("to_ary")]
  184. public static RubyArray/*!*/ ToArray(IList/*!*/ self) {
  185. RubyArray list = new RubyArray(self.Count);
  186. foreach (object item in self) {
  187. list.Add(item);
  188. }
  189. return list;
  190. }
  191. #endregion
  192. #region *, +, concat, product
  193. [RubyMethod("*")]
  194. public static IList/*!*/ Repeat(UnaryOpStorage/*!*/ allocateStorage, IList/*!*/ self, int repeat) {
  195. if (repeat < 0) {
  196. throw RubyExceptions.CreateArgumentError("negative argument");
  197. }
  198. IList result = CreateResultArray(allocateStorage, self);
  199. RubyArray array = result as RubyArray;
  200. if (array != null) {
  201. array.AddCapacity(self.Count * repeat);
  202. }
  203. for (int i = 0; i < repeat; ++i) {
  204. AddRange(result, self);
  205. }
  206. allocateStorage.Context.TaintObjectBy(result, self);
  207. return result;
  208. }
  209. [RubyMethod("*")]
  210. public static MutableString Repeat(JoinConversionStorage/*!*/ conversions, IList/*!*/ self, [NotNull]MutableString/*!*/ separator) {
  211. return Join(conversions, self, separator);
  212. }
  213. [RubyMethod("*")]
  214. public static object Repeat(UnaryOpStorage/*!*/ allocateStorage, JoinConversionStorage/*!*/ conversions,
  215. IList/*!*/ self, [DefaultProtocol, NotNull]Union<MutableString, int> repeat) {
  216. if (repeat.IsFixnum()) {
  217. return Repeat(allocateStorage, self, repeat.Fixnum());
  218. } else {
  219. return Repeat(conversions, self, repeat.String());
  220. }
  221. }
  222. [RubyMethod("+")]
  223. public static RubyArray/*!*/ Concatenate(IList/*!*/ self, [DefaultProtocol, NotNull]IList/*!*/ other) {
  224. RubyArray result = new RubyArray(self.Count + other.Count);
  225. AddRange(result, self);
  226. AddRange(result, other);
  227. return result;
  228. }
  229. [RubyMethod("concat")]
  230. public static IList/*!*/ Concat(IList/*!*/ self, [DefaultProtocol, NotNull]IList/*!*/ other) {
  231. AddRange(self, other);
  232. return self;
  233. }
  234. [RubyMethod("-")]
  235. public static RubyArray/*!*/ Difference(UnaryOpStorage/*!*/ hashStorage, BinaryOpStorage/*!*/ eqlStorage,
  236. IList/*!*/ self, [DefaultProtocol, NotNull]IList/*!*/ other) {
  237. RubyArray result = new RubyArray();
  238. // cost: (|self| + |other|) * (hash + eql) + dict
  239. var remove = new Dictionary<object, bool>(new EqualityComparer(hashStorage, eqlStorage));
  240. bool removeNull = false;
  241. foreach (var item in other) {
  242. if (item != null) {
  243. remove[item] = true;
  244. } else {
  245. removeNull = true;
  246. }
  247. }
  248. foreach (var item in self) {
  249. if (!(item != null ? remove.ContainsKey(item) : removeNull)) {
  250. result.Add(item);
  251. }
  252. }
  253. return result;
  254. }
  255. internal static int IndexOf(CallSite<Func<CallSite, object, object, object>>/*!*/ equalitySite, IList/*!*/ self, object item) {
  256. for (int i = 0; i < self.Count; i++) {
  257. if (Protocols.IsTrue(equalitySite.Target(equalitySite, item, self[i]))) {
  258. return i;
  259. }
  260. }
  261. return -1;
  262. }
  263. [RubyMethod("product")]
  264. public static RubyArray/*!*/ Product(IList/*!*/ self, [DefaultProtocol, NotNullItems]params IList/*!*/[]/*!*/ arrays) {
  265. var result = new RubyArray();
  266. if (self.Count == 0) {
  267. return result;
  268. }
  269. for (int i = 0; i < arrays.Length; i++) {
  270. if (arrays[i].Count == 0) {
  271. return result;
  272. }
  273. }
  274. int[] indices = new int[1 + arrays.Length];
  275. while (true) {
  276. var current = new RubyArray(indices.Length);
  277. for (int i = 0; i < indices.Length; i++) {
  278. current[i] = GetNth(i, self, arrays)[indices[i]];
  279. }
  280. result.Add(current);
  281. // increment indices:
  282. for (int i = indices.Length - 1; i >= 0; i--) {
  283. int newIndex = indices[i] + 1;
  284. if (newIndex < GetNth(i, self, arrays).Count) {
  285. indices[i] = newIndex;
  286. break;
  287. } else if (i > 0) {
  288. indices[i] = 0;
  289. } else {
  290. return result;
  291. }
  292. }
  293. }
  294. }
  295. private static IList GetNth(int n, IList first, IList[] items) {
  296. return (n == 0) ? first : items[n - 1];
  297. }
  298. #endregion
  299. #region ==, <=>, eql?, hash
  300. [RubyMethod("==")]
  301. public static bool Equals(RespondToStorage/*!*/ respondTo, BinaryOpStorage/*!*/ equals, IList/*!*/ self, object other) {
  302. return Protocols.RespondTo(respondTo, other, "to_ary") && Protocols.IsEqual(equals, other, self);
  303. }
  304. [MultiRuntimeAware]
  305. private static RubyUtils.RecursionTracker _EqualsTracker = new RubyUtils.RecursionTracker();
  306. [RubyMethod("==")]
  307. public static bool Equals(BinaryOpStorage/*!*/ equals, IList/*!*/ self, [NotNull]IList/*!*/ other) {
  308. Assert.NotNull(self, other);
  309. if (ReferenceEquals(self, other)) {
  310. return true;
  311. }
  312. if (self.Count != other.Count) {
  313. return false;
  314. }
  315. using (IDisposable handleSelf = _EqualsTracker.TrackObject(self), handleOther = _EqualsTracker.TrackObject(other)) {
  316. if (handleSelf == null && handleOther == null) {
  317. // both arrays went recursive:
  318. return true;
  319. }
  320. var site = equals.GetCallSite("==");
  321. for (int i = 0; i < self.Count; ++i) {
  322. if (!Protocols.IsEqual(site, self[i], other[i])) {
  323. return false;
  324. }
  325. }
  326. }
  327. return true;
  328. }
  329. [MultiRuntimeAware]
  330. private static RubyUtils.RecursionTracker _ComparisonTracker = new RubyUtils.RecursionTracker();
  331. [RubyMethod("<=>")]
  332. public static object Compare(BinaryOpStorage/*!*/ comparisonStorage, ConversionStorage<IList>/*!*/ toAry, IList/*!*/ self, object other) {
  333. IList otherArray = Protocols.TryCastToArray(toAry, other);
  334. return (otherArray != null) ? Compare(comparisonStorage, self, otherArray) : null;
  335. }
  336. [RubyMethod("<=>")]
  337. public static object Compare(BinaryOpStorage/*!*/ comparisonStorage, IList/*!*/ self, [NotNull]IList/*!*/ other) {
  338. using (IDisposable handleSelf = _ComparisonTracker.TrackObject(self), handleOther = _ComparisonTracker.TrackObject(other)) {
  339. if (handleSelf == null && handleOther == null) {
  340. // both arrays went recursive:
  341. return ScriptingRuntimeHelpers.Int32ToObject(0);
  342. }
  343. int limit = Math.Min(self.Count, other.Count);
  344. var compare = comparisonStorage.GetCallSite("<=>");
  345. for (int i = 0; i < limit; i++) {
  346. object result = compare.Target(compare, self[i], other[i]);
  347. if (!(result is int) || (int)result != 0) {
  348. return result;
  349. }
  350. }
  351. return ScriptingRuntimeHelpers.Int32ToObject(Math.Sign(self.Count - other.Count));
  352. }
  353. }
  354. [RubyMethod("eql?")]
  355. public static bool HashEquals(BinaryOpStorage/*!*/ eqlStorage, IList/*!*/ self, object other) {
  356. return RubyArray.Equals(eqlStorage, self, other);
  357. }
  358. [RubyMethod("hash")]
  359. public static int GetHashCode(UnaryOpStorage/*!*/ hashStorage, ConversionStorage<int>/*!*/ fixnumCast, IList/*!*/ self) {
  360. return RubyArray.GetHashCode(hashStorage, fixnumCast, self);
  361. }
  362. #endregion
  363. #region slice, [], at
  364. [RubyMethod("[]")]
  365. [RubyMethod("slice")]
  366. public static object GetElement(IList/*!*/ self, [DefaultProtocol]int index) {
  367. return InRangeNormalized(self, ref index) ? self[index] : null;
  368. }
  369. [RubyMethod("[]")]
  370. [RubyMethod("slice")]
  371. public static IList GetElements(UnaryOpStorage/*!*/ allocateStorage,
  372. IList/*!*/ self, [DefaultProtocol]int index, [DefaultProtocol]int count) {
  373. if (!NormalizeRange(self.Count, ref index, ref count)) {
  374. return null;
  375. }
  376. return GetResultRange(allocateStorage, self, index, count);
  377. }
  378. [RubyMethod("[]")]
  379. [RubyMethod("slice")]
  380. public static IList GetElements(ConversionStorage<int>/*!*/ fixnumCast, UnaryOpStorage/*!*/ allocateStorage,
  381. IList/*!*/ self, [NotNull]Range/*!*/ range) {
  382. int start, count;
  383. if (!NormalizeRange(fixnumCast, self.Count, range, out start, out count)) {
  384. return null;
  385. }
  386. return count < 0 ? CreateResultArray(allocateStorage, self) : GetElements(allocateStorage, self, start, count);
  387. }
  388. [RubyMethod("at")]
  389. public static object At(IList/*!*/ self, [DefaultProtocol]int index) {
  390. return GetElement(self, index);
  391. }
  392. #endregion
  393. #region []=
  394. public static void ExpandList(IList/*!*/ list, int index) {
  395. int diff = index - list.Count;
  396. for (int i = 0; i < diff; i++) {
  397. list.Add(null);
  398. }
  399. }
  400. public static void OverwriteOrAdd(IList/*!*/ list, int index, object value) {
  401. if (index < list.Count) {
  402. list[index] = value;
  403. } else {
  404. list.Add(value);
  405. }
  406. }
  407. public static void DeleteItems(IList/*!*/ list, int index, int length) {
  408. if (index >= list.Count) {
  409. ExpandList(list, index);
  410. } else {
  411. // normalize for max length
  412. if (index + length > list.Count) {
  413. length = list.Count - index;
  414. }
  415. if (length == 0) {
  416. RequireNotFrozen(list);
  417. } else {
  418. RemoveRange(list, index, length);
  419. }
  420. }
  421. }
  422. [RubyMethod("[]=")]
  423. public static object SetElement(RubyArray/*!*/ self, [DefaultProtocol]int index, object value) {
  424. index = NormalizeIndexThrowIfNegative(self, index);
  425. if (index >= self.Count) {
  426. self.AddMultiple(index + 1 - self.Count, null);
  427. }
  428. return self[index] = value;
  429. }
  430. [RubyMethod("[]=")]
  431. public static object SetElement(IList/*!*/ self, [DefaultProtocol]int index, object value) {
  432. index = NormalizeIndexThrowIfNegative(self, index);
  433. if (index < self.Count) {
  434. self[index] = value;
  435. } else {
  436. ExpandList(self, index);
  437. self.Add(value);
  438. }
  439. return value;
  440. }
  441. [RubyMethod("[]=")]
  442. public static object SetElement(ConversionStorage<IList>/*!*/ arrayTryCast, IList/*!*/ self,
  443. [DefaultProtocol]int index, [DefaultProtocol]int length, object value) {
  444. if (length < 0) {
  445. throw RubyExceptions.CreateIndexError("negative length ({0})", length);
  446. }
  447. index = NormalizeIndexThrowIfNegative(self, index);
  448. IList valueAsList = value as IList;
  449. if (valueAsList == null) {
  450. valueAsList = Protocols.TryCastToArray(arrayTryCast, value);
  451. }
  452. if (valueAsList != null && valueAsList.Count == 0) {
  453. DeleteItems(self, index, length);
  454. } else {
  455. if (valueAsList == null) {
  456. Insert(self, index, value);
  457. if (length > 0) {
  458. RemoveRange(self, index + 1, Math.Min(length, self.Count - index - 1));
  459. }
  460. } else {
  461. if (value == self) {
  462. var newList = new object[self.Count];
  463. self.CopyTo(newList, 0);
  464. valueAsList = newList;
  465. }
  466. ExpandList(self, index);
  467. int limit = length > valueAsList.Count ? valueAsList.Count : length;
  468. for (int i = 0; i < limit; i++) {
  469. OverwriteOrAdd(self, index + i, valueAsList[i]);
  470. }
  471. if (length < valueAsList.Count) {
  472. InsertRange(self, index + limit, valueAsList, limit, valueAsList.Count - limit);
  473. } else {
  474. RemoveRange(self, index + limit, Math.Min(length - valueAsList.Count, self.Count - (index + limit)));
  475. }
  476. }
  477. }
  478. return value;
  479. }
  480. [RubyMethod("[]=")]
  481. public static object SetElement(ConversionStorage<IList>/*!*/ arrayTryCast, ConversionStorage<int>/*!*/ fixnumCast,
  482. IList/*!*/ self, [NotNull]Range/*!*/ range, object value) {
  483. int start, count;
  484. RangeToStartAndCount(fixnumCast, range, self.Count, out start, out count);
  485. return SetElement(arrayTryCast, self, start, count, value);
  486. }
  487. private static void RangeToStartAndCount(ConversionStorage<int>/*!*/ fixnumCast, Range/*!*/ range, int length, out int start, out int count) {
  488. start = Protocols.CastToFixnum(fixnumCast, range.Begin);
  489. int end = Protocols.CastToFixnum(fixnumCast, range.End);
  490. start = start < 0 ? start + length : start;
  491. if (start < 0) {
  492. throw RubyExceptions.CreateRangeError("{0}..{1} out of range", start, end);
  493. }
  494. end = end < 0 ? end + length : end;
  495. count = Math.Max(range.ExcludeEnd ? end - start : end - start + 1, 0);
  496. }
  497. #endregion
  498. #region &, |
  499. [RubyMethod("&")]
  500. public static RubyArray/*!*/ Intersection(UnaryOpStorage/*!*/ hashStorage, BinaryOpStorage/*!*/ eqlStorage,
  501. IList/*!*/ self, [DefaultProtocol]IList/*!*/ other) {
  502. Dictionary<object, bool> items = new Dictionary<object, bool>(new EqualityComparer(hashStorage, eqlStorage));
  503. RubyArray result = new RubyArray();
  504. // first get the items in the RHS
  505. foreach (object item in other) {
  506. items[item] = true;
  507. }
  508. // now, go through the items in the LHS, adding ones that were also in the RHS
  509. // this ensures that we return the items in the correct order
  510. foreach (object item in self) {
  511. if (items.Remove(item)) {
  512. result.Add(item);
  513. if (items.Count == 0) {
  514. break; // all done
  515. }
  516. }
  517. }
  518. return result;
  519. }
  520. private static void AddUniqueItems(IList/*!*/ list, IList/*!*/ result, Dictionary<object, bool> seen, ref bool nilSeen) {
  521. foreach (object item in list) {
  522. if (item == null) {
  523. if (!nilSeen) {
  524. nilSeen = true;
  525. result.Add(null);
  526. }
  527. continue;
  528. }
  529. if (!seen.ContainsKey(item)) {
  530. seen.Add(item, true);
  531. result.Add(item);
  532. }
  533. }
  534. }
  535. [RubyMethod("|")]
  536. public static RubyArray/*!*/ Union(UnaryOpStorage/*!*/ hashStorage, BinaryOpStorage/*!*/ eqlStorage,
  537. IList/*!*/ self, [DefaultProtocol]IList other) {
  538. var seen = new Dictionary<object, bool>(new EqualityComparer(hashStorage, eqlStorage));
  539. bool nilSeen = false;
  540. var result = new RubyArray();
  541. // Union merges the two arrays, removing duplicates
  542. AddUniqueItems(self, result, seen, ref nilSeen);
  543. AddUniqueItems(other, result, seen, ref nilSeen);
  544. return result;
  545. }
  546. #endregion
  547. #region assoc, rassoc
  548. public static IList GetContainerOf(BinaryOpStorage/*!*/ equals, IList list, int index, object item) {
  549. foreach (object current in list) {
  550. IList subArray = current as IList;
  551. if (subArray != null && subArray.Count > index) {
  552. if (Protocols.IsEqual(equals, subArray[index], item)) {
  553. return subArray;
  554. }
  555. }
  556. }
  557. return null;
  558. }
  559. [RubyMethod("assoc")]
  560. public static IList GetContainerOfFirstItem(BinaryOpStorage/*!*/ equals, IList/*!*/ self, object item) {
  561. return GetContainerOf(equals, self, 0, item);
  562. }
  563. [RubyMethod("rassoc")]
  564. public static IList/*!*/ GetContainerOfSecondItem(BinaryOpStorage/*!*/ equals, IList/*!*/ self, object item) {
  565. return GetContainerOf(equals, self, 1, item);
  566. }
  567. #endregion
  568. #region collect!, map!, compact, compact!
  569. [RubyMethod("collect!")]
  570. [RubyMethod("map!")]
  571. public static object CollectInPlace(BlockParam collector, IList/*!*/ self) {
  572. return (collector != null) ? CollectInPlaceImpl(collector, self) : new Enumerator((_, block) => CollectInPlaceImpl(block, self));
  573. }
  574. private static object CollectInPlaceImpl(BlockParam/*!*/ collector, IList/*!*/ self) {
  575. int i = 0;
  576. while (i < self.Count) {
  577. object result;
  578. if (collector.Yield(self[i], out result)) {
  579. return result;
  580. }
  581. self[i] = result;
  582. i++;
  583. }
  584. return self;
  585. }
  586. [RubyMethod("compact")]
  587. public static IList/*!*/ Compact(UnaryOpStorage/*!*/ allocateStorage, IList/*!*/ self) {
  588. IList result = CreateResultArray(allocateStorage, self);
  589. foreach (object item in self) {
  590. if (item != null) {
  591. result.Add(item);
  592. }
  593. }
  594. allocateStorage.Context.TaintObjectBy(result, self);
  595. return result;
  596. }
  597. [RubyMethod("compact!")]
  598. public static IList CompactInPlace(IList/*!*/ self) {
  599. RequireNotFrozen(self);
  600. bool changed = false;
  601. int i = 0;
  602. while (i < self.Count) {
  603. if (self[i] == null) {
  604. changed = true;
  605. self.RemoveAt(i);
  606. } else {
  607. i++;
  608. }
  609. }
  610. return changed ? self : null;
  611. }
  612. #endregion
  613. #region delete, delete_at, reject, reject!
  614. public static bool Remove(BinaryOpStorage/*!*/ equals, IList/*!*/ self, object item) {
  615. int i = 0;
  616. bool removed = false;
  617. while (i < self.Count) {
  618. if (Protocols.IsEqual(equals, self[i], item)) {
  619. self.RemoveAt(i);
  620. removed = true;
  621. } else {
  622. i++;
  623. }
  624. }
  625. return removed;
  626. }
  627. [RubyMethod("delete")]
  628. public static object Delete(BinaryOpStorage/*!*/ equals, IList/*!*/ self, object item) {
  629. return Remove(equals, self, item) ? item : null;
  630. }
  631. [RubyMethod("delete")]
  632. public static object Delete(BinaryOpStorage/*!*/ equals, BlockParam block, IList/*!*/ self, object item) {
  633. bool removed = Remove(equals, self, item);
  634. if (!removed && block != null) {
  635. object result;
  636. block.Yield(out result);
  637. return result;
  638. }
  639. return removed ? item : null;
  640. }
  641. [RubyMethod("delete_at")]
  642. public static object DeleteAt(IList/*!*/ self, [DefaultProtocol]int index) {
  643. index = index < 0 ? index + self.Count : index;
  644. if (index < 0 || index >= self.Count) {
  645. return null;
  646. }
  647. object result = GetElement(self, index);
  648. self.RemoveAt(index);
  649. return result;
  650. }
  651. [RubyMethod("delete_if")]
  652. public static object DeleteIf(BlockParam block, IList/*!*/ self) {
  653. return (block != null) ? DeleteIfImpl(block, self) : new Enumerator((_, innerBlock) => DeleteIfImpl(innerBlock, self));
  654. }
  655. private static object DeleteIfImpl(BlockParam/*!*/ block, IList/*!*/ self) {
  656. bool changed, jumped;
  657. DeleteIf(block, self, out changed, out jumped);
  658. return self;
  659. }
  660. [RubyMethod("reject!")]
  661. public static object RejectInPlace(BlockParam block, IList/*!*/ self) {
  662. return (block != null) ? RejectInPlaceImpl(block, self) : new Enumerator((_, innerBlock) => RejectInPlaceImpl(innerBlock, self));
  663. }
  664. private static object RejectInPlaceImpl(BlockParam/*!*/ block, IList/*!*/ self) {
  665. bool changed, jumped;
  666. object result = DeleteIf(block, self, out changed, out jumped);
  667. return jumped ? result : changed ? self : null;
  668. }
  669. [RubyMethod("reject")]
  670. public static object Reject(CallSiteStorage<EachSite>/*!*/ each, UnaryOpStorage/*!*/ allocate,
  671. BlockParam predicate, IList/*!*/ self) {
  672. return (predicate != null) ? RejectImpl(each, allocate, predicate, self) : new Enumerator((_, block) => RejectImpl(each, allocate, block, self));
  673. }
  674. private static object RejectImpl(CallSiteStorage<EachSite>/*!*/ each, UnaryOpStorage/*!*/ allocate,
  675. BlockParam/*!*/ predicate, IList/*!*/ self) {
  676. IList result = CreateResultArray(allocate, self);
  677. for (int i = 0; i < self.Count; i++) {
  678. object item = self[i];
  679. object blockResult;
  680. if (predicate.Yield(item, out blockResult)) {
  681. return blockResult;
  682. }
  683. if (RubyOps.IsFalse(blockResult)) {
  684. result.Add(item);
  685. }
  686. }
  687. return result;
  688. }
  689. private static object DeleteIf(BlockParam/*!*/ block, IList/*!*/ self, out bool changed, out bool jumped) {
  690. Assert.NotNull(block, self);
  691. changed = false;
  692. jumped = false;
  693. RequireNotFrozen(self);
  694. // TODO: if block jumps the array is not modified:
  695. int i = 0;
  696. while (i < self.Count) {
  697. object result;
  698. if (block.Yield(self[i], out result)) {
  699. jumped = true;
  700. return result;
  701. }
  702. if (RubyOps.IsTrue(result)) {
  703. changed = true;
  704. self.RemoveAt(i);
  705. } else {
  706. i++;
  707. }
  708. }
  709. return null;
  710. }
  711. #endregion
  712. #region each, each_index, reverse_each
  713. [RubyMethod("each")]
  714. public static Enumerator/*!*/ Each(IList/*!*/ self) {
  715. return new Enumerator((_, block) => Each(block, self));
  716. }
  717. [RubyMethod("each")]
  718. public static object Each([NotNull]BlockParam/*!*/ block, IList/*!*/ self) {
  719. for (int i = 0; i < self.Count; i++) {
  720. object result;
  721. if (block.Yield(self[i], out result)) {
  722. return result;
  723. }
  724. }
  725. return self;
  726. }
  727. [RubyMethod("each_index")]
  728. public static Enumerator/*!*/ EachIndex(IList/*!*/ self) {
  729. return new Enumerator((_, block) => EachIndex(block, self));
  730. }
  731. [RubyMethod("each_index")]
  732. public static object EachIndex([NotNull]BlockParam/*!*/ block, IList/*!*/ self) {
  733. int i = 0;
  734. while (i < self.Count) {
  735. object result;
  736. if (block.Yield(ScriptingRuntimeHelpers.Int32ToObject(i), out result)) {
  737. return result;
  738. }
  739. i++;
  740. }
  741. return self;
  742. }
  743. [RubyMethod("reverse_each")]
  744. public static Enumerator/*!*/ ReverseEach(RubyArray/*!*/ self) {
  745. return new Enumerator((_, block) => ReverseEach(block, self));
  746. }
  747. [RubyMethod("reverse_each")]
  748. public static object ReverseEach([NotNull]BlockParam/*!*/ block, RubyArray/*!*/ self) {
  749. foreach (int index in IListOps.ReverseEnumerateIndexes(self)) {
  750. object result;
  751. if (block.Yield(self[index], out result)) {
  752. return result;
  753. }
  754. }
  755. return self;
  756. }
  757. #endregion
  758. #region fetch
  759. [RubyMethod("fetch")]
  760. public static object Fetch(
  761. ConversionStorage<int>/*!*/ fixnumCast,
  762. BlockParam outOfRangeValueProvider,
  763. IList/*!*/ list,
  764. object/*!*/ index,
  765. [Optional]object defaultValue) {
  766. int convertedIndex = Protocols.CastToFixnum(fixnumCast, index);
  767. if (InRangeNormalized(list, ref convertedIndex)) {
  768. return list[convertedIndex];
  769. }
  770. if (outOfRangeValueProvider != null) {
  771. if (defaultValue != Missing.Value) {
  772. fixnumCast.Context.ReportWarning("block supersedes default value argument");
  773. }
  774. object result;
  775. outOfRangeValueProvider.Yield(index, out result);
  776. return result;
  777. }
  778. if (defaultValue == Missing.Value) {
  779. throw RubyExceptions.CreateIndexError("index {0} out of array", convertedIndex);
  780. }
  781. return defaultValue;
  782. }
  783. #endregion
  784. #region fill
  785. [RubyMethod("fill")]
  786. public static IList/*!*/ Fill(IList/*!*/ self, object obj, [DefaultParameterValue(0)]int start) {
  787. // Note: Array#fill(obj, start) is not equivalent to Array#fill(obj, start, 0)
  788. // (as per MRI behavior, the latter can expand the array if start > length, but the former doesn't)
  789. start = Math.Max(0, NormalizeIndex(self, start));
  790. for (int i = start; i < self.Count; i++) {
  791. self[i] = obj;
  792. }
  793. return self;
  794. }
  795. [RubyMethod("fill")]
  796. public static IList/*!*/ Fill(IList/*!*/ self, object obj, int start, int length) {
  797. // Note: Array#fill(obj, start) is not equivalent to Array#fill(obj, start, 0)
  798. // (as per MRI behavior, the latter can expand the array if start > length, but the former doesn't)
  799. start = Math.Max(0, NormalizeIndex(self, start));
  800. ExpandList(self, Math.Min(start, start + length));
  801. for (int i = 0; i < length; i++) {
  802. OverwriteOrAdd(self, start + i, obj);
  803. }
  804. return self;
  805. }
  806. [RubyMethod("fill")]
  807. public static IList/*!*/ Fill(ConversionStorage<int>/*!*/ fixnumCast, IList/*!*/ self, object obj, object start, [DefaultParameterValue(null)]object length) {
  808. int startFixnum = (start == null) ? 0 : Protocols.CastToFixnum(fixnumCast, start);
  809. if (length == null) {
  810. return Fill(self, obj, startFixnum);
  811. } else {
  812. return Fill(self, obj, startFixnum, Protocols.CastToFixnum(fixnumCast, length));
  813. }
  814. }
  815. [RubyMethod("fill")]
  816. public static IList/*!*/ Fill(ConversionStorage<int>/*!*/ fixnumCast, IList/*!*/ self, object obj, [NotNull]Range/*!*/ range) {
  817. int begin = NormalizeIndex(self, Protocols.CastToFixnum(fixnumCast, range.Begin));
  818. int end = NormalizeIndex(self, Protocols.CastToFixnum(fixnumCast, range.End));
  819. int length = Math.Max(0, end - begin + (range.ExcludeEnd ? 0 : 1));
  820. return Fill(self, obj, begin, length);
  821. }
  822. [RubyMethod("fill")]
  823. public static object Fill([NotNull]BlockParam/*!*/ block, IList/*!*/ self, [DefaultParameterValue(0)]int start) {
  824. start = Math.Max(0, NormalizeIndex(self, start));
  825. for (int i = start; i < self.Count; i++) {
  826. object result;
  827. if (block.Yield(i, out result)) {
  828. return result;
  829. }
  830. self[i] = result;
  831. }
  832. return self;
  833. }
  834. [RubyMethod("fill")]
  835. public static object Fill([NotNull]BlockParam/*!*/ block, IList/*!*/ self, int start, int length) {
  836. start = Math.Max(0, NormalizeIndex(self, start));
  837. ExpandList(self, Math.Min(start, start + length));
  838. for (int i = start; i < start + length; i++) {
  839. object result;
  840. if (block.Yield(i, out result)) {
  841. return result;
  842. }
  843. OverwriteOrAdd(self, i, result);
  844. }
  845. return self;
  846. }
  847. [RubyMethod("fill")]
  848. public static object Fill(ConversionStorage<int>/*!*/ fixnumCast, [NotNull]BlockParam/*!*/ block, IList/*!*/ self, object start, [DefaultParameterValue(null)]object length) {
  849. int startFixnum = (start == null) ? 0 : Protocols.CastToFixnum(fixnumCast, start);
  850. if (length == null) {
  851. return Fill(block, self, startFixnum);
  852. } else {
  853. return Fill(block, self, startFixnum, Protocols.CastToFixnum(fixnumCast, length));
  854. }
  855. }
  856. [RubyMethod("fill")]
  857. public static object Fill(ConversionStorage<int>/*!*/ fixnumCast, [NotNull]BlockParam/*!*/ block, IList/*!*/ self, [NotNull]Range/*!*/ range) {
  858. int begin = NormalizeIndex(self, Protocols.CastToFixnum(fixnumCast, range.Begin));
  859. int end = NormalizeIndex(self, Protocols.CastToFixnum(fixnumCast, range.End));
  860. int length = Math.Max(0, end - begin + (range.ExcludeEnd ? 0 : 1));
  861. return Fill(block, self, begin, length);
  862. }
  863. #endregion
  864. #region first, last
  865. [RubyMethod("first")]
  866. public static object First(IList/*!*/ self) {
  867. return self.Count == 0 ? null : self[0];
  868. }
  869. [RubyMethod("first")]
  870. public static IList/*!*/ First(IList/*!*/ self, [DefaultProtocol]int count) {
  871. if (count < 0) {
  872. throw RubyExceptions.CreateArgumentError("negative array size (or size too big)");
  873. }
  874. if (count > self.Count) {
  875. count = self.Count;
  876. }
  877. return new RubyArray(self, 0, count);
  878. }
  879. [RubyMethod("last")]
  880. public static object Last(IList/*!*/ self) {
  881. return self.Count == 0 ? null : self[self.Count - 1];
  882. }
  883. [RubyMethod("last")]
  884. public static IList/*!*/ Last(IList/*!*/ self, [DefaultProtocol]int count) {
  885. if (count < 0) {
  886. throw RubyExceptions.CreateArgumentError("negative array size (or size too big)");
  887. }
  888. if (count > self.Count) {
  889. count = self.Count;
  890. }
  891. return new RubyArray(self, self.Count - count, count);
  892. }
  893. #endregion
  894. #region flatten, flatten!
  895. private static int IndexOfList(ConversionStorage<IList>/*!*/ tryToAry, IList/*!*/ list, int start, out IList listItem) {
  896. for (int i = start; i < list.Count; i++) {
  897. listItem = Protocols.TryCastToArray(tryToAry, list[i]);
  898. if (listItem != null) {
  899. return i;
  900. }
  901. }
  902. listItem = null;
  903. return -1;
  904. }
  905. /// <summary>
  906. /// Enumerates all items of the list recursively - if there are any items convertible to IList the items of that lists are enumerated as well.
  907. /// Returns null if there are no nested lists and so the list can be enumerated using a standard enumerator.
  908. /// </summary>
  909. public static IEnumerable<object> EnumerateRecursively(ConversionStorage<IList>/*!*/ tryToAry, IList/*!*/ list, int maxDepth, Func<IList, object>/*!*/ loopDetected) {
  910. if (maxDepth == 0) {
  911. return null;
  912. }
  913. IList nested;
  914. int nestedIndex = IndexOfList(tryToAry, list, 0, out nested);
  915. if (nestedIndex == -1) {
  916. return null;
  917. }
  918. return EnumerateRecursively(tryToAry, list, list, nested, nestedIndex, maxDepth, loopDetected);
  919. }
  920. private static IEnumerable<object>/*!*/ EnumerateRecursively(ConversionStorage<IList>/*!*/ tryToAry, IList/*!*/ root,
  921. IList/*!*/ list, IList/*!*/ nested, int nestedIndex, int maxDepth, Func<IList, object>/*!*/ loopDetected) {
  922. Debug.Assert(nested != null);
  923. Debug.Assert(nestedIndex != -1);
  924. if (maxDepth < 0) {
  925. maxDepth = Int32.MaxValue;
  926. }
  927. var worklist = new Stack<KeyValuePair<IList, int>>();
  928. var recursionPath = new HashSet<object>(ReferenceEqualityComparer<object>.Instance);
  929. recursionPath.Add(root);
  930. int start = 0;
  931. while (true) {
  932. // "list" is the list being visited by the current work item (there might be more work items visiting the same list)
  933. // "nestedIndex" is the index of "nested" in the "list"
  934. if (nestedIndex >= 0) {
  935. // push a work item that will process the items following the nested list:
  936. worklist.Push(new KeyValuePair<IList, int>(list, nestedIndex + 1));
  937. // yield items preceding the nested list:
  938. for (int i = start; i < nestedIndex; i++) {
  939. yield return list[i];
  940. }
  941. // push a workitem for the nested list:
  942. worklist.Push(new KeyValuePair<IList, int>(nested, 0));
  943. } else {
  944. // there is no nested list => yield all remaining items:
  945. for (int i = start; i < list.Count; i++) {
  946. yield return list[i];
  947. }
  948. }
  949. next:
  950. if (worklist.Count == 0) {
  951. break;
  952. }
  953. var workitem = worklist.Pop();
  954. list = workitem.Key;
  955. start = workitem.Value;
  956. // finishing nested list:
  957. if (start == list.Count) {
  958. recursionPath.Remove(list);
  959. goto next;
  960. }
  961. // starting nested list:
  962. if (start == 0 && recursionPath.Contains(list)) {
  963. yield return loopDetected(list);
  964. goto next;
  965. }
  966. // set the index to -1 if we would go deeper then we should:
  967. nestedIndex = (recursionPath.Count < maxDepth) ? IndexOfList(tryToAry, list, start, out nested) : -1;
  968. // starting nested list:
  969. if (start == 0 && nestedIndex != -1) {
  970. recursionPath.Add(list);
  971. }
  972. }
  973. }
  974. internal static IList/*!*/ Flatten(ConversionStorage<IList>/*!*/ tryToAry, IList/*!*/ list, int maxDepth, IList/*!*/ result) {
  975. var recEnum = EnumerateRecursively(tryToAry, list, maxDepth, (_) => {
  976. throw RubyExceptions.CreateArgumentError("tried to flatten recursive array");
  977. });
  978. if (recEnum != null) {
  979. foreach (var item in recEnum) {
  980. result.Add(item);
  981. }
  982. } else {
  983. AddRange(result, list);
  984. }
  985. return result;
  986. }
  987. [RubyMethod("flatten")]
  988. public static IList/*!*/ Flatten(UnaryOpStorage/*!*/ allocateStorage, ConversionStorage<IList>/*!*/ tryToAry, IList/*!*/ self,
  989. [DefaultProtocol, DefaultParameterValue(-1)] int maxDepth) {
  990. return Flatten(tryToAry, self, maxDepth, CreateResultArray(allocateStorage, self));
  991. }
  992. [RubyMethod("flatten!")]
  993. public static IList FlattenInPlace(ConversionStorage<IList>/*!*/ tryToAry, RubyArray/*!*/ self, [DefaultProtocol, DefaultParameterValue(-1)] int maxDepth) {
  994. self.RequireNotFrozen();
  995. return FlattenInPlace(tryToAry, (IList)self, maxDepth);
  996. }
  997. [RubyMethod("flatten!")]
  998. public static IList FlattenInPlace(ConversionStorage<IList>/*!*/ tryToAry, IList/*!*/ self, [DefaultProtocol, DefaultParameterValue(-1)] int maxDepth) {
  999. if (maxDepth == 0) {
  1000. return null;
  1001. }
  1002. IList nested;
  1003. int nestedIndex = IndexOfList(tryToAry, self, 0, out nested);
  1004. if (nestedIndex == -1) {
  1005. return null;
  1006. }
  1007. var remaining = new object[self.Count - nestedIndex];
  1008. for (int i = 0, j = nestedIndex; i < remaining.Length; i++) {
  1009. remaining[i] = self[j++];
  1010. }
  1011. bool isRecursive = false;
  1012. var recEnum = EnumerateRecursively(tryToAry, self, remaining, nested, 0, maxDepth, (rec) => {
  1013. isRecursive = true;
  1014. return rec;
  1015. });
  1016. // rewrite items following the first nested list (including the list):
  1017. int itemCount = nestedIndex;
  1018. foreach (var item in recEnum) {
  1019. if (itemCount < self.Count) {
  1020. self[itemCount] = item;
  1021. } else {
  1022. self.Add(item);
  1023. }
  1024. itemCount++;
  1025. }
  1026. // empty arrays can make the list shrink:
  1027. while (self.Count > itemCount) {
  1028. self.RemoveAt(self.Count - 1);
  1029. }
  1030. if (isRecursive) {
  1031. throw RubyExceptions.CreateArgumentError("tried to flatten recursive array");
  1032. }
  1033. return self;
  1034. }
  1035. #endregion
  1036. #region include?, find_index/index, rindex
  1037. [RubyMethod("include?")]
  1038. public static bool Include(BinaryOpStorage/*!*/ equals, IList/*!*/ self, object item) {
  1039. return FindIndex(equals, null, self, item) != null;
  1040. }
  1041. [RubyMethod("find_index")]
  1042. [RubyMethod("index")]
  1043. public static Enumerator/*!*/ GetFindIndexEnumerator(BlockParam predicate, IList/*!*/ self) {
  1044. Debug.Assert(predicate == null);
  1045. throw new NotImplementedError("TODO: find_index enumerator");
  1046. }
  1047. [RubyMethod("find_index")]
  1048. [RubyMethod("index")]
  1049. public static object FindIndex([NotNull]BlockParam/*!*/ predicate, IList/*!*/ self) {
  1050. for (int i = 0; i < self.Count; i++) {
  1051. object blockResult;
  1052. if (predicate.Yield(self[i], out blockResult)) {
  1053. return blockResult;
  1054. }
  1055. if (Protocols.IsTrue(blockResult)) {
  1056. return ScriptingRuntimeHelpers.Int32ToObject(i);
  1057. }
  1058. }
  1059. return null;
  1060. }
  1061. [RubyMethod("find_index")]
  1062. [RubyMethod("index")]
  1063. public static object FindIndex(BinaryOpStorage/*!*/ equals, BlockParam predicate, IList/*!*/ self, object value) {
  1064. if (predicate != null) {
  1065. equals.Context.ReportWarning("given block not used");
  1066. }
  1067. for (int i = 0; i < self.Count; i++) {
  1068. if (Protocols.IsEqual(equals, self[i], value)) {
  1069. return ScriptingRuntimeHelpers.Int32ToObject(i);
  1070. }
  1071. }
  1072. return null;
  1073. }
  1074. [RubyMethod("rindex")]
  1075. public static object ReverseIndex([NotNull]BlockParam/*!*/ predicate, IList/*!*/ self) {
  1076. foreach (int i in IListOps.ReverseEnumerateIndexes(self)) {
  1077. object blockResult;
  1078. if (predicate.Yield(self[i], out blockResult)) {
  1079. return blockResult;
  1080. }
  1081. if (Protocols.IsTrue(blockResult)) {
  1082. return ScriptingRuntimeHelpers.Int32ToObject(i);
  1083. }
  1084. }
  1085. return null;
  1086. }
  1087. [RubyMethod("rindex")]
  1088. public static object ReverseIndex(BinaryOpStorage/*!*/ equals, BlockParam predicate, IList/*!*/ self, object item) {
  1089. if (predicate != null) {
  1090. equals.Context.ReportWarning("given block not used");
  1091. }
  1092. foreach (int i in IListOps.ReverseEnumerateIndexes(self)) {
  1093. if (Protocols.IsEqual(equals, self[i], item)) {
  1094. return ScriptingRuntimeHelpers.Int32ToObject(i);
  1095. }
  1096. }
  1097. return null;
  1098. }
  1099. #endregion
  1100. #region indexes, indices, values_at
  1101. [RubyMethod("indexes")]
  1102. [RubyMethod("indices")]
  1103. public static object Indexes(ConversionStorage<int>/*!*/ fixnumCast,
  1104. UnaryOpStorage/*!*/ allocateStorage,
  1105. IList/*!*/ self, params object[]/*!*/ values) {
  1106. fixnumCast.Context.ReportWarning("Array#indexes and Array#indices are deprecated; use Array#values_at");
  1107. RubyArray result = new RubyArray();
  1108. for (int i = 0; i < values.Length; ++i) {
  1109. Range range = values[i] as Range;
  1110. if (range != null) {
  1111. IList fragment = GetElements(fixnumCast, allocateStorage, self, range);
  1112. if (fragment != null) {
  1113. result.Add(fragment);
  1114. }
  1115. } else {
  1116. result.Add(GetElement(self, Protocols.CastToFixnum(fixnumCast, values[i])));
  1117. }
  1118. }
  1119. return result;
  1120. }
  1121. [RubyMethod("values_at")]
  1122. public static RubyArray/*!*/ ValuesAt(ConversionStorage<int>/*!*/ fixnumCast,
  1123. UnaryOpStorage/*!*/ allocateStorage,
  1124. IList/*!*/ self, params object[]/*!*/ values) {
  1125. RubyArray result = new RubyArray();
  1126. for (int i = 0; i < values.Length; i++) {
  1127. Range range = values[i] as Range;
  1128. if (range != null) {
  1129. int start, count;
  1130. if (!NormalizeRange(fixnumCast, self.Count, range, out start, out count)) {
  1131. continue;
  1132. }
  1133. if (count > 0) {
  1134. result.AddRange(GetElements(allocateStorage, self, start, count));
  1135. if (start + count >= self.Count) {
  1136. result.Add(null);
  1137. }
  1138. }
  1139. } else {
  1140. result.Add(GetElement(self, Protocols.CastToFixnum(fixnumCast, values[i])));
  1141. }
  1142. }
  1143. return result;
  1144. }
  1145. #endregion
  1146. #region join, to_s, inspect
  1147. private static void JoinRecursive(JoinConversionStorage/*!*/ conversions, IList/*!*/ list, List<MutableString/*!*/>/*!*/ parts,
  1148. ref bool? isBinary, ref Dictionary<object, bool> seen) {
  1149. foreach (object item in list) {
  1150. if (item == null) {
  1151. parts.Add(null);
  1152. continue;
  1153. }
  1154. IList listItem = conversions.ToAry.Target(conversions.ToAry, item);
  1155. if (listItem != null) {
  1156. bool _;
  1157. if (ReferenceEquals(listItem, list) || seen != null && seen.TryGetValue(listItem, out _)) {
  1158. throw RubyExceptions.CreateArgumentError("recursive array join");
  1159. }
  1160. if (seen == null) {
  1161. seen = new Dictionary<object, bool>(ReferenceEqualityComparer<object>.Instance);
  1162. }
  1163. seen.Add(listItem, true);
  1164. JoinRecursive(conversions, listItem, parts, ref isBinary, ref seen);
  1165. seen.Remove(listItem);
  1166. continue;
  1167. }
  1168. // try to_str first, then to_s:
  1169. MutableString strItem = conversions.ToStr.Target(conversions.ToStr, item) ?? conversions.ToS.Target(conversions.ToS, item);
  1170. parts.Add(strItem);
  1171. isBinary = isBinary.HasValue ? (isBinary | strItem.IsBinary) : strItem.IsBinary;
  1172. }
  1173. }
  1174. public static MutableString/*!*/ Join(JoinConversionStorage/*!*/ conversions, IList/*!*/ self, MutableString separator) {
  1175. var parts = new List<MutableString>(self.Count);
  1176. bool? isBinary = (separator != null) ? separator.IsBinary : (bool?)null;
  1177. Dictionary<object, bool> seen = null;
  1178. // build a list of strings to join:
  1179. JoinRecursive(conversions, self, parts, ref isBinary, ref seen);
  1180. if (parts.Count == 0) {
  1181. return MutableString.CreateEmpty();
  1182. }
  1183. if (separator != null && separator.IsBinary != isBinary && !separator.IsAscii()) {
  1184. isBinary = true;
  1185. }
  1186. // calculate length:
  1187. MutableString any = separator;
  1188. int length = (separator != null) ? (isBinary.HasValue && isBinary.Value ? separator.GetByteCount() : separator.GetCharCount()) * (parts.Count - 1) : 0;
  1189. for (int i = 0, n = parts.Count; i < n; i++) {
  1190. var part = parts[i];
  1191. if (part != null) {
  1192. length += (isBinary.HasValue && isBinary.Value) ? part.GetByteCount() : part.GetCharCount();
  1193. if (any == null) {
  1194. any = part;
  1195. }
  1196. }
  1197. }
  1198. if (any == null) {
  1199. return MutableString.CreateEmpty();
  1200. }
  1201. var result = isBinary.HasValue && isBinary.Value ?
  1202. MutableString.CreateBinary(length, any.Encoding) :
  1203. MutableString.CreateMutable(length, any.Encoding);
  1204. for (int i = 0, n = parts.Count; i < n; i++) {
  1205. var part = parts[i];
  1206. if (separator != null && i > 0) {
  1207. result.Append(separator);
  1208. }
  1209. if (part != null) {
  1210. result.Append(part);
  1211. result.TaintBy(part);
  1212. }
  1213. }
  1214. if (separator != null) {
  1215. result.TaintBy(separator);
  1216. }
  1217. if (!result.IsTainted || !result.IsUntrusted) {
  1218. result.TaintBy(self, conversions.Context);
  1219. }
  1220. return result;
  1221. }
  1222. [RubyMethod("join")]
  1223. public static MutableString/*!*/ Join(JoinConversionStorage/*!*/ conversions, IList/*!*/ self) {
  1224. return Join(conversions, self, conversions.Context.ItemSeparator);
  1225. }
  1226. [RubyMethod("join")]
  1227. public static MutableString/*!*/ JoinWithLazySeparatorConversion(
  1228. JoinConversionStorage/*!*/ conversions,
  1229. ConversionStorage<MutableString>/*!*/ toStr,
  1230. IList/*!*/ self, object separator) {
  1231. if (self.Count == 0) {
  1232. return MutableString.CreateEmpty();
  1233. }
  1234. return Join(conversions, self, separator != null ? Protocols.CastToString(toStr, separator) : null);
  1235. }
  1236. [RubyMethod("to_s")]
  1237. [RubyMethod("inspect")]
  1238. public static MutableString/*!*/ Inspect(RubyContext/*!*/ context, IList/*!*/ self) {
  1239. using (IDisposable handle = RubyUtils.InfiniteInspectTracker.TrackObject(self)) {
  1240. if (handle == null) {
  1241. return MutableString.CreateAscii("[...]");
  1242. }
  1243. MutableString str = MutableString.CreateMutable(RubyEncoding.Binary);
  1244. str.Append('[');
  1245. bool first = true;
  1246. foreach (object obj in self) {
  1247. if (first) {
  1248. first = false;
  1249. } else {
  1250. str.Append(", ");
  1251. }
  1252. str.Append(context.Inspect(obj));
  1253. }
  1254. str.Append(']');
  1255. return str;
  1256. }
  1257. }
  1258. #endregion
  1259. #region length, size, empty?, nitems
  1260. [RubyMethod("length")]
  1261. [RubyMethod("size")]
  1262. [RubyMethod("count")]
  1263. public static int Length(IList/*!*/ self) {
  1264. return self.Count;
  1265. }
  1266. [RubyMethod("empty?")]
  1267. [RubyMethod("none?")]
  1268. public static bool Empty(IList/*!*/ self) {
  1269. return self.Count == 0;
  1270. }
  1271. [RubyMethod("nitems")]
  1272. public static int NumberOfNonNilItems(IList/*!*/ self) {
  1273. int count = 0;
  1274. foreach (object obj in self) {
  1275. if (obj != null) {
  1276. count++;
  1277. }
  1278. }
  1279. return count;
  1280. }
  1281. #endregion
  1282. #region insert, push, pop, shift, unshift, <<
  1283. [RubyMethod("insert")]
  1284. public static IList/*!*/ Insert(IList/*!*/ self, [DefaultProtocol]int index, params object[]/*!*/ args) {
  1285. if (args.Length == 0) {
  1286. var array = self as RubyArray;
  1287. if (array != null) {
  1288. array.RequireNotFrozen();
  1289. }
  1290. return self;
  1291. }
  1292. if (index == -1) {
  1293. AddRange(self, args);
  1294. return self;
  1295. }
  1296. index = index < 0 ? index + self.Count + 1 : index;
  1297. if (index < 0) {
  1298. throw RubyExceptions.CreateIndexError("index {0} out of array", index);
  1299. }
  1300. if (index >= self.Count) {
  1301. ExpandList(self, index);
  1302. AddRange(self, args);
  1303. return self;
  1304. }
  1305. InsertRange(self, index, args, 0, args.Length);
  1306. return self;
  1307. }
  1308. [RubyMethod("push")]
  1309. public static IList/*!*/ Push(IList/*!*/ self, params object[]/*!*/ values) {
  1310. AddRange(self, values);
  1311. return self;
  1312. }
  1313. [RubyMethod("pop")]
  1314. public static object Pop(IList/*!*/ self) {
  1315. if (self.Count == 0) {
  1316. return null;
  1317. }
  1318. object result = self[self.Count - 1];
  1319. self.RemoveAt(self.Count - 1);
  1320. return result;
  1321. }
  1322. [RubyMethod("pop")]
  1323. public static object Pop(RubyContext/*!*/ context, IList/*!*/ self, [DefaultProtocol]int count) {
  1324. RequireNotFrozen(self);
  1325. if (count < 0) {
  1326. throw RubyExceptions.CreateArgumentError("negative array size");
  1327. }
  1328. if (count == 0 || self.Count == 0) {
  1329. return new RubyArray();
  1330. }
  1331. var normalizedCount = count <= self.Count ? count : self.Count;
  1332. var index = self.Count - normalizedCount;
  1333. var result = new RubyArray(self, index, normalizedCount);
  1334. IListOps.RemoveRange(self, index, normalizedCount);
  1335. return result;
  1336. }
  1337. [RubyMethod("shift")]
  1338. public static object Shift(IList/*!*/ self) {
  1339. if (self.Count == 0) {
  1340. return null;
  1341. }
  1342. object result = self[0];
  1343. self.RemoveAt(0);
  1344. return result;
  1345. }
  1346. [RubyMethod("unshift")]
  1347. public static IList/*!*/ Unshift(IList/*!*/ self, object/*!*/ arg) {
  1348. self.Insert(0, arg);
  1349. return self;
  1350. }
  1351. [RubyMethod("unshift")]
  1352. public static IList/*!*/ Unshift(IList/*!*/ self, params object[]/*!*/ args) {
  1353. InsertRange(self, 0, args, 0, args.Length);
  1354. return self;
  1355. }
  1356. [RubyMethod("<<")]
  1357. public static IList/*!*/ Append(IList/*!*/ self, object value) {
  1358. self.Add(value);
  1359. return self;
  1360. }
  1361. #endregion
  1362. #region slice!
  1363. [RubyMethod("slice!")]
  1364. public static object SliceInPlace(IList/*!*/ self, [DefaultProtocol]int index) {
  1365. index = index < 0 ? index + self.Count : index;
  1366. if (index >= 0 && index < self.Count) {
  1367. object result = self[index];
  1368. DeleteElements(self, index, 1);
  1369. return result;
  1370. } else {
  1371. return null;
  1372. }
  1373. }
  1374. [RubyMethod("slice!")]
  1375. public static IList SliceInPlace(ConversionStorage<int>/*!*/ fixnumCast, UnaryOpStorage/*!*/ allocateStorage,
  1376. IList/*!*/ self, [NotNull]Range/*!*/ range) {
  1377. IList result = GetElements(fixnumCast, allocateStorage, self, range);
  1378. int start, count;
  1379. RangeToStartAndCount(fixnumCast, range, self.Count, out start, out count);
  1380. DeleteElements(self, start, count);
  1381. return result;
  1382. }
  1383. [RubyMethod("slice!")]
  1384. public static IList SliceInPlace(UnaryOpStorage/*!*/ allocateStorage,
  1385. IList/*!*/ self, [DefaultProtocol]int start, [DefaultProtocol]int length) {
  1386. IList result = GetElements(allocateStorage, self, start, length);
  1387. DeleteElements(self, start, length);
  1388. return result;
  1389. }
  1390. private static void DeleteElements(IList/*!*/ self, int start, int count) {
  1391. if (count < 0) {
  1392. throw RubyExceptions.CreateIndexError("negative length ({0})", count);
  1393. }
  1394. DeleteItems(self, NormalizeIndexThrowIfNegative(self, start), count);
  1395. }
  1396. #endregion
  1397. #region sort, sort!
  1398. [RubyMethod("sort")]
  1399. public static object Sort(UnaryOpStorage/*!*/ allocateStorage, ComparisonStorage/*!*/ comparisonStorage, BlockParam block, IList/*!*/ self) {
  1400. // TODO: this is not optimal because it makes an extra array copy
  1401. // (only affects sorting of .NET types, do we need our own quicksort?)
  1402. IList resultList = CreateResultArray(allocateStorage, self);
  1403. StrongBox<object> breakResult;
  1404. RubyArray result = ArrayOps.SortInPlace(comparisonStorage, block, ToArray(self), out breakResult);
  1405. if (breakResult == null) {
  1406. Replace(resultList, result);
  1407. return resultList;
  1408. } else {
  1409. return breakResult.Value;
  1410. }
  1411. }
  1412. [RubyMethod("sort!")]
  1413. public static object SortInPlace(ComparisonStorage/*!*/ comparisonStorage, BlockParam block, IList/*!*/ self) {
  1414. // this should always call ArrayOps.SortInPlace instead
  1415. Debug.Assert(!(self is RubyArray));
  1416. // TODO: this is not optimal because it makes an extra array copy
  1417. // (only affects sorting of .NET types, do we need our own quicksort?)
  1418. StrongBox<object> breakResult;
  1419. RubyArray result = ArrayOps.SortInPlace(comparisonStorage, block, ToArray(self), out breakResult);
  1420. if (breakResult == null) {
  1421. Replace(self, result);
  1422. return self;
  1423. } else {
  1424. return breakResult.Value;
  1425. }
  1426. }
  1427. #endregion
  1428. #region shuffle, shuffle!
  1429. [RubyMethod("shuffle")]
  1430. public static IList/*!*/ Shuffle(UnaryOpStorage/*!*/ allocateStorage, RubyArray/*!*/ self) {
  1431. IList result = CreateResultArray(allocateStorage, self);
  1432. if (self.Count == 0) {
  1433. return result;
  1434. }
  1435. RubyArray array = result as RubyArray;
  1436. if (array != null && array.Count < self.Count) {
  1437. array.AddCapacity(self.Count - array.Count);
  1438. }
  1439. var generator = allocateStorage.Context.RandomNumberGenerator;
  1440. result.Add(self[0]);
  1441. for (int i = 1; i < self.Count; i++) {
  1442. int j = generator.Next(i + 1);
  1443. result.Add((j < result.Count) ? result[j] : null);
  1444. result[j] = self[i];
  1445. }
  1446. return result;
  1447. }
  1448. [RubyMethod("shuffle!")]
  1449. public static RubyArray/*!*/ ShuffleInPlace(RubyContext/*!*/ context, RubyArray/*!*/ self) {
  1450. var generator = context.RandomNumberGenerator;
  1451. for (int i = self.Count - 1; i >= 0; i--) {
  1452. int j = generator.Next(i + 1);
  1453. object value = self[i];
  1454. self[i] = self[j];
  1455. self[j] = value;
  1456. }
  1457. return self;
  1458. }
  1459. #endregion
  1460. #region reverse, reverse!, transpose, uniq, uniq!
  1461. [RubyMethod("reverse")]
  1462. public static IList/*!*/ Reverse(UnaryOpStorage/*!*/ allocateStorage, IList/*!*/ self) {
  1463. IList reversedList = CreateResultArray(allocateStorage, self);
  1464. if (reversedList is RubyArray) {
  1465. (reversedList as RubyArray).AddCapacity(self.Count);
  1466. }
  1467. for (int i = 0; i < self.Count; i++) {
  1468. reversedList.Add(self[self.Count - i - 1]);
  1469. }
  1470. return reversedList;
  1471. }
  1472. [RubyMethod("reverse!")]
  1473. public static IList/*!*/ InPlaceReverse(IList/*!*/ self) {
  1474. int stop = self.Count / 2;
  1475. int last = self.Count - 1;
  1476. for (int i = 0; i < stop; i++) {
  1477. int swap = last - i;
  1478. object t = self[i];
  1479. self[i] = self[swap];
  1480. self[swap] = t;
  1481. }
  1482. return self;
  1483. }
  1484. [RubyMethod("transpose")]
  1485. public static RubyArray/*!*/ Transpose(ConversionStorage<IList>/*!*/ arrayCast, IList/*!*/ self) {
  1486. // Get the arrays. Note we need to check length as we go, so we call to_ary on all the
  1487. // arrays we encounter before the error (if any).
  1488. RubyArray result = new RubyArray();
  1489. for (int i = 0; i < self.Count; i++) {
  1490. IList list = Protocols.CastToArray(arrayCast, self[i]);
  1491. if (i == 0) {
  1492. // initialize the result
  1493. result.AddCapacity(list.Count);
  1494. for (int j = 0; j < list.Count; j++) {
  1495. result.Add(new RubyArray());
  1496. }
  1497. } else if (list.Count != result.Count) {
  1498. throw RubyExceptions.CreateIndexError("element size differs ({0} should be {1})", list.Count, result.Count);
  1499. }
  1500. // add items
  1501. Debug.Assert(list.Count == result.Count);
  1502. for (int j = 0; j < result.Count; j++) {
  1503. ((RubyArray)result[j]).Add(list[j]);
  1504. }
  1505. }
  1506. return result;
  1507. }
  1508. [RubyMethod("uniq")]
  1509. public static IList/*!*/ Unique(UnaryOpStorage/*!*/ allocateStorage, IList/*!*/ self) {
  1510. IList result = CreateResultArray(allocateStorage, self);
  1511. var seen = new Dictionary<object, bool>(allocateStorage.Context.EqualityComparer);
  1512. bool nilSeen = false;
  1513. AddUniqueItems(self, result, seen, ref nilSeen);
  1514. return result;
  1515. }
  1516. [RubyMethod("uniq!")]
  1517. public static IList UniqueSelf(UnaryOpStorage/*!*/ hashStorage, BinaryOpStorage/*!*/ eqlStorage, RubyArray/*!*/ self) {
  1518. self.RequireNotFrozen();
  1519. return UniqueSelf(hashStorage, eqlStorage, (IList)self);
  1520. }
  1521. [RubyMethod("uniq!")]
  1522. public static IList UniqueSelf(UnaryOpStorage/*!*/ hashStorage, BinaryOpStorage/*!*/ eqlStorage, IList/*!*/ self) {
  1523. var seen = new Dictionary<object, bool>(new EqualityComparer(hashStorage, eqlStorage));
  1524. bool nilSeen = false;
  1525. bool modified = false;
  1526. int i = 0;
  1527. while (i < self.Count) {
  1528. object key = self[i];
  1529. if (key != null && !seen.ContainsKey(key)) {
  1530. seen.Add(key, true);
  1531. i++;
  1532. } else if (key == null && !nilSeen) {
  1533. nilSeen = true;
  1534. i++;
  1535. } else {
  1536. self.RemoveAt(i);
  1537. modified = true;
  1538. }
  1539. }
  1540. return modified ? self : null;
  1541. }
  1542. #endregion
  1543. #region permutation, combination
  1544. internal sealed class PermutationEnumerator : IEnumerator {
  1545. private struct State {
  1546. public readonly int i, j;
  1547. public State(int i, int j) { this.i = i; this.j = j; }
  1548. }
  1549. private readonly IList/*!*/ _list;
  1550. private readonly int? _size;
  1551. public PermutationEnumerator(IList/*!*/ list, int? size) {
  1552. _size = size;
  1553. _list = list;
  1554. }
  1555. //
  1556. // rec = lambda do |i|
  1557. // # State "j < -1"
  1558. // if i == result.length
  1559. // yield result.dup
  1560. // return
  1561. // end
  1562. //
  1563. // j = i
  1564. // while j < values.length
  1565. // values[j], values[i] = values[i], values[j]
  1566. // result[i] = values[i]
  1567. // rec.(i + 1)
  1568. // # State "j >= 0"
  1569. // j += 1
  1570. // end
  1571. //
  1572. // while j > i
  1573. // j -= 1
  1574. // values[j], values[i] = values[i], values[j]
  1575. // end
  1576. // end
  1577. //
  1578. // rec.(0)
  1579. //
  1580. public object Each(RubyScope/*!*/ scope, BlockParam/*!*/ block) {
  1581. int size = _size ?? _list.Count;
  1582. if (size < 0 || size > _list.Count) {
  1583. return _list;
  1584. }
  1585. var result = new object[size];
  1586. var values = new object[_list.Count];
  1587. _list.CopyTo(values, 0);
  1588. var stack = new Stack<State>();
  1589. stack.Push(new State(0, -1));
  1590. while (stack.Count > 0) {
  1591. var entry = stack.Pop();
  1592. int i = entry.i;
  1593. int j = entry.j;
  1594. if (j < 0) {
  1595. if (i == result.Length) {
  1596. object blockResult;
  1597. if (block.Yield(RubyOps.MakeArrayN(result), out blockResult)) {
  1598. return blockResult;
  1599. }
  1600. } else {
  1601. result[i] = values[i];
  1602. stack.Push(new State(i, i));
  1603. stack.Push(new State(i + 1, -1));
  1604. }
  1605. } else {
  1606. j++;
  1607. if (j == values.Length) {
  1608. while (j > i) {
  1609. j--;
  1610. Xchg(values, i, j);
  1611. }
  1612. } else {
  1613. Xchg(values, i, j);
  1614. result[i] = values[i];
  1615. stack.Push(new State(i, j));
  1616. stack.Push(new State(i + 1, -1));
  1617. }
  1618. }
  1619. }
  1620. return _list;
  1621. }
  1622. private static void Xchg(object[]/*!*/ values, int i, int j) {
  1623. object item = values[j];
  1624. values[j] = values[i];
  1625. values[i] = item;
  1626. }
  1627. }
  1628. internal sealed class CombinationEnumerator : IEnumerator {
  1629. private struct State {
  1630. public readonly int i, j;
  1631. public readonly bool init;
  1632. public State(int i, int j, bool init) { this.i = i; this.j = j; this.init = init; }
  1633. }
  1634. private readonly IList/*!*/ _list;
  1635. private readonly int? _size;
  1636. public CombinationEnumerator(IList/*!*/ list, int? size) {
  1637. _size = size;
  1638. _list = list;
  1639. }
  1640. //
  1641. // rec = lambda do |i,j|
  1642. // # State "init"
  1643. // if j == result.length
  1644. // yield result.dup
  1645. // return
  1646. // end
  1647. //
  1648. // while i <= values.length - result.length + j
  1649. // result[j] = values[i]
  1650. // rec.(i + 1, j + 1)
  1651. // # State "!init"
  1652. // i += 1
  1653. // end
  1654. // end
  1655. //
  1656. // rec.(0, 0)
  1657. //
  1658. public object Each(RubyScope/*!*/ scope, BlockParam/*!*/ block) {
  1659. int size = _size ?? _list.Count;
  1660. if (size < 0 || size > _list.Count) {
  1661. return _list;
  1662. }
  1663. var result = new object[size];
  1664. var values = new object[_list.Count];
  1665. _list.CopyTo(values, 0);
  1666. var stack = new Stack<State>();
  1667. stack.Push(new State(0, 0, true));
  1668. while (stack.Count > 0) {
  1669. var entry = stack.Pop();
  1670. int i = entry.i;
  1671. int j = entry.j;
  1672. if (entry.init && j == result.Length) {
  1673. object blockResult;
  1674. if (block.Yield(RubyOps.MakeArrayN(result), out blockResult)) {
  1675. return blockResult;
  1676. }
  1677. } else {
  1678. if (!entry.init) {
  1679. i++;
  1680. }
  1681. if (i <= values.Length - result.Length + j) {
  1682. result[j] = values[i];
  1683. stack.Push(new State(i, j, false));
  1684. stack.Push(new State(i + 1, j + 1, true));
  1685. }
  1686. }
  1687. }
  1688. return _list;
  1689. }
  1690. }
  1691. [RubyMethod("permutation")]
  1692. public static object GetPermutations(BlockParam block, IList/*!*/ self, [DefaultProtocol, Optional]int? size) {
  1693. var enumerator = new PermutationEnumerator(self, size);
  1694. if (block == null) {
  1695. return new Enumerator(enumerator);
  1696. }
  1697. return enumerator.Each(null, block);
  1698. }
  1699. [RubyMethod("combination")]
  1700. public static object GetCombinations(BlockParam block, IList/*!*/ self, [DefaultProtocol, Optional]int? size) {
  1701. var enumerator = new CombinationEnumerator(self, size);
  1702. if (block == null) {
  1703. return new Enumerator(enumerator);
  1704. }
  1705. return enumerator.Each(null, block);
  1706. }
  1707. #endregion
  1708. }
  1709. }