PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/Languages/IronPython/IronPython/Runtime/Operations/ArrayOps.cs

http://github.com/IronLanguages/main
C# | 374 lines | 263 code | 75 blank | 36 comment | 76 complexity | 48cab2e2dd2cce1387438723094ad3c3 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. * dlr@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 Microsoft.Scripting.Runtime;
  20. using Microsoft.Scripting.Utils;
  21. using System.Text;
  22. using IronPython.Runtime.Types;
  23. using SpecialNameAttribute = System.Runtime.CompilerServices.SpecialNameAttribute;
  24. namespace IronPython.Runtime.Operations {
  25. public static class ArrayOps {
  26. #region Python APIs
  27. [SpecialName]
  28. public static Array Add(Array data1, Array data2) {
  29. if (data1 == null) throw PythonOps.TypeError("expected array for 1st argument, got None");
  30. if (data2 == null) throw PythonOps.TypeError("expected array for 2nd argument, got None");
  31. if (data1.Rank > 1 || data2.Rank > 1) throw new NotImplementedException("can't add multidimensional arrays");
  32. Type type1 = data1.GetType();
  33. Type type2 = data2.GetType();
  34. Type type = (type1 == type2) ? type1.GetElementType() : typeof(object);
  35. Array ret = Array.CreateInstance(type, data1.Length + data2.Length);
  36. Array.Copy(data1, 0, ret, 0, data1.Length);
  37. Array.Copy(data2, 0, ret, data1.Length, data2.Length);
  38. return ret;
  39. }
  40. [StaticExtensionMethod]
  41. public static object __new__(CodeContext context, PythonType pythonType, ICollection items) {
  42. Type type = pythonType.UnderlyingSystemType.GetElementType();
  43. Array res = Array.CreateInstance(type, items.Count);
  44. int i = 0;
  45. foreach (object item in items) {
  46. res.SetValue(Converter.Convert(item, type), i++);
  47. }
  48. return res;
  49. }
  50. [StaticExtensionMethod]
  51. public static object __new__(CodeContext context, PythonType pythonType, object items) {
  52. Type type = pythonType.UnderlyingSystemType.GetElementType();
  53. object lenFunc;
  54. if (!PythonOps.TryGetBoundAttr(items, "__len__", out lenFunc))
  55. throw PythonOps.TypeErrorForBadInstance("expected object with __len__ function, got {0}", items);
  56. int len = PythonContext.GetContext(context).ConvertToInt32(PythonOps.CallWithContext(context, lenFunc));
  57. Array res = Array.CreateInstance(type, len);
  58. IEnumerator ie = PythonOps.GetEnumerator(items);
  59. int i = 0;
  60. while (ie.MoveNext()) {
  61. res.SetValue(Converter.Convert(ie.Current, type), i++);
  62. }
  63. return res;
  64. }
  65. /// <summary>
  66. /// Multiply two object[] arrays - slow version, we need to get the type, etc...
  67. /// </summary>
  68. [SpecialName]
  69. public static Array Multiply(Array data, int count) {
  70. if (data.Rank > 1) throw new NotImplementedException("can't multiply multidimensional arrays");
  71. Type elemType = data.GetType().GetElementType();
  72. if (count <= 0) return Array.CreateInstance(elemType, 0);
  73. int newCount = data.Length * count;
  74. Array ret = Array.CreateInstance(elemType, newCount);
  75. Array.Copy(data, 0, ret, 0, data.Length);
  76. // this should be extremely fast for large count as it uses the same algoithim as efficient integer powers
  77. // ??? need to test to see how large count and n need to be for this to be fastest approach
  78. int block = data.Length;
  79. int pos = data.Length;
  80. while (pos < newCount) {
  81. Array.Copy(ret, 0, ret, pos, Math.Min(block, newCount - pos));
  82. pos += block;
  83. block *= 2;
  84. }
  85. return ret;
  86. }
  87. [SpecialName]
  88. public static object GetItem(Array data, int index) {
  89. if (data == null) throw PythonOps.TypeError("expected Array, got None");
  90. return data.GetValue(PythonOps.FixIndex(index, data.Length) + data.GetLowerBound(0));
  91. }
  92. [SpecialName]
  93. public static object GetItem(Array data, Slice slice) {
  94. if (data == null) throw PythonOps.TypeError("expected Array, got None");
  95. return GetSlice(data, data.Length, slice);
  96. }
  97. [SpecialName]
  98. public static object GetItem(Array data, PythonTuple tuple) {
  99. if (data == null) throw PythonOps.TypeError("expected Array, got None");
  100. return GetItem(data, tuple.ToArray());
  101. }
  102. [SpecialName]
  103. public static object GetItem(Array data, params object[] indices) {
  104. if (indices == null || indices.Length < 1) throw PythonOps.TypeError("__getitem__ requires at least 1 parameter");
  105. int iindex;
  106. if (indices.Length == 1 && Converter.TryConvertToInt32(indices[0], out iindex)) {
  107. return GetItem(data, iindex);
  108. }
  109. Type t = data.GetType();
  110. Debug.Assert(t.HasElementType);
  111. int[] iindices = TupleToIndices(data, indices);
  112. if (data.Rank != indices.Length) throw PythonOps.ValueError("bad dimensions for array, got {0} expected {1}", indices.Length, data.Rank);
  113. for (int i = 0; i < iindices.Length; i++) iindices[i] += data.GetLowerBound(i);
  114. return data.GetValue(iindices);
  115. }
  116. [SpecialName]
  117. public static void SetItem(Array data, int index, object value) {
  118. if (data == null) throw PythonOps.TypeError("expected Array, got None");
  119. data.SetValue(Converter.Convert(value, data.GetType().GetElementType()), PythonOps.FixIndex(index, data.Length) + data.GetLowerBound(0));
  120. }
  121. [SpecialName]
  122. public static void SetItem(Array a, params object[] indexAndValue) {
  123. if (indexAndValue == null || indexAndValue.Length < 2) throw PythonOps.TypeError("__setitem__ requires at least 2 parameters");
  124. int iindex;
  125. if (indexAndValue.Length == 2 && Converter.TryConvertToInt32(indexAndValue[0], out iindex)) {
  126. SetItem(a, iindex, indexAndValue[1]);
  127. return;
  128. }
  129. Type t = a.GetType();
  130. Debug.Assert(t.HasElementType);
  131. object[] args = ArrayUtils.RemoveLast(indexAndValue);
  132. int[] indices = TupleToIndices(a, args);
  133. if (a.Rank != args.Length) throw PythonOps.ValueError("bad dimensions for array, got {0} expected {1}", args.Length, a.Rank);
  134. for (int i = 0; i < indices.Length; i++) indices[i] += a.GetLowerBound(i);
  135. a.SetValue(indexAndValue[indexAndValue.Length - 1], indices);
  136. }
  137. [SpecialName]
  138. public static void SetItem(Array a, Slice index, object value) {
  139. if (a.Rank != 1) throw PythonOps.NotImplementedError("slice on multi-dimensional array");
  140. Type elm = a.GetType().GetElementType();
  141. index.DoSliceAssign(
  142. delegate(int idx, object val) {
  143. a.SetValue(Converter.Convert(val, elm), idx + a.GetLowerBound(0));
  144. },
  145. a.Length,
  146. value);
  147. }
  148. public static string __repr__(CodeContext/*!*/ context, [NotNull]Array/*!*/ self) {
  149. List<object> infinite = PythonOps.GetAndCheckInfinite(self);
  150. if (infinite == null) {
  151. return "...";
  152. }
  153. int index = infinite.Count;
  154. infinite.Add(self);
  155. try {
  156. StringBuilder ret = new StringBuilder();
  157. if (self.Rank == 1) {
  158. // single dimensional Array's have a valid display
  159. ret.Append("Array[");
  160. Type elemType = self.GetType().GetElementType();
  161. ret.Append(DynamicHelpers.GetPythonTypeFromType(elemType).Name);
  162. ret.Append("]");
  163. ret.Append("((");
  164. for (int i = 0; i < self.Length; i++) {
  165. if (i > 0) ret.Append(", ");
  166. ret.Append(PythonOps.Repr(context, self.GetValue(i + self.GetLowerBound(0))));
  167. }
  168. ret.Append("))");
  169. } else {
  170. // multi dimensional arrays require multiple statements to construct so we just
  171. // give enough info to identify the object and its type.
  172. ret.Append("<");
  173. ret.Append(self.Rank);
  174. ret.Append(" dimensional Array[");
  175. Type elemType = self.GetType().GetElementType();
  176. ret.Append(DynamicHelpers.GetPythonTypeFromType(elemType).Name);
  177. ret.Append("] at ");
  178. ret.Append(PythonOps.HexId(self));
  179. ret.Append(">");
  180. }
  181. return ret.ToString();
  182. } finally {
  183. System.Diagnostics.Debug.Assert(index == infinite.Count - 1);
  184. infinite.RemoveAt(index);
  185. }
  186. }
  187. #endregion
  188. #region Internal APIs
  189. /// <summary>
  190. /// Multiply two object[] arrays - internal version used for objects backed by arrays
  191. /// </summary>
  192. internal static object[] Multiply(object[] data, int size, int count) {
  193. int newCount = checked(size * count);
  194. object[] ret = ArrayOps.CopyArray(data, newCount);
  195. if (count > 0) {
  196. // this should be extremely fast for large count as it uses the same algoithim as efficient integer powers
  197. // ??? need to test to see how large count and n need to be for this to be fastest approach
  198. int block = size;
  199. int pos = size;
  200. while (pos < newCount) {
  201. Array.Copy(ret, 0, ret, pos, Math.Min(block, newCount - pos));
  202. pos += block;
  203. block *= 2;
  204. }
  205. }
  206. return ret;
  207. }
  208. /// <summary>
  209. /// Add two arrays - internal versions for objects backed by arrays
  210. /// </summary>
  211. /// <param name="data1"></param>
  212. /// <param name="size1"></param>
  213. /// <param name="data2"></param>
  214. /// <param name="size2"></param>
  215. /// <returns></returns>
  216. internal static object[] Add(object[] data1, int size1, object[] data2, int size2) {
  217. object[] ret = ArrayOps.CopyArray(data1, size1 + size2);
  218. Array.Copy(data2, 0, ret, size1, size2);
  219. return ret;
  220. }
  221. internal static object[] GetSlice(object[] data, int start, int stop) {
  222. if (stop <= start) return ArrayUtils.EmptyObjects;
  223. object[] ret = new object[stop - start];
  224. int index = 0;
  225. for (int i = start; i < stop; i++) {
  226. ret[index++] = data[i];
  227. }
  228. return ret;
  229. }
  230. internal static object[] GetSlice(object[] data, int start, int stop, int step) {
  231. Debug.Assert(step != 0);
  232. if (step == 1) {
  233. return GetSlice(data, start, stop);
  234. }
  235. int size = GetSliceSize(start, stop, step);
  236. if (size <= 0) return ArrayUtils.EmptyObjects;
  237. object[] res = new object[size];
  238. for (int i = 0, index = start; i < res.Length; i++, index += step) {
  239. res[i] = data[index];
  240. }
  241. return res;
  242. }
  243. internal static object[] GetSlice(object[] data, Slice slice) {
  244. int start, stop, step;
  245. slice.indices(data.Length, out start, out stop, out step);
  246. return GetSlice(data, start, stop, step);
  247. }
  248. internal static Array GetSlice(Array data, int size, Slice slice) {
  249. if (data.Rank != 1) throw PythonOps.NotImplementedError("slice on multi-dimensional array");
  250. int start, stop, step;
  251. slice.indices(size, out start, out stop, out step);
  252. if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
  253. if (data.GetType().GetElementType() == typeof(object))
  254. return ArrayUtils.EmptyObjects;
  255. return Array.CreateInstance(data.GetType().GetElementType(), 0);
  256. }
  257. if (step == 1) {
  258. int n = stop - start;
  259. Array ret = Array.CreateInstance(data.GetType().GetElementType(), n);
  260. Array.Copy(data, start + data.GetLowerBound(0), ret, 0, n);
  261. return ret;
  262. } else {
  263. int n = GetSliceSize(start, stop, step);
  264. Array ret = Array.CreateInstance(data.GetType().GetElementType(), n);
  265. int ri = 0;
  266. for (int i = 0, index = start; i < n; i++, index += step) {
  267. ret.SetValue(data.GetValue(index + data.GetLowerBound(0)), ri++);
  268. }
  269. return ret;
  270. }
  271. }
  272. private static int GetSliceSize(int start, int stop, int step) {
  273. // could cause overflow (?)
  274. return step > 0 ? (stop - start + step - 1) / step : (stop - start + step + 1) / step;
  275. }
  276. internal static object[] CopyArray(object[] data, int newSize) {
  277. if (newSize == 0) return ArrayUtils.EmptyObjects;
  278. object[] newData = new object[newSize];
  279. if (data.Length < 20) {
  280. for (int i = 0; i < data.Length && i < newSize; i++) {
  281. newData[i] = data[i];
  282. }
  283. } else {
  284. Array.Copy(data, newData, Math.Min(newSize, data.Length));
  285. }
  286. return newData;
  287. }
  288. #endregion
  289. #region Private helpers
  290. private static int[] TupleToIndices(Array a, IList<object> tuple) {
  291. int[] indices = new int[tuple.Count];
  292. for (int i = 0; i < indices.Length; i++) {
  293. indices[i] = PythonOps.FixIndex(Converter.ConvertToInt32(tuple[i]), a.GetUpperBound(i) + 1);
  294. }
  295. return indices;
  296. }
  297. #endregion
  298. }
  299. }