PageRenderTime 197ms CodeModel.GetById 65ms RepoModel.GetById 1ms app.codeStats 0ms

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

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