/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* [email protected] . By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
*
* ***************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using IronPython.Runtime;
using IronPython.Runtime.Operations;
using IronPython.Runtime.Types;
using Microsoft.Scripting;
using Microsoft.Scripting.Runtime;
using Microsoft.Scripting.Utils;
using SpecialName = System.Runtime.CompilerServices.SpecialNameAttribute;
using System.Reflection;
#if FEATURE_NUMERICS
using System.Numerics;
#else
using Microsoft.Scripting.Math;
#endif
[assembly: PythonModule("array", typeof(IronPython.Modules.ArrayModule))]
namespace IronPython.Modules {
public static class ArrayModule {
public const string __doc__ = "Provides arrays for native data types. These can be used for compact storage or native interop via ctypes";
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
public static readonly PythonType/*!*/ ArrayType = DynamicHelpers.GetPythonTypeFromType(typeof(array));
[PythonType]
public class array : IPythonArray, IEnumerable, IWeakReferenceable, ICollection, ICodeFormattable, IList, IStructuralEquatable
#if CLR2
, IValueEquality
#endif
{
private ArrayData _data;
private char _typeCode;
private WeakRefTracker _tracker;
public array([BytesConversion]string type, [Optional]object initializer) {
if (type == null || type.Length != 1) {
throw PythonOps.TypeError("expected character, got {0}", PythonTypeOps.GetName(type));
}
_typeCode = type[0];
_data = CreateData(_typeCode);
if (initializer != Missing.Value) extend(initializer);
}
private array(char typeCode, ArrayData data) {
_typeCode = typeCode;
_data = data;
}
private static ArrayData CreateData(char typecode) {
ArrayData data;
switch (typecode) {
case 'c': data = new ArrayData(); break;
case 'b': data = new ArrayData(); break;
case 'B': data = new ArrayData(); break;
case 'u': data = new ArrayData(); break;
case 'h': data = new ArrayData(); break;
case 'H': data = new ArrayData(); break;
case 'l':
case 'i': data = new ArrayData(); break;
case 'L':
case 'I': data = new ArrayData(); break;
case 'f': data = new ArrayData(); break;
case 'd': data = new ArrayData(); break;
default:
throw PythonOps.ValueError("Bad type code (expected one of 'c', 'b', 'B', 'u', 'H', 'h', 'i', 'I', 'l', 'L', 'f', 'd')");
}
return data;
}
[SpecialName]
public array InPlaceAdd(array other) {
if (typecode != other.typecode) throw PythonOps.TypeError("cannot add different typecodes");
if (other._data.Length != 0) {
extend(other);
}
return this;
}
public static array operator +(array self, array other) {
if (self.typecode != other.typecode) throw PythonOps.TypeError("cannot add different typecodes");
array res = new array(self.typecode, Missing.Value);
foreach (object o in self) {
res.append(o);
}
foreach (object o in other) {
res.append(o);
}
return res;
}
[SpecialName]
public array InPlaceMultiply(int value) {
if (value <= 0) {
_data.Clear();
} else {
List myData = tolist();
for (int i = 0; i < (value - 1); i++) {
extend(myData);
}
}
return this;
}
public static array operator *(array array, int value) {
if ((BigInteger)value * array.__len__() * array.itemsize > SysModule.maxsize) {
throw PythonOps.MemoryError("");
}
if (value <= 0) {
return new array(array.typecode, Missing.Value);
}
return new array(array._typeCode, array._data.Multiply(value));
}
public static array operator *(array array, BigInteger value) {
int intValue;
if (!value.AsInt32(out intValue)) {
throw PythonOps.OverflowError("cannot fit 'long' into an index-sized integer");
} else if (value * array.__len__() * array.itemsize > SysModule.maxsize) {
throw PythonOps.MemoryError("");
}
return array * intValue;
}
public static array operator *(int value, array array) {
return array * value;
}
public static array operator *(BigInteger value, array array) {
return array * value;
}
public void append(object iterable) {
_data.Append(iterable);
}
internal IntPtr GetArrayAddress() {
return _data.GetAddress();
}
public PythonTuple buffer_info() {
return PythonTuple.MakeTuple(
_data.GetAddress().ToPython(),
_data.Length
);
}
public void byteswap() {
Stream s = ToStream();
byte[] bytes = new byte[s.Length];
s.Read(bytes, 0, bytes.Length);
byte[] tmp = new byte[itemsize];
for (int i = 0; i < bytes.Length; i += itemsize) {
for (int j = 0; j < itemsize; j++) {
tmp[j] = bytes[i + j];
}
for (int j = 0; j < itemsize; j++) {
bytes[i + j] = tmp[itemsize - (j + 1)];
}
}
_data.Clear();
MemoryStream ms = new MemoryStream(bytes);
FromStream(ms);
}
public int count(object x) {
if (x == null) return 0;
return _data.Count(x);
}
public void extend(object iterable) {
array pa = iterable as array;
if (pa != null && typecode != pa.typecode) {
throw PythonOps.TypeError("cannot extend with different typecode");
}
string str = iterable as string;
if (str != null && _typeCode != 'u') {
fromstring(str);
return;
}
Bytes bytes = iterable as Bytes;
if (bytes != null) {
fromstring(bytes);
return;
}
PythonBuffer buf = iterable as PythonBuffer;
if (buf != null) {
fromstring(buf);
return;
}
IEnumerator ie = PythonOps.GetEnumerator(iterable);
while (ie.MoveNext()) {
append(ie.Current);
}
}
public void fromlist(object iterable) {
IEnumerator ie = PythonOps.GetEnumerator(iterable);
List items = new List();
while (ie.MoveNext()) {
if (!_data.CanStore(ie.Current)) {
throw PythonOps.TypeError("expected {0}, got {1}",
DynamicHelpers.GetPythonTypeFromType(_data.StorageType).Name,
DynamicHelpers.GetPythonType(ie.Current).Name);
}
items.Add(ie.Current);
}
extend(items);
}
public void fromfile(PythonFile f, int n) {
int bytesNeeded = n * itemsize;
string bytes = f.read(bytesNeeded);
if (bytes.Length < bytesNeeded) throw PythonOps.EofError("file not large enough");
fromstring(bytes);
}
public void fromstring([NotNull]Bytes b) {
if ((b.Count % itemsize) != 0) throw PythonOps.ValueError("string length not a multiple of itemsize");
FromStream(new MemoryStream(b._bytes, false));
}
public void fromstring([NotNull]string s) {
if ((s.Length % itemsize) != 0) throw PythonOps.ValueError("string length not a multiple of itemsize");
byte[] bytes = new byte[s.Length];
for (int i = 0; i < bytes.Length; i++) {
bytes[i] = checked((byte)s[i]);
}
MemoryStream ms = new MemoryStream(bytes);
FromStream(ms);
}
public void fromstring([NotNull]PythonBuffer buf) {
if ((buf.Size % itemsize) != 0) throw PythonOps.ValueError("string length not a multiple of itemsize");
FromStream(new MemoryStream(buf.byteCache, false));
}
public void fromunicode(CodeContext/*!*/ context, string s) {
if (s == null) {
throw PythonOps.TypeError("expected string");
} else if (_typeCode != 'u') {
throw PythonOps.ValueError("fromunicode() may only be called on type 'u' arrays");
}
ArrayData data = (ArrayData)_data;
data.EnsureSize(data.Length + s.Length);
for (int i = 0; i < s.Length; i++) {
data.Data[i + data.Length] = s[i];
}
data.Length += s.Length;
}
public int index(object x) {
if (x == null) throw PythonOps.ValueError("got None, expected value");
int res = _data.Index(x);
if (res == -1) throw PythonOps.ValueError("x not found");
return res;
}
public void insert(int i, object x) {
if (i > _data.Length) i = _data.Length;
if (i < 0) i = _data.Length + i;
if (i < 0) i = 0;
_data.Insert(i, x);
}
public int itemsize {
get {
switch (_typeCode) {
case 'c': // char
case 'b': // signed byte
case 'B': // unsigned byte
case 'x': // pad byte
case 's': // null-terminated string
case 'p': // Pascal string
return 1;
case 'u': // unicode char
case 'h': // signed short
case 'H': // unsigned short
return 2;
case 'i': // signed int
case 'I': // unsigned int
case 'l': // signed long
case 'L': // unsigned long
case 'f': // float
return 4;
case 'P': // pointer
return IntPtr.Size;
case 'q': // signed long long
case 'Q': // unsigned long long
case 'd': // double
return 8;
default:
return 0;
}
}
}
public object pop() {
return pop(-1);
}
public object pop(int i) {
i = PythonOps.FixIndex(i, _data.Length);
object res = _data.GetData(i);
_data.RemoveAt(i);
return res;
}
public void read(PythonFile f, int n) {
fromfile(f, n);
}
public void remove(object value) {
if (value == null) throw PythonOps.ValueError("got None, expected value");
_data.Remove(value);
}
public void reverse() {
for (int index = 0; index < _data.Length / 2; index++) {
int left = index, right = _data.Length - (index + 1);
Debug.Assert(left != right);
_data.Swap(left, right);
}
}
public virtual object this[int index] {
get {
object val = _data.GetData(PythonOps.FixIndex(index, _data.Length));
switch (_typeCode) {
case 'b': return (int)(sbyte)val;
case 'B': return (int)(byte)val;
case 'c':
case 'u': return new string((char)val, 1);
case 'h': return (int)(short)val;
case 'H': return (int)(ushort)val;
case 'l': return val;
case 'i': return val;
case 'L': return (BigInteger)(uint)val;
case 'I':
return (BigInteger)(uint)val;
case 'f': return (double)(float)val;
case 'd': return val;
default:
throw PythonOps.ValueError("Bad type code (expected one of 'c', 'b', 'B', 'u', 'H', 'h', 'i', 'I', 'l', 'L', 'f', 'd')");
}
}
set {
_data.SetData(PythonOps.FixIndex(index, _data.Length), value);
}
}
internal byte[] RawGetItem(int index) {
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
switch (_typeCode) {
case 'c': bw.Write((byte)(char)_data.GetData(index)); break;
case 'b': bw.Write((sbyte)_data.GetData(index)); break;
case 'B': bw.Write((byte)_data.GetData(index)); break;
case 'u': bw.Write((char)_data.GetData(index)); break;
case 'h': bw.Write((short)_data.GetData(index)); break;
case 'H': bw.Write((ushort)_data.GetData(index)); break;
case 'l':
case 'i': bw.Write((int)_data.GetData(index)); break;
case 'L':
case 'I': bw.Write((uint)_data.GetData(index)); break;
case 'f': bw.Write((float)_data.GetData(index)); break;
case 'd': bw.Write((double)_data.GetData(index)); break;
}
return ms.ToArray();
}
public void __delitem__(int index) {
_data.RemoveAt(PythonOps.FixIndex(index, _data.Length));
}
public void __delitem__(Slice slice) {
if (slice == null) throw PythonOps.TypeError("expected Slice, got None");
int start, stop, step;
// slice is sealed, indices can't be user code...
slice.indices(_data.Length, out start, out stop, out step);
if (step > 0 && (start >= stop)) return;
if (step < 0 && (start <= stop)) return;
if (step == 1) {
int i = start;
for (int j = stop; j < _data.Length; j++, i++) {
_data.SetData(i, _data.GetData(j));
}
for (i = 0; i < stop - start; i++) {
_data.RemoveAt(_data.Length - 1);
}
return;
}
if (step == -1) {
int i = stop + 1;
for (int j = start + 1; j < _data.Length; j++, i++) {
_data.SetData(i, _data.GetData(j));
}
for (i = 0; i < stop - start; i++) {
_data.RemoveAt(_data.Length - 1);
}
return;
}
if (step < 0) {
// find "start" we will skip in the 1,2,3,... order
int i = start;
while (i > stop) {
i += step;
}
i -= step;
// swap start/stop, make step positive
stop = start + 1;
start = i;
step = -step;
}
int curr, skip, move;
// skip: the next position we should skip
// curr: the next position we should fill in data
// move: the next position we will check
curr = skip = move = start;
while (curr < stop && move < stop) {
if (move != skip) {
_data.SetData(curr++, _data.GetData(move));
} else
skip += step;
move++;
}
while (stop < _data.Length) {
_data.SetData(curr++, _data.GetData(stop++));
}
while (_data.Length > curr) {
_data.RemoveAt(_data.Length - 1);
}
}
public object this[Slice index] {
get {
if (index == null) throw PythonOps.TypeError("expected Slice, got None");
int start, stop, step;
index.indices(_data.Length, out start, out stop, out step);
array pa = new array(new string(_typeCode, 1), Missing.Value);
if (step < 0) {
for (int i = start; i > stop; i += step) {
pa._data.Append(_data.GetData(i));
}
} else {
for (int i = start; i < stop; i += step) {
pa._data.Append(_data.GetData(i));
}
}
return pa;
}
set {
if (index == null) throw PythonOps.TypeError("expected Slice, got None");
CheckSliceAssignType(value);
if (index.step != null) {
if (Object.ReferenceEquals(value, this)) value = this.tolist();
index.DoSliceAssign(SliceAssign, _data.Length, value);
} else {
int start, stop, step;
index.indices(_data.Length, out start, out stop, out step);
if (stop < start) {
stop = start;
}
SliceNoStep(value, start, stop);
}
}
}
private void CheckSliceAssignType(object value) {
array pa = value as array;
if (pa == null) {
throw PythonOps.TypeError("can only assign array (not \"{0}\") to array slice", PythonTypeOps.GetName(value));
} else if (pa != null && pa._typeCode != _typeCode) {
throw PythonOps.TypeError("bad argument type for built-in operation");
}
}
private void SliceNoStep(object value, int start, int stop) {
// replace between start & stop w/ values
IEnumerator ie = PythonOps.GetEnumerator(value);
ArrayData newData = CreateData(_typeCode);
for (int i = 0; i < start; i++) {
newData.Append(_data.GetData(i));
}
while (ie.MoveNext()) {
newData.Append(ie.Current);
}
for (int i = Math.Max(stop, start); i < _data.Length; i++) {
newData.Append(_data.GetData(i));
}
_data = newData;
}
public object __getslice__(object start, object stop) {
return this[new Slice(start, stop)];
}
public void __setslice__(int start, int stop, object value) {
CheckSliceAssignType(value);
Slice.FixSliceArguments(_data.Length, ref start, ref stop);
SliceNoStep(value, start, stop);
}
public void __delslice__(object start, object stop) {
__delitem__(new Slice(start, stop));
}
public PythonTuple __reduce__() {
return PythonOps.MakeTuple(
DynamicHelpers.GetPythonType(this),
PythonOps.MakeTuple(
typecode,
ToByteArray().MakeString()
),
null
);
}
public array __copy__() {
return new array(typecode, this);
}
public array __deepcopy__() {
// we only have simple data so this is the same as a copy
return __copy__();
}
public PythonTuple __reduce_ex__(int version) {
return __reduce__();
}
public PythonTuple __reduce_ex__() {
return __reduce__();
}
private void SliceAssign(int index, object value) {
_data.SetData(index, value);
}
public void tofile(PythonFile f) {
f.write(tostring());
}
public List tolist() {
List res = new List();
for (int i = 0; i < _data.Length; i++) {
res.AddNoLock(this[i]);
}
return res;
}
public string tostring() {
Stream s = ToStream();
byte[] bytes = new byte[s.Length];
s.Read(bytes, 0, (int)s.Length);
StringBuilder res = new StringBuilder();
for (int i = 0; i < bytes.Length; i++) {
res.Append((char)bytes[i]);
}
return res.ToString();
}
public string tounicode(CodeContext/*!*/ context) {
if (_typeCode != 'u') throw PythonOps.ValueError("only 'u' arrays can be converted to unicode");
return new string(((ArrayData)_data).Data, 0, _data.Length);
}
public void write(PythonFile f) {
tofile(f);
}
public string/*!*/ typecode {
get { return ScriptingRuntimeHelpers.CharToString(_typeCode); }
}
private abstract class ArrayData {
public abstract void SetData(int index, object value);
public abstract object GetData(int index);
public abstract void Append(object value);
public abstract int Count(object value);
public abstract bool CanStore(object value);
public abstract Type StorageType { get; }
public abstract int Index(object value);
public abstract void Insert(int index, object value);
public abstract void Remove(object value);
public abstract void RemoveAt(int index);
public abstract int Length { get; set; }
public abstract void Swap(int x, int y);
public abstract void Clear();
public abstract IntPtr GetAddress();
public abstract ArrayData Multiply(int count);
}
internal MemoryStream ToStream() {
MemoryStream ms = new MemoryStream();
ToStream(ms);
ms.Seek(0, SeekOrigin.Begin);
return ms;
}
internal void ToStream(Stream ms) {
BinaryWriter bw = new BinaryWriter(ms, Encoding.Unicode);
for (int i = 0; i < _data.Length; i++) {
switch (_typeCode) {
case 'c': bw.Write((byte)(char)_data.GetData(i)); break;
case 'b': bw.Write((sbyte)_data.GetData(i)); break;
case 'B': bw.Write((byte)_data.GetData(i)); break;
case 'u': bw.Write((char)_data.GetData(i)); break;
case 'h': bw.Write((short)_data.GetData(i)); break;
case 'H': bw.Write((ushort)_data.GetData(i)); break;
case 'l':
case 'i': bw.Write((int)_data.GetData(i)); break;
case 'L':
case 'I': bw.Write((uint)_data.GetData(i)); break;
case 'f': bw.Write((float)_data.GetData(i)); break;
case 'd': bw.Write((double)_data.GetData(i)); break;
}
}
}
internal byte[] ToByteArray() {
return ToStream().ToArray();
}
internal void Clear() {
_data = CreateData(_typeCode);
}
internal void FromStream(Stream ms) {
BinaryReader br = new BinaryReader(ms);
for (int i = 0; i < ms.Length / itemsize; i++) {
object value;
switch (_typeCode) {
case 'c': value = (char)br.ReadByte(); break;
case 'b': value = (sbyte)br.ReadByte(); break;
case 'B': value = br.ReadByte(); break;
case 'u': value = ReadBinaryChar(br); break;
case 'h': value = br.ReadInt16(); break;
case 'H': value = br.ReadUInt16(); break;
case 'i': value = br.ReadInt32(); break;
case 'I': value = br.ReadUInt32(); break;
case 'l': value = br.ReadInt32(); break;
case 'L': value = br.ReadUInt32(); break;
case 'f': value = br.ReadSingle(); break;
case 'd': value = br.ReadDouble(); break;
default: throw new InvalidOperationException(); // should never happen
}
_data.Append(value);
}
}
// a version of FromStream that overwrites starting at 'index'
internal void FromStream(Stream ms, int index) {
BinaryReader br = new BinaryReader(ms);
for (int i = index; i < ms.Length / itemsize + index; i++) {
object value;
switch (_typeCode) {
case 'c': value = (char)br.ReadByte(); break;
case 'b': value = (sbyte)br.ReadByte(); break;
case 'B': value = br.ReadByte(); break;
case 'u': value = ReadBinaryChar(br); break;
case 'h': value = br.ReadInt16(); break;
case 'H': value = br.ReadUInt16(); break;
case 'i': value = br.ReadInt32(); break;
case 'I': value = br.ReadUInt32(); break;
case 'l': value = br.ReadInt32(); break;
case 'L': value = br.ReadUInt32(); break;
case 'f': value = br.ReadSingle(); break;
case 'd': value = br.ReadDouble(); break;
default: throw new InvalidOperationException(); // should never happen
}
_data.SetData(i, value);
}
}
// a version of FromStream that overwrites up to 'nbytes' bytes, starting at 'index'
// Returns the number of bytes written.
internal long FromStream(Stream ms, int index, int nbytes) {
BinaryReader br = new BinaryReader(ms);
if (nbytes <= 0) {
return 0;
}
int len = Math.Min((int)(ms.Length - ms.Position), nbytes);
for (int i = index; i < len / itemsize + index; i++) {
object value;
switch (_typeCode) {
case 'c': value = (char)br.ReadByte(); break;
case 'b': value = (sbyte)br.ReadByte(); break;
case 'B': value = br.ReadByte(); break;
case 'u':
value = ReadBinaryChar(br);
break;
case 'h': value = br.ReadInt16(); break;
case 'H': value = br.ReadUInt16(); break;
case 'i': value = br.ReadInt32(); break;
case 'I': value = br.ReadUInt32(); break;
case 'l': value = br.ReadInt32(); break;
case 'L': value = br.ReadUInt32(); break;
case 'f': value = br.ReadSingle(); break;
case 'd': value = br.ReadDouble(); break;
default: throw new InvalidOperationException(); // should never happen
}
_data.SetData(i, value);
}
if (len % itemsize > 0) {
// we have some extra bytes that we need to do a partial read on.
byte[] curBytes = ToBytes(len / itemsize + index);
for (int i = 0; i < len % itemsize; i++) {
curBytes[i] = br.ReadByte();
}
_data.SetData(len / itemsize + index, FromBytes(curBytes));
}
return len;
}
// br.ReadChar() doesn't read 16-bit chars, it reads 8-bit chars.
private static object ReadBinaryChar(BinaryReader br) {
object value;
byte byteVal = br.ReadByte();
value = (char)((br.ReadByte() << 8) | byteVal);
return value;
}
private byte[] ToBytes(int index) {
switch(_typeCode) {
case 'c': return new[] { (byte)(char)_data.GetData(index) };
case 'b': return new[] { (byte)(sbyte)_data.GetData(index) };
case 'B': return new[] { (byte)_data.GetData(index) };
case 'u': return BitConverter.GetBytes((char)_data.GetData(index));
case 'h': return BitConverter.GetBytes((short)_data.GetData(index));
case 'H': return BitConverter.GetBytes((ushort)_data.GetData(index));
case 'l':
case 'i': return BitConverter.GetBytes((int)_data.GetData(index));
case 'L':
case 'I': return BitConverter.GetBytes((uint)_data.GetData(index));
case 'f': return BitConverter.GetBytes((float)_data.GetData(index));
case 'd': return BitConverter.GetBytes((double)_data.GetData(index));
default:
throw PythonOps.ValueError("Bad type code (expected one of 'c', 'b', 'B', 'u', 'H', 'h', 'i', 'I', 'l', 'L', 'f', 'd')");
}
}
private object FromBytes(byte[] bytes) {
switch (_typeCode) {
case 'c': return (char)bytes[0];
case 'b': return (sbyte)bytes[0];
case 'B': return bytes[0];
case 'u': return BitConverter.ToChar(bytes, 0);
case 'h': return BitConverter.ToInt16(bytes, 0);
case 'H': return BitConverter.ToUInt16(bytes, 0);
case 'l':
case 'i': return BitConverter.ToInt32(bytes, 0);
case 'L':
case 'I': return BitConverter.ToUInt32(bytes, 0);
case 'f': return BitConverter.ToSingle(bytes, 0);
case 'd': return BitConverter.ToDouble(bytes, 0);
default:
throw PythonOps.ValueError("Bad type code (expected one of 'c', 'b', 'B', 'u', 'H', 'h', 'i', 'I', 'l', 'L', 'f', 'd')");
}
}
private class ArrayData : ArrayData {
private T[] _data;
private int _count;
private GCHandle? _dataHandle;
public ArrayData() {
GC.SuppressFinalize(this);
_data = new T[8];
}
private ArrayData(int size) {
GC.SuppressFinalize(this);
_data = new T[size];
_count = size;
}
~ArrayData() {
Debug.Assert(_dataHandle.HasValue);
_dataHandle.Value.Free();
}
public T[] Data {
get {
return _data;
}
set {
_data = value;
}
}
public override object GetData(int index) {
return _data[index];
}
public override void SetData(int index, object value) {
_data[index] = GetValue(value);
}
private static T GetValue(object value) {
if (!(value is T)) {
object newVal;
if (!Converter.TryConvert(value, typeof(T), out newVal)) {
if (value != null && typeof(T).IsPrimitive && typeof(T) != typeof(char))
throw PythonOps.OverflowError("couldn't convert {1} to {0}",
DynamicHelpers.GetPythonTypeFromType(typeof(T)).Name,
DynamicHelpers.GetPythonType(value).Name);
throw PythonOps.TypeError("expected {0}, got {1}",
DynamicHelpers.GetPythonTypeFromType(typeof(T)).Name,
DynamicHelpers.GetPythonType(value).Name);
}
value = newVal;
}
return (T)value;
}
public override void Append(object value) {
EnsureSize(_count + 1);
_data[_count++] = GetValue(value);
}
public void EnsureSize(int size) {
if (_data.Length < size) {
Array.Resize(ref _data, _data.Length * 2);
if (_dataHandle != null) {
_dataHandle.Value.Free();
_dataHandle = null;
GC.SuppressFinalize(this);
}
}
}
public override int Count(object value) {
T other = GetValue(value);
int count = 0;
for (int i = 0; i < _count; i++) {
if (_data[i].Equals(other)) {
count++;
}
}
return count;
}
public override void Insert(int index, object value) {
EnsureSize(_count + 1);
if (index < _count) {
Array.Copy(_data, index, _data, index + 1, _count - index);
}
_data[index] = GetValue(value);
_count++;
}
public override int Index(object value) {
T other = GetValue(value);
for (int i = 0; i < _count; i++) {
if (_data[i].Equals(other)) return i;
}
return -1;
}
public override void Remove(object value) {
T other = GetValue(value);
for (int i = 0; i < _count; i++) {
if (_data[i].Equals(other)) {
RemoveAt(i);
return;
}
}
throw PythonOps.ValueError("couldn't find value to remove");
}
public override void RemoveAt(int index) {
_count--;
if (index < _count) {
Array.Copy(_data, index + 1, _data, index, _count - index);
}
}
public override void Swap(int x, int y) {
T temp = _data[x];
_data[x] = _data[y];
_data[y] = temp;
}
public override int Length {
get {
return _count;
}
set {
_count = value;
}
}
public override void Clear() {
_count = 0;
}
public override bool CanStore(object value) {
object tmp;
if (!(value is T) && !Converter.TryConvert(value, typeof(T), out tmp))
return false;
return true;
}
public override Type StorageType {
get { return typeof(T); }
}
public override IntPtr GetAddress() {
// slightly evil to pin our data array but it's only used in rare
// interop cases. If this becomes a problem we can move the allocation
// onto the unmanaged heap if we have full trust via a different subclass
// of ArrayData.
if (!_dataHandle.HasValue) {
_dataHandle = GCHandle.Alloc(_data, GCHandleType.Pinned);
GC.ReRegisterForFinalize(this);
}
return _dataHandle.Value.AddrOfPinnedObject();
}
public override ArrayData Multiply(int count) {
var res = new ArrayData(count * _count);
if (count != 0) {
Array.Copy(_data, res._data, _count);
int newCount = count * _count;
int block = _count;
int pos = _count;
while (pos < newCount) {
Array.Copy(res._data, 0, res._data, pos, Math.Min(block, newCount - pos));
pos += block;
block *= 2;
}
}
return res;
}
}
#region IValueEquality Members
#if CLR2
int IValueEquality.GetValueHashCode() {
throw PythonOps.TypeError("unhashable type");
}
bool IValueEquality.ValueEquals(object other) {
array pa = other as array;
if (pa == null) return false;
if (_data.Length != pa._data.Length) return false;
for (int i = 0; i < _data.Length; i++) {
if (!_data.GetData(i).Equals(pa._data.GetData(i))) {
return false;
}
}
return true;
}
#endif
#endregion
#region IStructuralEquatable Members
public const object __hash__ = null;
int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) {
IStructuralEquatable dataTuple;
switch(_typeCode) {
case 'c': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'b': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'B': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'u': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'h': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'H': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'l':
case 'i': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'L':
case 'I': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'f': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'd': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
default:
throw PythonOps.ValueError("Bad type code (expected one of 'c', 'b', 'B', 'u', 'H', 'h', 'i', 'I', 'l', 'L', 'f', 'd')");
}
return dataTuple.GetHashCode(comparer);
}
bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) {
array pa = other as array;
if (pa == null) return false;
if (_data.Length != pa._data.Length) return false;
for (int i = 0; i < _data.Length; i++) {
if (!comparer.Equals(_data.GetData(i), pa._data.GetData(i))) {
return false;
}
}
return true;
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator() {
for (int i = 0; i < _data.Length; i++) {
yield return _data.GetData(i);
}
}
#endregion
#region ICodeFormattable Members
public virtual string/*!*/ __repr__(CodeContext/*!*/ context) {
string res = "array('" + typecode.ToString() + "'";
if (_data.Length == 0) {
return res + ")";
}
StringBuilder sb = new StringBuilder(res);
if (_typeCode == 'c' || _typeCode == 'u') {
char quote = '\'';
string s = new string(((ArrayData)_data).Data, 0, _data.Length);
if (s.IndexOf('\'') != -1 && s.IndexOf('\"') == -1) {
quote = '\"';
}
if (_typeCode == 'u') {
sb.Append(", u");
} else {
sb.Append(", ");
}
sb.Append(quote);
bool isUnicode = false;
sb.Append(StringOps.ReprEncode(s, quote, ref isUnicode));
sb.Append(quote);
sb.Append(")");
} else {
sb.Append(", [");
for (int i = 0; i < _data.Length; i++) {
if (i > 0) {
sb.Append(", ");
}
sb.Append(PythonOps.Repr(context, this[i]));
}
sb.Append("])");
}
return sb.ToString();
}
#endregion
#region IWeakReferenceable Members
WeakRefTracker IWeakReferenceable.GetWeakRef() {
return _tracker;
}
bool IWeakReferenceable.SetWeakRef(WeakRefTracker value) {
_tracker = value;
return true;
}
void IWeakReferenceable.SetFinalizer(WeakRefTracker value) {
_tracker = value;
}
#endregion
#region IPythonContainer Members
public int __len__() {
return _data.Length;
}
public bool __contains__(object value) {
return _data.Index(value) != -1;
}
#endregion
#region IRichComparable Members
private bool TryCompare(object other, out int res) {
array pa = other as array;
if (pa == null || pa.typecode != typecode) {
res = 0;
return false;
}
if (pa._data.Length != _data.Length) {
res = _data.Length - pa._data.Length;
} else {
res = 0;
for (int i = 0; i < pa._data.Length && res == 0; i++) {
res = PythonOps.Compare(_data.GetData(i), pa._data.GetData(i));
}
}
return true;
}
[return: MaybeNotImplemented]
public static object operator >(array self, object other) {
int res;
if (!self.TryCompare(other, out res)) {
return NotImplementedType.Value;
}
return ScriptingRuntimeHelpers.BooleanToObject(res > 0);
}
[return: MaybeNotImplemented]
public static object operator <(array self, object other) {
int res;
if (!self.TryCompare(other, out res)) {
return NotImplementedType.Value;
}
return ScriptingRuntimeHelpers.BooleanToObject(res < 0);
}
[return: MaybeNotImplemented]
public static object operator >=(array self, object other) {
int res;
if (!self.TryCompare(other, out res)) {
return NotImplementedType.Value;
}
return ScriptingRuntimeHelpers.BooleanToObject(res >= 0);
}
[return: MaybeNotImplemented]
public static object operator <=(array self, object other) {
int res;
if (!self.TryCompare(other, out res)) {
return NotImplementedType.Value;
}
return ScriptingRuntimeHelpers.BooleanToObject(res <= 0);
}
#endregion
#region ICollection Members
void ICollection.CopyTo(Array array, int index) {
throw new NotImplementedException();
}
int ICollection.Count {
get { return __len__(); }
}
bool ICollection.IsSynchronized {
get { return false; }
}
object ICollection.SyncRoot {
get { return this; }
}
#endregion
#region IList Members
int IList.IndexOf(object item) {
return _data.Index(item);
}
void IList.Insert(int index, object item) {
insert(index, item);
}
void IList.RemoveAt(int index) {
__delitem__(index);
}
#endregion
#region ICollection Members
void ICollection.Add(object item) {
append(item);
}
void ICollection.Clear() {
__delitem__(new Slice(null, null));
}
bool ICollection.Contains(object item) {
return __contains__(item);
}
void ICollection.CopyTo(object[] array, int arrayIndex) {
throw new NotImplementedException();
}
int ICollection.Count {
get { return __len__(); }
}
bool ICollection.IsReadOnly {
get { return false; }
}
bool ICollection.Remove(object item) {
try {
remove(item);
return true;
} catch (ArgumentException) {
return false;
}
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator() {
for (int i = 0; i < _data.Length; i++) {
yield return _data.GetData(i);
}
}
#endregion
}
}
}