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

/DICK.B1/IronPython/Runtime/Operations/ArrayOps.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 360 lines | 252 code | 72 blank | 36 comment | 73 complexity | 7fc436eaa2f13d0dbfd318a44c7b1093 MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
  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, params object[] indices) {
  99. if (indices == null || indices.Length < 1) throw PythonOps.TypeError("__getitem__ requires at least 1 parameter");
  100. int iindex;
  101. if (indices.Length == 1 && Converter.TryConvertToInt32(indices[0], out iindex)) {
  102. return GetItem(data, iindex);
  103. }
  104. Type t = data.GetType();
  105. Debug.Assert(t.HasElementType);
  106. int[] iindices = TupleToIndices(data, indices);
  107. if (data.Rank != indices.Length) throw PythonOps.ValueError("bad dimensions for array, got {0} expected {1}", indices.Length, data.Rank);
  108. for (int i = 0; i < iindices.Length; i++) iindices[i] += data.GetLowerBound(i);
  109. return data.GetValue(iindices);
  110. }
  111. [SpecialName]
  112. public static void SetItem(Array data, int index, object value) {
  113. if (data == null) throw PythonOps.TypeError("expected Array, got None");
  114. data.SetValue(Converter.Convert(value, data.GetType().GetElementType()), PythonOps.FixIndex(index, data.Length) + data.GetLowerBound(0));
  115. }
  116. [SpecialName]
  117. public static void SetItem(Array a, params object[] indexAndValue) {
  118. if (indexAndValue == null || indexAndValue.Length < 2) throw PythonOps.TypeError("__setitem__ requires at least 2 parameters");
  119. int iindex;
  120. if (indexAndValue.Length == 2 && Converter.TryConvertToInt32(indexAndValue[0], out iindex)) {
  121. SetItem(a, iindex, indexAndValue[1]);
  122. return;
  123. }
  124. Type t = a.GetType();
  125. Debug.Assert(t.HasElementType);
  126. object[] args = ArrayUtils.RemoveLast(indexAndValue);
  127. int[] indices = TupleToIndices(a, args);
  128. if (a.Rank != args.Length) throw PythonOps.ValueError("bad dimensions for array, got {0} expected {1}", args.Length, a.Rank);
  129. for (int i = 0; i < indices.Length; i++) indices[i] += a.GetLowerBound(i);
  130. a.SetValue(indexAndValue[indexAndValue.Length - 1], indices);
  131. }
  132. [SpecialName]
  133. public static void SetItem(Array a, Slice index, object value) {
  134. if (a.Rank != 1) throw PythonOps.NotImplementedError("slice on multi-dimensional array");
  135. Type elm = a.GetType().GetElementType();
  136. index.DoSliceAssign(
  137. delegate(int idx, object val) {
  138. a.SetValue(Converter.Convert(val, elm), idx + a.GetLowerBound(0));
  139. },
  140. a.Length,
  141. value);
  142. }
  143. public static string __repr__(CodeContext/*!*/ context, [NotNull]Array/*!*/ self) {
  144. List<object> infinite = PythonOps.GetAndCheckInfinite(self);
  145. if (infinite == null) {
  146. return "...";
  147. }
  148. int index = infinite.Count;
  149. infinite.Add(self);
  150. try {
  151. StringBuilder ret = new StringBuilder();
  152. if (self.Rank == 1) {
  153. // single dimensional Array's have a valid display
  154. ret.Append("Array[");
  155. Type elemType = self.GetType().GetElementType();
  156. ret.Append(DynamicHelpers.GetPythonTypeFromType(elemType).Name);
  157. ret.Append("]");
  158. ret.Append("((");
  159. for (int i = 0; i < self.Length; i++) {
  160. if (i > 0) ret.Append(", ");
  161. ret.Append(PythonOps.Repr(context, self.GetValue(i + self.GetLowerBound(0))));
  162. }
  163. ret.Append("))");
  164. } else {
  165. // multi dimensional arrays require multiple statements to construct so we just
  166. // give enough info to identify the object and its type.
  167. ret.Append("<");
  168. ret.Append(self.Rank);
  169. ret.Append(" dimensional Array[");
  170. Type elemType = self.GetType().GetElementType();
  171. ret.Append(DynamicHelpers.GetPythonTypeFromType(elemType).Name);
  172. ret.Append("] at ");
  173. ret.Append(PythonOps.HexId(self));
  174. ret.Append(">");
  175. }
  176. return ret.ToString();
  177. } finally {
  178. System.Diagnostics.Debug.Assert(index == infinite.Count - 1);
  179. infinite.RemoveAt(index);
  180. }
  181. }
  182. #endregion
  183. #region Internal APIs
  184. /// <summary>
  185. /// Multiply two object[] arrays - internal version used for objects backed by arrays
  186. /// </summary>
  187. internal static object[] Multiply(object[] data, int size, int count) {
  188. int newCount = checked(size * count);
  189. object[] ret = ArrayOps.CopyArray(data, newCount);
  190. if (count > 0) {
  191. // this should be extremely fast for large count as it uses the same algoithim as efficient integer powers
  192. // ??? need to test to see how large count and n need to be for this to be fastest approach
  193. int block = size;
  194. int pos = size;
  195. while (pos < newCount) {
  196. Array.Copy(ret, 0, ret, pos, Math.Min(block, newCount - pos));
  197. pos += block;
  198. block *= 2;
  199. }
  200. }
  201. return ret;
  202. }
  203. /// <summary>
  204. /// Add two arrays - internal versions for objects backed by arrays
  205. /// </summary>
  206. /// <param name="data1"></param>
  207. /// <param name="size1"></param>
  208. /// <param name="data2"></param>
  209. /// <param name="size2"></param>
  210. /// <returns></returns>
  211. internal static object[] Add(object[] data1, int size1, object[] data2, int size2) {
  212. object[] ret = ArrayOps.CopyArray(data1, size1 + size2);
  213. Array.Copy(data2, 0, ret, size1, size2);
  214. return ret;
  215. }
  216. internal static object[] GetSlice(object[] data, int start, int stop) {
  217. if (stop <= start) return ArrayUtils.EmptyObjects;
  218. object[] ret = new object[stop - start];
  219. int index = 0;
  220. for (int i = start; i < stop; i++) {
  221. ret[index++] = data[i];
  222. }
  223. return ret;
  224. }
  225. internal static object[] GetSlice(object[] data, Slice slice) {
  226. int start, stop, step;
  227. slice.indices(data.Length, out start, out stop, out step);
  228. if (step == 1) return GetSlice(data, start, stop);
  229. int size = GetSliceSize(start, stop, step);
  230. if (size <= 0) return ArrayUtils.EmptyObjects;
  231. object[] res = new object[size];
  232. for (int i = 0, index = start; i < res.Length; i++, index += step) {
  233. res[i] = data[index];
  234. }
  235. return res;
  236. }
  237. internal static Array GetSlice(Array data, int size, Slice slice) {
  238. if (data.Rank != 1) throw PythonOps.NotImplementedError("slice on multi-dimensional array");
  239. int start, stop, step;
  240. slice.indices(size, out start, out stop, out step);
  241. if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
  242. if (data.GetType().GetElementType() == typeof(object))
  243. return ArrayUtils.EmptyObjects;
  244. return Array.CreateInstance(data.GetType().GetElementType(), 0);
  245. }
  246. if (step == 1) {
  247. int n = stop - start;
  248. Array ret = Array.CreateInstance(data.GetType().GetElementType(), n);
  249. Array.Copy(data, start + data.GetLowerBound(0), ret, 0, n);
  250. return ret;
  251. } else {
  252. int n = GetSliceSize(start, stop, step);
  253. Array ret = Array.CreateInstance(data.GetType().GetElementType(), n);
  254. int ri = 0;
  255. for (int i = 0, index = start; i < n; i++, index += step) {
  256. ret.SetValue(data.GetValue(index + data.GetLowerBound(0)), ri++);
  257. }
  258. return ret;
  259. }
  260. }
  261. private static int GetSliceSize(int start, int stop, int step) {
  262. // could cause overflow (?)
  263. return step > 0 ? (stop - start + step - 1) / step : (stop - start + step + 1) / step;
  264. }
  265. internal static object[] CopyArray(object[] data, int newSize) {
  266. if (newSize == 0) return ArrayUtils.EmptyObjects;
  267. object[] newData = new object[newSize];
  268. if (data.Length < 20) {
  269. for (int i = 0; i < data.Length && i < newSize; i++) {
  270. newData[i] = data[i];
  271. }
  272. } else {
  273. Array.Copy(data, newData, Math.Min(newSize, data.Length));
  274. }
  275. return newData;
  276. }
  277. #endregion
  278. #region Private helpers
  279. private static int[] TupleToIndices(Array a, IList<object> tuple) {
  280. int[] indices = new int[tuple.Count];
  281. for (int i = 0; i < indices.Length; i++) {
  282. indices[i] = PythonOps.FixIndex(Converter.ConvertToInt32(tuple[i]), a.GetUpperBound(i) + 1);
  283. }
  284. return indices;
  285. }
  286. #endregion
  287. }
  288. }