PageRenderTime 21ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/DLR_Main/Languages/Ruby/Ruby/Builtins/RubyArray.cs

https://bitbucket.org/mdavid/dlr
C# | 660 lines | 512 code | 117 blank | 31 comment | 89 complexity | 3b2063e05a61642e4e8184607588f197 MD5 | raw file
  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 IronRuby.Runtime;
  20. using Microsoft.Scripting;
  21. using Microsoft.Scripting.Utils;
  22. using IronRuby.Runtime.Calls;
  23. using IronRuby.Runtime.Conversions;
  24. using System.Security.Permissions;
  25. using System.Runtime.Serialization;
  26. namespace IronRuby.Builtins {
  27. /// <summary>
  28. /// Implements Ruby array.
  29. /// Not thread safe (even when frozen).
  30. /// </summary>
  31. [Serializable]
  32. [DebuggerDisplay("{GetDebugView()}")]
  33. public partial class RubyArray : IList<object>, IList, IRubyObjectState, IDuplicable {
  34. private object[]/*!*/ _content;
  35. private int _start;
  36. private int _count;
  37. private uint _flags;
  38. private const uint IsFrozenFlag = 1;
  39. private const uint IsTaintedFlag = 2;
  40. private const uint IsUntrustedFlag = 4;
  41. [Conditional("DEBUG")]
  42. private void ObjectInvariant() {
  43. Debug.Assert(_start >= 0 && _count >= 0);
  44. Debug.Assert(_start + _count <= _content.Length);
  45. Debug.Assert(_content.Length == 0 || _content.Length >= Utils.MinListSize);
  46. }
  47. [Conditional("DEBUG")]
  48. internal void RequireNullEmptySlots() {
  49. for (int i = 0; i < _content.Length; i++) {
  50. if (i < _start || i >= _start + _count) {
  51. Debug.Assert(_content[i] == null);
  52. }
  53. }
  54. }
  55. #region Construction
  56. private RubyArray(object[]/*!*/ content, int count) {
  57. _content = content;
  58. _count = count;
  59. ObjectInvariant();
  60. }
  61. public RubyArray()
  62. : this(ArrayUtils.EmptyObjects, 0) {
  63. }
  64. public RubyArray(int capacity)
  65. : this(new object[Math.Max(capacity, Utils.MinListSize)], 0) {
  66. }
  67. public RubyArray(RubyArray/*!*/ items)
  68. : this(items, 0, items.Count) {
  69. }
  70. public RubyArray(RubyArray/*!*/ items, int start, int count)
  71. : this(count) {
  72. ContractUtils.RequiresNotNull(items, "items");
  73. ContractUtils.RequiresArrayRange(items.Count, start, count, "start", "count");
  74. AddVector(items._content, items._start + start, count);
  75. }
  76. public RubyArray(IList/*!*/ items)
  77. : this(items, 0, items.Count) {
  78. }
  79. public RubyArray(IList/*!*/ items, int start, int count)
  80. : this(count) {
  81. AddRange(items, start, count);
  82. }
  83. public RubyArray(ICollection/*!*/ items)
  84. : this(items.Count) {
  85. AddCollection(items);
  86. }
  87. public RubyArray(IEnumerable/*!*/ items)
  88. : this() {
  89. AddRange(items);
  90. }
  91. public static RubyArray/*!*/ Create(object item) {
  92. var content = new object[Utils.MinListSize];
  93. content[0] = item;
  94. return new RubyArray(content, 1);
  95. }
  96. /// <summary>
  97. /// Creates a blank instance of a RubyArray or its subclass given the Ruby class object.
  98. /// </summary>
  99. public static RubyArray/*!*/ CreateInstance(RubyClass/*!*/ rubyClass) {
  100. return (rubyClass.GetUnderlyingSystemType() == typeof(RubyArray)) ? new RubyArray() : new RubyArray.Subclass(rubyClass);
  101. }
  102. /// <summary>
  103. /// Creates an empty instance.
  104. /// Doesn't copy instance data.
  105. /// Preserves the class of the Array.
  106. /// </summary>
  107. public virtual RubyArray/*!*/ CreateInstance() {
  108. return new RubyArray();
  109. }
  110. object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) {
  111. var result = CreateInstance();
  112. context.CopyInstanceData(this, result, copySingletonMembers);
  113. return result;
  114. }
  115. #endregion
  116. #region Flags
  117. private void Mutate() {
  118. if ((_flags & IsFrozenFlag) != 0) {
  119. throw RubyExceptions.CreateObjectFrozenError();
  120. }
  121. }
  122. public bool IsTainted {
  123. get {
  124. return (_flags & IsTaintedFlag) != 0;
  125. }
  126. set {
  127. Mutate();
  128. _flags = (_flags & ~IsTaintedFlag) | (value ? IsTaintedFlag : 0);
  129. }
  130. }
  131. public bool IsUntrusted {
  132. get {
  133. return (_flags & IsUntrustedFlag) != 0;
  134. }
  135. set {
  136. Mutate();
  137. _flags = (_flags & ~IsUntrustedFlag) | (value ? IsUntrustedFlag : 0);
  138. }
  139. }
  140. public bool IsFrozen {
  141. get {
  142. return (_flags & IsFrozenFlag) != 0;
  143. }
  144. }
  145. void IRubyObjectState.Freeze() {
  146. Freeze();
  147. }
  148. public RubyArray/*!*/ Freeze() {
  149. _flags |= IsFrozenFlag;
  150. return this;
  151. }
  152. #endregion
  153. #region HashCode, Equality
  154. [MultiRuntimeAware]
  155. private static RubyUtils.RecursionTracker _HashTracker = new RubyUtils.RecursionTracker();
  156. public static int GetHashCode(UnaryOpStorage/*!*/ hashStorage, ConversionStorage<int>/*!*/ fixnumCast, IList/*!*/ self) {
  157. int hash = self.Count;
  158. using (IDisposable handle = _HashTracker.TrackObject(self)) {
  159. if (handle == null) {
  160. // hashing of recursive array
  161. return 0;
  162. }
  163. var hashSite = hashStorage.GetCallSite("hash");
  164. var toIntSite = fixnumCast.GetSite(ConvertToFixnumAction.Make(fixnumCast.Context));
  165. foreach (object item in self) {
  166. hash = (hash << 1) ^ toIntSite.Target(toIntSite, hashSite.Target(hashSite, item));
  167. }
  168. }
  169. return hash;
  170. }
  171. [MultiRuntimeAware]
  172. private static RubyUtils.RecursionTracker _EqualsTracker = new RubyUtils.RecursionTracker();
  173. public static bool Equals(BinaryOpStorage/*!*/ eqlStorage, IList/*!*/ self, object obj) {
  174. if (ReferenceEquals(self, obj)) {
  175. return true;
  176. }
  177. IList other = obj as IList;
  178. if (other == null || self.Count != other.Count) {
  179. return false;
  180. }
  181. using (IDisposable handleSelf = _EqualsTracker.TrackObject(self), handleOther = _EqualsTracker.TrackObject(other)) {
  182. if (handleSelf == null && handleOther == null) {
  183. // both arrays went recursive:
  184. return true;
  185. }
  186. var site = eqlStorage.GetCallSite("eql?");
  187. for (int i = 0; i < self.Count; i++) {
  188. if (!Protocols.IsEqual(site, self[i], other[i])) {
  189. return false;
  190. }
  191. }
  192. }
  193. return true;
  194. }
  195. #endregion
  196. #region Add, AddRange
  197. private int ResizeForAppend(int count, bool clear) {
  198. int oldCount = _count;
  199. _count += count;
  200. if (_start + oldCount > _content.Length - count) {
  201. object[] newContent;
  202. if (_count > _content.Length) {
  203. newContent = new object[Utils.GetExpandedSize(_content, _count)];
  204. } else {
  205. newContent = _content;
  206. }
  207. Array.Copy(_content, _start, newContent, 0, oldCount);
  208. if (newContent == _content && (count < _start || clear)) {
  209. if (_start < oldCount) {
  210. Utils.Fill(newContent, oldCount, null, _start);
  211. } else {
  212. Utils.Fill(newContent, _start, null, oldCount);
  213. }
  214. }
  215. _content = newContent;
  216. _start = 0;
  217. }
  218. ObjectInvariant();
  219. return _start + oldCount;
  220. }
  221. public void Add(object item) {
  222. Mutate();
  223. int index = ResizeForAppend(1, false);
  224. _content[index] = item;
  225. }
  226. int IList.Add(object value) {
  227. int result = _count;
  228. Add(value);
  229. return result;
  230. }
  231. public RubyArray/*!*/ AddCapacity(int capacity) {
  232. Mutate();
  233. int oldCount = _count;
  234. ResizeForAppend(capacity, true);
  235. _count = oldCount;
  236. return this;
  237. }
  238. public RubyArray/*!*/ AddMultiple(int count, object value) {
  239. Mutate();
  240. if (value != null) {
  241. int start = ResizeForAppend(count, false);
  242. int end = start + count;
  243. for (int i = start; i < end; i++) {
  244. _content[i] = value;
  245. }
  246. } else {
  247. ResizeForAppend(count, true);
  248. }
  249. return this;
  250. }
  251. public RubyArray/*!*/ AddRange(IList/*!*/ items, int start, int count) {
  252. ContractUtils.RequiresNotNull(items, "items");
  253. ContractUtils.RequiresArrayRange(items.Count, start, count, "start", "count");
  254. Mutate();
  255. RubyArray array;
  256. object[] vector;
  257. if ((array = items as RubyArray) != null) {
  258. AddVector(array._content, array._start + start, count);
  259. } else if ((vector = items as object[]) != null) {
  260. AddVector(vector, start, count);
  261. } else {
  262. AddList(items, start, count);
  263. }
  264. return this;
  265. }
  266. public RubyArray/*!*/ AddRange(IEnumerable/*!*/ items) {
  267. ContractUtils.RequiresNotNull(items, "items");
  268. Mutate();
  269. RubyArray array;
  270. ICollection collection;
  271. object[] vector;
  272. if ((array = items as RubyArray) != null) {
  273. AddVector(array._content, array._start, array._count);
  274. } else if ((vector = items as object[]) != null) {
  275. AddVector(vector, 0, vector.Length);
  276. } else if ((collection = items as ICollection) != null) {
  277. AddCollection(collection);
  278. } else {
  279. AddSequence(items);
  280. }
  281. return this;
  282. }
  283. private void AddList(IList/*!*/ items, int start, int count) {
  284. int s = ResizeForAppend(count, false);
  285. for (int i = 0; i < count; i++) {
  286. _content[s + i] = items[start + i];
  287. }
  288. }
  289. private void AddVector(object[]/*!*/ items, int start, int count) {
  290. int s = ResizeForAppend(count, false);
  291. Array.Copy(items, start, _content, s, count);
  292. }
  293. private void AddCollection(ICollection/*!*/ items) {
  294. int i = ResizeForAppend(items.Count, false);
  295. foreach (object item in items) {
  296. _content[i++] = item;
  297. }
  298. }
  299. private void AddSequence(IEnumerable/*!*/ items) {
  300. foreach (var item in items) {
  301. Add(item);
  302. }
  303. }
  304. #endregion
  305. #region Insert, InsertRange
  306. private int ResizeForInsertion(int index, int size) {
  307. if (_count + size > _content.Length) {
  308. object[] newContent = new object[Utils.GetExpandedSize(_content, _count + size)];
  309. Array.Copy(_content, _start, newContent, 0, index);
  310. Array.Copy(_content, _start + index, newContent, index + size, _count - index);
  311. _count += size;
  312. _content = newContent;
  313. return index;
  314. }
  315. int rindex = _start + index;
  316. int result = rindex;
  317. int spaceRight = _content.Length - _start - _count;
  318. int shiftLeft = 0, shiftRight = 0;
  319. if (_start >= size) {
  320. if (spaceRight >= size) {
  321. if (index < _count / 2) {
  322. shiftLeft = size;
  323. result -= size;
  324. } else {
  325. shiftRight = size;
  326. }
  327. } else {
  328. shiftLeft = size;
  329. result -= size;
  330. }
  331. } else if (spaceRight >= size) {
  332. shiftRight = size;
  333. } else {
  334. shiftLeft = _start;
  335. shiftRight = size - shiftLeft;
  336. result -= shiftLeft;
  337. }
  338. if (shiftLeft > 0) {
  339. var newStart = _start - shiftLeft;
  340. Array.Copy(_content, _start, _content, newStart, index);
  341. _start = newStart;
  342. }
  343. if (shiftRight > 0) {
  344. Array.Copy(_content, rindex, _content, rindex + shiftRight, _count - index);
  345. }
  346. _count += size;
  347. return result;
  348. }
  349. public void Insert(int index, object item) {
  350. ContractUtils.RequiresArrayInsertIndex(_count, index, "index");
  351. Mutate();
  352. int i = ResizeForInsertion(index, 1);
  353. _content[i] = item;
  354. ObjectInvariant();
  355. }
  356. public void InsertRange(int index, IList/*!*/ items, int start, int count) {
  357. ContractUtils.RequiresNotNull(items, "items");
  358. ContractUtils.RequiresArrayInsertIndex(_count, index, "index");
  359. ContractUtils.RequiresArrayRange(items.Count, start, count, "start", "count");
  360. Mutate();
  361. RubyArray array;
  362. object[] vector;
  363. if ((array = items as RubyArray) != null) {
  364. InsertVector(index, array._content, start, count);
  365. } else if ((vector = items as object[]) != null) {
  366. InsertVector(index, vector, start, count);
  367. } else {
  368. InsertList(index, items, start, count);
  369. }
  370. }
  371. public void InsertRange(int index, IEnumerable/*!*/ items) {
  372. ContractUtils.RequiresNotNull(items, "items");
  373. ContractUtils.RequiresArrayInsertIndex(_count, index, "index");
  374. Mutate();
  375. RubyArray array;
  376. IList list;
  377. object[] vector;
  378. if ((array = items as RubyArray) != null) {
  379. InsertArray(index, array);
  380. } else if ((vector = items as object[]) != null) {
  381. InsertVector(index, vector, 0, vector.Length);
  382. } else if ((list = items as IList) != null) {
  383. InsertList(index, list, 0, list.Count);
  384. } else {
  385. InsertArray(index, new RubyArray(items));
  386. }
  387. }
  388. private void InsertArray(int index, RubyArray/*!*/ array) {
  389. InsertVector(index, array._content, array._start, array._count);
  390. }
  391. private void InsertVector(int index, object[]/*!*/ items, int start, int count) {
  392. int s = ResizeForInsertion(index, count);
  393. Array.Copy(items, start, _content, s, count);
  394. }
  395. private void InsertList(int index, IList/*!*/ items, int start, int count) {
  396. int s = ResizeForInsertion(index, count);
  397. for (int i = 0; i < count; i++) {
  398. _content[s + i] = items[start + i];
  399. }
  400. }
  401. #endregion
  402. #region RemoveAt, RemoveRange, Remove Clear
  403. public void RemoveRange(int index, int size) {
  404. ContractUtils.RequiresArrayRange(_count, index, size, "index", "size");
  405. Mutate();
  406. int newCount = _count - size;
  407. int length = _content.Length;
  408. int newLength = 2 * newCount - 1;
  409. if (newLength <= length / 2) {
  410. // resize:
  411. object[] newContent = new object[Math.Max(Utils.MinListSize, newLength)];
  412. Array.Copy(_content, _start, newContent, 0, index);
  413. Array.Copy(_content, _start + index + size, newContent, index, newCount - index);
  414. _content = newContent;
  415. _start = 0;
  416. } else if (index <= newCount / 2) {
  417. // shift right:
  418. int newStart = _start + size;
  419. Array.Copy(_content, _start, _content, newStart, index);
  420. Utils.Fill(_content, _start, null, size);
  421. _start = newStart;
  422. } else {
  423. // shift left:
  424. Array.Copy(_content, _start + index + size, _content, _start + index, newCount - index);
  425. Utils.Fill(_content, _start + newCount, null, size);
  426. }
  427. _count = newCount;
  428. ObjectInvariant();
  429. }
  430. public void RemoveAt(int index) {
  431. RemoveRange(index, 1);
  432. }
  433. public bool Remove(object item) {
  434. int index = IndexOf(item);
  435. if (index >= 0) {
  436. RemoveAt(index);
  437. return true;
  438. }
  439. return false;
  440. }
  441. void IList.Remove(object value) {
  442. Remove(value);
  443. }
  444. public void Clear() {
  445. Mutate();
  446. _content = ArrayUtils.EmptyObjects;
  447. _start = _count = 0;
  448. }
  449. #endregion
  450. #region CopyTo, ToArray
  451. public void CopyTo(object[]/*!*/ array, int index) {
  452. Array.Copy(_content, _start, array, index, _count);
  453. }
  454. void ICollection.CopyTo(Array array, int index) {
  455. Array.Copy(_content, _start, array, index, _count);
  456. }
  457. public object[]/*!*/ ToArray() {
  458. object[] result = new object[_count];
  459. CopyTo(result, 0);
  460. return result;
  461. }
  462. #endregion
  463. #region IndexOf, Contains, FindIndex, GetEnumerator
  464. public int IndexOf(object item) {
  465. return IndexOf(item, 0, _count);
  466. }
  467. public int IndexOf(object item, int startIndex) {
  468. return Array.IndexOf(_content, item, startIndex, _count - startIndex);
  469. }
  470. public int IndexOf(object item, int startIndex, int count) {
  471. return Array.IndexOf(_content, item, _start + startIndex, count);
  472. }
  473. #if !SILVERLIGHT // Array.FindIndex
  474. public int FindIndex(Predicate<object> match) {
  475. return FindIndex(0, _count, match);
  476. }
  477. public int FindIndex(int startIndex, Predicate<object>/*!*/ match) {
  478. return FindIndex(startIndex, _count - startIndex, match);
  479. }
  480. public int FindIndex(int startIndex, int count, Predicate<object>/*!*/ match) {
  481. return Array.FindIndex(_content, _start + startIndex, count, match);
  482. }
  483. #endif
  484. public bool Contains(object item) {
  485. return IndexOf(item) >= 0;
  486. }
  487. public IEnumerator<object>/*!*/ GetEnumerator() {
  488. for (int i = 0, start = _start, count = _count; i < count; i++) {
  489. yield return _content[start + i];
  490. }
  491. }
  492. IEnumerator/*!*/ IEnumerable.GetEnumerator() {
  493. return ((IEnumerable<object>)this).GetEnumerator();
  494. }
  495. #endregion
  496. #region Misc
  497. public int Count {
  498. get { return _count; }
  499. }
  500. public bool IsReadOnly {
  501. get { return IsFrozen; }
  502. }
  503. bool IList.IsFixedSize {
  504. get { return IsReadOnly; }
  505. }
  506. bool ICollection.IsSynchronized {
  507. get { return false; }
  508. }
  509. object ICollection.SyncRoot {
  510. get { return this; }
  511. }
  512. public int Capacity {
  513. get { return _content.Length; }
  514. }
  515. public object this[int index] {
  516. get {
  517. return _content[_start + index];
  518. }
  519. set {
  520. Mutate();
  521. _content[_start + index] = value;
  522. }
  523. }
  524. public void Reverse() {
  525. Mutate();
  526. Array.Reverse(_content, _start, _count);
  527. }
  528. public void Sort() {
  529. Mutate();
  530. Array.Sort(_content, _start, _count);
  531. }
  532. public void Sort(Comparison<object>/*!*/ comparison) {
  533. Mutate();
  534. Array.Sort(_content, _start, _count, ArrayUtils.ToComparer(comparison));
  535. }
  536. #endregion
  537. #region DebugView
  538. internal string/*!*/ GetDebugView() {
  539. return RubyContext._Default != null ? RubyContext._Default.Inspect(this).ToString() : ToString();
  540. }
  541. #endregion
  542. }
  543. }