// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Scripting.Runtime;
using Microsoft.Scripting.Utils;
using IronPython.Runtime;
using IronPython.Runtime.Exceptions;
using IronPython.Runtime.Operations;
using IronPython.Runtime.Types;
using SpecialName = System.Runtime.CompilerServices.SpecialNameAttribute;
using NotNull = Microsoft.Scripting.Runtime.NotNullAttribute;
[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));
public static readonly string typecodes = "bBuhHiIlLqQfd";
private static array ArrayReconstructor(CodeContext context, [NotNull]PythonType cls, [NotNull]string typecode, int mformat_code, [NotNull]Bytes items) {
if (typecode.Length != 1)
throw PythonOps.TypeError("expected character, got {0}", PythonOps.GetPythonTypeName(typecode));
if (!typecodes.Contains(typecode))
throw PythonOps.ValueError("bad typecode (must be b, B, u, h, H, i, I, l, L, q, Q, f or d)");
var actualTypeCode = MachineFormatToTypeCode(mformat_code, out bool isBigEndian, out string? encoding);
var arrayType = DynamicHelpers.GetPythonTypeFromType(typeof(array));
if (!cls.IsSubclassOf(arrayType)) {
throw PythonOps.TypeError($"{cls} is not a subtype of array.array");
}
array res;
if (cls == arrayType) {
res = new array(actualTypeCode);
} else if (cls.CreateInstance(context, actualTypeCode) is array arr) {
res = arr;
} else {
throw PythonOps.TypeError($"{cls} is not a subtype of array.array");
}
if (encoding == null) {
res.frombytes(items);
if (isBigEndian) res.byteswap();
} else {
res.fromunicode(context, StringOps.RawDecode(context, items, encoding, null));
}
return res;
static string MachineFormatToTypeCode(int machineFormat, out bool isBigEndian, out string? encoding) {
isBigEndian = machineFormat % 2 == 1;
encoding = machineFormat switch
{
18 => "UTF-16-LE",
19 => "UTF-16-BE",
20 => "UTF-32-LE",
21 => "UTF-32-BE",
_ => null,
};
return machineFormat switch
{
0 => "B",
1 => "b",
2 => "H",
3 => "H",
4 => "h",
5 => "h",
6 => "I",
7 => "I",
8 => "i",
9 => "i",
10 => "Q",
11 => "Q",
12 => "q",
13 => "q",
14 => "f",
15 => "f",
16 => "d",
17 => "d",
18 => "u",
19 => "u",
20 => "u",
21 => "u",
_ => throw PythonOps.ValueError("invalid machine code format"),
};
}
}
public static readonly BuiltinFunction _array_reconstructor = BuiltinFunction.MakeFunction(nameof(_array_reconstructor), ArrayUtils.ConvertAll(typeof(ArrayModule).GetMember(nameof(ArrayReconstructor), BindingFlags.NonPublic | BindingFlags.Static), x => (MethodBase)x), typeof(ArrayModule));
[PythonType]
public class array : IEnumerable, IWeakReferenceable, ICollection, ICodeFormattable, IList, IStructuralEquatable, IBufferProtocol {
// _data is readonly to ensure proper size locking during buffer exports
private readonly ArrayData _data;
private readonly char _typeCode;
private WeakRefTracker? _tracker;
public array([NotNull]string type) {
if (type == null || type.Length != 1) {
throw PythonOps.TypeError("expected character, got {0}", PythonOps.GetPythonTypeName(type));
}
_typeCode = type[0];
_data = CreateData(_typeCode);
}
public array([NotNull]string type, [NotNull]Bytes initializer) : this(type) {
frombytes(initializer);
}
public array([NotNull]string type, [NotNull]ByteArray initializer) : this(type) {
frombytes(initializer);
}
public array([NotNull]string type, [NotNull]array initializer) : this(type) {
if (_typeCode != 'u' && initializer._typeCode == 'u') {
throw PythonOps.TypeError("cannot use a unicode array to initialize an array with typecode '{0}'", _typeCode);
}
ExtendIter(initializer);
}
public array([NotNull]string type, object? initializer) : this(type) {
if (_typeCode != 'u') {
if (initializer is string || initializer is Extensible)
throw PythonOps.TypeError("cannot use a str to initialize an array with typecode '{0}'", _typeCode);
if (initializer is array arr && arr._typeCode == 'u')
throw PythonOps.TypeError("cannot use a unicode array to initialize an array with typecode '{0}'", _typeCode);
}
ExtendIter(initializer);
}
private array(char typeCode, ArrayData data) {
_typeCode = typeCode;
_data = data;
}
private static ArrayData CreateData(char typecode) {
return (typecode) switch
{
'b' => new ArrayData(),
'B' => new ArrayData(),
'u' => new ArrayData(),
'h' => new ArrayData(),
'H' => new ArrayData(),
'i' => new ArrayData(),
'I' => new ArrayData(),
'l' => new ArrayData(),
'L' => new ArrayData(),
'q' => new ArrayData(),
'Q' => new ArrayData(),
'f' => new ArrayData(),
'd' => new ArrayData(),
_ => throw PythonOps.ValueError("bad typecode (must be b, B, u, h, H, i, I, l, L, q, Q, f or d)"),
};
}
[SpecialName]
public array InPlaceAdd([NotNull]array other) {
ExtendArray(other);
return this;
}
public static array operator +([NotNull]array self, [NotNull]array other) {
if (self.typecode != other.typecode) throw PythonOps.TypeError("cannot add different typecodes");
array res = new array(self.typecode);
res.ExtendArray(self);
res.ExtendArray(other);
return res;
}
[SpecialName]
public array InPlaceMultiply(int value) {
if (value <= 0) {
_data.Clear();
} else {
var myData = __copy__();
for (int i = 0; i < (value - 1); i++) {
ExtendArray(myData);
}
}
return this;
}
public static array operator *([NotNull]array array, int value) {
if ((BigInteger)value * array.__len__() * array.itemsize > SysModule.maxsize) {
throw PythonOps.MemoryError("");
}
if (value <= 0) {
return new array(array.typecode);
}
return new array(array._typeCode, array._data.Multiply(value));
}
public static array operator *([NotNull]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, [NotNull]array array) {
return array * value;
}
public static array operator *(BigInteger value, [NotNull]array array) {
return array * value;
}
public void append(object? iterable) {
_data.Add(iterable);
}
internal IntPtr GetArrayAddress() {
return _data.GetAddress();
}
public PythonTuple buffer_info() {
return PythonTuple.MakeTuple(
_data.GetAddress().ToPython(),
_data.Count
);
}
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) => _data.CountItems(x);
private void ExtendArray(array pa) {
if (_typeCode != pa._typeCode) {
throw PythonOps.TypeError("can only extend with array of same kind");
}
if (pa._data.Count == 0) return;
_data.AddRange(pa._data);
}
private void ExtendIter(object? iterable) {
if (_typeCode == 'B' && iterable is Bytes bytes) {
FromBytes(bytes);
return;
}
if (_typeCode == 'u' && iterable is string str) {
FromUnicode(str);
return;
}
IEnumerator ie = PythonOps.GetEnumerator(iterable);
while (ie.MoveNext()) {
append(ie.Current);
}
}
public void extend(object? iterable) {
if (iterable is array pa) {
ExtendArray(pa);
} else {
ExtendIter(iterable);
}
}
public void fromlist([NotNull]PythonList 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,
PythonOps.GetPythonTypeName(ie.Current));
}
items.Add(ie.Current);
}
ExtendIter(items);
}
public void fromfile(CodeContext/*!*/ context, [NotNull]PythonIOModule._IOBase f, int n) {
int bytesNeeded = n * itemsize;
Bytes bytes = (Bytes)f.read(context, bytesNeeded);
frombytes(bytes);
if (bytes.Count < bytesNeeded) throw PythonOps.EofError("file not large enough");
}
public void frombytes([NotNull]IBufferProtocol buffer) {
using IPythonBuffer pb = buffer.GetBuffer();
if ((pb.NumBytes() % itemsize) != 0) throw PythonOps.ValueError("bytes length not a multiple of item size");
if (buffer is Bytes b) {
FromBytes(b);
return;
}
// TODO: eliminate data copy
FromStream(new MemoryStream(pb.ToArray(), false));
}
private void FromBytes(Bytes b) {
FromStream(new MemoryStream(b.UnsafeByteArray, false));
}
public void fromstring(CodeContext/*!*/ context, [NotNull]Bytes b) {
PythonOps.Warn(context, PythonExceptions.DeprecationWarning, "fromstring() is deprecated. Use frombytes() instead.");
if ((b.Count % itemsize) != 0) throw PythonOps.ValueError("bytes length not a multiple of itemsize");
FromBytes(b);
}
public void fromstring(CodeContext/*!*/ context, [NotNull]string s) {
PythonOps.Warn(context, PythonExceptions.DeprecationWarning, "fromstring() is deprecated. Use frombytes() instead.");
if ((s.Length % itemsize) != 0) throw PythonOps.ValueError("bytes 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 fromunicode(CodeContext/*!*/ context, [NotNull]string s) {
if (_typeCode != 'u') {
throw PythonOps.ValueError("fromunicode() may only be called on type 'u' arrays");
}
FromUnicode(s);
}
private void FromUnicode(string s) {
ArrayData data = (ArrayData)_data;
data.AddRange(s);
}
public int index(object? x) {
if (x == null) throw PythonOps.ValueError("got None, expected value");
int res = _data.IndexOf(x);
if (res == -1) throw PythonOps.ValueError("x not found");
return res;
}
public void insert(int i, [NotNull]object x) {
if (i > _data.Count) i = _data.Count;
if (i < 0) i = _data.Count + i;
if (i < 0) i = 0;
_data.Insert(i, x);
}
public int itemsize {
get {
switch (_typeCode) {
case 'b': // signed byte
case 'B': // unsigned byte
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 'q': // signed long long
case 'Q': // unsigned long long
case 'd': // double
return 8;
default:
throw new InvalidOperationException(); // should never happen
}
}
}
public object pop() {
return pop(-1);
}
public object pop(int i) {
i = PythonOps.FixIndex(i, _data.Count);
object res = _data[i]!;
_data.RemoveAt(i);
return res;
}
public void remove(object? value) {
if (!_data.Remove(value)) throw PythonOps.ValueError("couldn't find value to remove");
}
public void reverse()
=> _data.Reverse();
public virtual object this[int index] {
get {
index = PythonOps.FixIndex(index, _data.Count);
switch (_typeCode) {
case 'b': return (int)((ArrayData)_data)[index];
case 'B': return (int)((ArrayData)_data)[index];
case 'u': return ScriptingRuntimeHelpers.CharToString(((ArrayData)_data)[index]);
case 'h': return (int)((ArrayData)_data)[index];
case 'H': return (int)((ArrayData)_data)[index];
case 'i': return ((ArrayData)_data)[index];
case 'I': return (BigInteger)((ArrayData)_data)[index];
case 'l': return ((ArrayData)_data)[index];
case 'L': return (BigInteger)((ArrayData)_data)[index];
case 'q': return (BigInteger)((ArrayData)_data)[index];
case 'Q': return (BigInteger)((ArrayData)_data)[index];
case 'f': return (double)((ArrayData)_data)[index];
case 'd': return ((ArrayData)_data)[index];
default: throw new InvalidOperationException(); // should never happen
}
}
[param: AllowNull]
set {
index = PythonOps.FixIndex(index, _data.Count);
_data[index] = value;
}
}
internal byte[] RawGetItem(int index) {
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
switch (_typeCode) {
case 'b': bw.Write(((ArrayData)_data)[index]); break;
case 'B': bw.Write(((ArrayData)_data)[index]); break;
case 'u': WriteBinaryChar(bw, ((ArrayData)_data)[index]); break;
case 'h': bw.Write(((ArrayData)_data)[index]); break;
case 'H': bw.Write(((ArrayData)_data)[index]); break;
case 'i': bw.Write(((ArrayData)_data)[index]); break;
case 'I': bw.Write(((ArrayData)_data)[index]); break;
case 'l': bw.Write(((ArrayData)_data)[index]); break;
case 'L': bw.Write(((ArrayData)_data)[index]); break;
case 'q': bw.Write(((ArrayData)_data)[index]); break;
case 'Q': bw.Write(((ArrayData)_data)[index]); break;
case 'f': bw.Write(((ArrayData)_data)[index]); break;
case 'd': bw.Write(((ArrayData)_data)[index]); break;
default: throw new InvalidOperationException(); // should never happen
}
return ms.ToArray();
}
public void __delitem__(int index) {
_data.RemoveAt(PythonOps.FixIndex(index, _data.Count));
}
public void __delitem__([NotNull]Slice slice) {
_data.RemoveSlice(slice);
}
[System.Diagnostics.CodeAnalysis.NotNull]
public object? this[[NotNull]Slice index] {
get {
index.Indices(_data.Count, out int start, out int stop, out int step);
array pa = new array(ScriptingRuntimeHelpers.CharToString(_typeCode));
if (step < 0) {
for (int i = start; i > stop; i += step) {
pa._data.Add(_data[i]);
}
} else {
for (int i = start; i < stop; i += step) {
pa._data.Add(_data[i]);
}
}
return pa;
}
set {
array arr = CheckSliceAssignType(value);
if (index.step != null) {
if (Object.ReferenceEquals(value, this)) value = this.tolist();
index.DoSliceAssign(SliceAssign, _data.Count, value);
} else {
index.Indices(_data.Count, out int start, out int stop, out int step);
if (stop < start) {
stop = start;
}
SliceNoStep(arr, start, stop);
}
}
}
private array CheckSliceAssignType([System.Diagnostics.CodeAnalysis.NotNull]object? value) {
if (!(value is array pa)) {
throw PythonOps.TypeError("can only assign array (not \"{0}\") to array slice", PythonOps.GetPythonTypeName(value));
} else if (pa._typeCode != _typeCode) {
throw PythonOps.TypeError("bad argument type for built-in operation");
}
return pa;
}
private void SliceNoStep(array arr, int start, int stop) {
// replace between start & stop w/ values
int count = stop - start;
if (count == 0 && arr._data.Count == 0) return;
if (ReferenceEquals(this, arr)) {
arr = new array(typecode, this);
}
_data.InsertRange(start, count, arr._data);
}
public array __copy__() {
return new array(typecode, this);
}
public array __deepcopy__(object? memo) {
return __copy__();
}
public PythonTuple __reduce_ex__(CodeContext context, int version) {
PythonOps.TryGetBoundAttr(context, this, "__dict__", out object? dictObject);
var dict = dictObject as PythonDictionary;
if (version < 3) {
return PythonTuple.MakeTuple(
DynamicHelpers.GetPythonType(this),
PythonTuple.MakeTuple(
typecode,
tolist()
),
dict
);
}
return PythonTuple.MakeTuple(
_array_reconstructor,
PythonTuple.MakeTuple(
DynamicHelpers.GetPythonType(this),
typecode,
TypeCodeToMachineFormat(_typeCode),
tobytes()
),
dict);
static int TypeCodeToMachineFormat(char typeCode) {
return typeCode switch
{
'b' => 1,
'B' => 0,
'u' => 18,
'h' => 4,
'H' => 2,
'i' => 8,
'I' => 6,
'l' => 8,
'L' => 6,
'q' => 12,
'Q' => 10,
'f' => 14,
'd' => 16,
_ => throw new InvalidOperationException(),// should never happen
};
}
}
private void SliceAssign(int index, object? value) {
_data[index] = value;
}
public void tofile(CodeContext context, [NotNull]PythonIOModule._IOBase f) {
f.write(context, tobytes());
}
public PythonList tolist() {
PythonList res = new PythonList();
for (int i = 0; i < _data.Count; i++) {
res.AddNoLock(this[i]);
}
return res;
}
public Bytes tobytes() {
Stream s = ToStream();
byte[] bytes = new byte[s.Length];
s.Read(bytes, 0, (int)s.Length);
return Bytes.Make(bytes);
}
public Bytes tostring(CodeContext/*!*/ context) {
PythonOps.Warn(context, PythonExceptions.DeprecationWarning, "tostring() is deprecated. Use tobytes() instead.");
return tobytes();
}
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.Count);
}
public string/*!*/ typecode {
get { return ScriptingRuntimeHelpers.CharToString(_typeCode); }
}
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);
for (int i = 0; i < _data.Count; i++) {
switch (_typeCode) {
case 'b': bw.Write(((ArrayData)_data)[i]); break;
case 'B': bw.Write(((ArrayData)_data)[i]); break;
case 'u': WriteBinaryChar(bw, ((ArrayData)_data)[i]); break;
case 'h': bw.Write(((ArrayData)_data)[i]); break;
case 'H': bw.Write(((ArrayData)_data)[i]); break;
case 'i': bw.Write(((ArrayData)_data)[i]); break;
case 'I': bw.Write(((ArrayData)_data)[i]); break;
case 'l': bw.Write(((ArrayData)_data)[i]); break;
case 'L': bw.Write(((ArrayData)_data)[i]); break;
case 'q': bw.Write(((ArrayData)_data)[i]); break;
case 'Q': bw.Write(((ArrayData)_data)[i]); break;
case 'f': bw.Write(((ArrayData)_data)[i]); break;
case 'd': bw.Write(((ArrayData)_data)[i]); break;
default: throw new InvalidOperationException(); // should never happen
}
}
}
private static void WriteBinaryChar(BinaryWriter bw, char c) {
bw.Write((ushort)c);
}
internal byte[] ToByteArray() {
if (_data is ArrayData data) {
Debug.Assert(_typeCode == 'B');
var res = new byte[data.Count];
data.CopyTo(res, 0);
return res;
}
return ToStream().ToArray();
}
internal void Clear() {
_data.Clear();
}
internal void FromStream(Stream ms) {
BinaryReader br = new BinaryReader(ms);
if (_data is ArrayData data) {
Debug.Assert(_typeCode == 'B');
var length = (int)ms.Length;
data.AddRange(br.ReadBytes(length));
return;
}
for (int i = 0; i < ms.Length / itemsize; i++) {
switch (_typeCode) {
case 'b': ((ArrayData)_data).Add((sbyte)br.ReadByte()); break;
case 'B': ((ArrayData)_data).Add(br.ReadByte()); break;
case 'u': ((ArrayData)_data).Add(ReadBinaryChar(br)); break;
case 'h': ((ArrayData)_data).Add(br.ReadInt16()); break;
case 'H': ((ArrayData)_data).Add(br.ReadUInt16()); break;
case 'i': ((ArrayData)_data).Add(br.ReadInt32()); break;
case 'I': ((ArrayData)_data).Add(br.ReadUInt32()); break;
case 'l': ((ArrayData)_data).Add(br.ReadInt32()); break;
case 'L': ((ArrayData)_data).Add(br.ReadUInt32()); break;
case 'q': ((ArrayData)_data).Add(br.ReadInt64()); break;
case 'Q': ((ArrayData)_data).Add(br.ReadUInt64()); break;
case 'f': ((ArrayData)_data).Add(br.ReadSingle()); break;
case 'd': ((ArrayData)_data).Add(br.ReadDouble()); break;
default: throw new InvalidOperationException(); // should never happen
}
}
}
// 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++) {
switch (_typeCode) {
case 'b': ((ArrayData)_data)[i] = (sbyte)br.ReadByte(); break;
case 'B': ((ArrayData)_data)[i] = br.ReadByte(); break;
case 'u': ((ArrayData)_data)[i] = ReadBinaryChar(br); break;
case 'h': ((ArrayData)_data)[i] = br.ReadInt16(); break;
case 'H': ((ArrayData)_data)[i] = br.ReadUInt16(); break;
case 'i': ((ArrayData)_data)[i] = br.ReadInt32(); break;
case 'I': ((ArrayData)_data)[i] = br.ReadUInt32(); break;
case 'l': ((ArrayData)_data)[i] = br.ReadInt32(); break;
case 'L': ((ArrayData)_data)[i] = br.ReadUInt32(); break;
case 'q': ((ArrayData)_data)[i] = br.ReadInt64(); break;
case 'Q': ((ArrayData)_data)[i] = br.ReadUInt64(); break;
case 'f': ((ArrayData)_data)[i] = br.ReadSingle(); break;
case 'd': ((ArrayData)_data)[i] = br.ReadDouble(); break;
default: throw new InvalidOperationException(); // should never happen
}
}
}
// 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++) {
switch (_typeCode) {
case 'b': ((ArrayData)_data)[i] = (sbyte)br.ReadByte(); break;
case 'B': ((ArrayData)_data)[i] = br.ReadByte(); break;
case 'u': ((ArrayData)_data)[i] = ReadBinaryChar(br); break;
case 'h': ((ArrayData)_data)[i] = br.ReadInt16(); break;
case 'H': ((ArrayData)_data)[i] = br.ReadUInt16(); break;
case 'i': ((ArrayData)_data)[i] = br.ReadInt32(); break;
case 'I': ((ArrayData)_data)[i] = br.ReadUInt32(); break;
case 'l': ((ArrayData)_data)[i] = br.ReadInt32(); break;
case 'L': ((ArrayData)_data)[i] = br.ReadUInt32(); break;
case 'q': ((ArrayData)_data)[i] = br.ReadInt64(); break;
case 'Q': ((ArrayData)_data)[i] = br.ReadUInt64(); break;
case 'f': ((ArrayData)_data)[i] = br.ReadSingle(); break;
case 'd': ((ArrayData)_data)[i] = br.ReadDouble(); break;
default: throw new InvalidOperationException(); // should never happen
}
}
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[len / itemsize + index] = FromBytes(curBytes);
}
return len;
}
// br.ReadChar() doesn't read 16-bit chars, it reads 8-bit chars.
private static char ReadBinaryChar(BinaryReader br) {
byte byteVal = br.ReadByte();
return (char)((br.ReadByte() << 8) | byteVal);
}
private byte[] ToBytes(int index) {
switch (_typeCode) {
case 'b': return new[] { (byte)((ArrayData)_data)[index] };
case 'B': return new[] { ((ArrayData)_data)[index] };
case 'u': return BitConverter.GetBytes(((ArrayData)_data)[index]);
case 'h': return BitConverter.GetBytes(((ArrayData)_data)[index]);
case 'H': return BitConverter.GetBytes(((ArrayData)_data)[index]);
case 'i': return BitConverter.GetBytes(((ArrayData)_data)[index]);
case 'I': return BitConverter.GetBytes(((ArrayData)_data)[index]);
case 'l': return BitConverter.GetBytes(((ArrayData)_data)[index]);
case 'L': return BitConverter.GetBytes(((ArrayData)_data)[index]);
case 'q': return BitConverter.GetBytes(((ArrayData)_data)[index]);
case 'Q': return BitConverter.GetBytes(((ArrayData)_data)[index]);
case 'f': return BitConverter.GetBytes(((ArrayData)_data)[index]);
case 'd': return BitConverter.GetBytes(((ArrayData)_data)[index]);
default: throw new InvalidOperationException(); // should never happen
}
}
private object FromBytes(byte[] bytes) {
switch (_typeCode) {
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 'i': return BitConverter.ToInt32(bytes, 0);
case 'I': return BitConverter.ToUInt32(bytes, 0);
case 'l': return BitConverter.ToInt32(bytes, 0);
case 'L': return BitConverter.ToInt32(bytes, 0);
case 'q': return BitConverter.ToInt64(bytes, 0);
case 'Q': return BitConverter.ToInt64(bytes, 0);
case 'f': return BitConverter.ToSingle(bytes, 0);
case 'd': return BitConverter.ToDouble(bytes, 0);
default: throw new InvalidOperationException(); // should never happen
}
}
#region IStructuralEquatable Members
public const object __hash__ = null;
int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) {
IStructuralEquatable dataTuple;
switch (_typeCode) {
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 'i': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'I': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'l': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'L': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'q': dataTuple = PythonTuple.MakeTuple(((ArrayData)_data).Data); break;
case 'Q': 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 new InvalidOperationException(); // should never happen
}
return dataTuple.GetHashCode(comparer);
}
bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) {
if (!(other is array pa)) return false;
if (_data.Count != pa._data.Count) return false;
for (int i = 0; i < _data.Count; i++) {
if (!comparer.Equals(_data[i], pa._data[i])) {
return false;
}
}
return true;
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator() => new arrayiterator(this);
#endregion
#region ICodeFormattable Members
public virtual string/*!*/ __repr__(CodeContext/*!*/ context) {
string res = "array('" + typecode.ToString() + "'";
if (_data.Count == 0) {
return res + ")";
}
StringBuilder sb = new StringBuilder(res);
if (_typeCode == 'u') {
char quote = '\'';
string s = new string(((ArrayData)_data).Data, 0, _data.Count);
if (s.IndexOf('\'') != -1 && s.IndexOf('\"') == -1) {
quote = '\"';
}
sb.Append(", ");
sb.Append(quote);
sb.Append(StringOps.ReprEncode(s, quote));
sb.Append(quote);
sb.Append(")");
} else {
sb.Append(", [");
for (int i = 0; i < _data.Count; 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.Count;
}
public bool __contains__(object? value) {
return _data.Contains(value);
}
#endregion
#region IRichComparable Members
public static object? operator >([NotNull] array self, [NotNull] array other)
=> PythonOps.ArraysGreaterThan(DefaultContext.Default, self!, other!);
public static object? operator <([NotNull] array self, [NotNull] array other)
=> PythonOps.ArraysLessThan(DefaultContext.Default, self!, other!);
public static object? operator >=([NotNull] array self, [NotNull] array other)
=> PythonOps.ArraysGreaterThanOrEqual(DefaultContext.Default, self!, other!);
public static object? operator <=([NotNull] array self, [NotNull] array other)
=> PythonOps.ArraysLessThanOrEqual(DefaultContext.Default, self!, other!);
#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.IndexOf(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() => new arrayiterator(this);
#endregion
#region IBufferProtocol Members
IPythonBuffer IBufferProtocol.GetBuffer(BufferFlags flags) {
return _data.GetBuffer(this, _typeCode.ToString(), flags);
}
#endregion
}
[PythonType]
public sealed class arrayiterator : IEnumerator {
private int _index;
private readonly IList _array;
private bool _iterating;
internal arrayiterator(array a) {
_array = a;
Reset();
}
[PythonHidden]
public object Current => _array[_index];
public object __iter__() => this;
public object __reduce__(CodeContext context) {
object? iter;
context.TryLookupBuiltin("iter", out iter);
if (_iterating) {
return PythonTuple.MakeTuple(iter, PythonTuple.MakeTuple(_array), _index + 1);
}
return PythonTuple.MakeTuple(iter, PythonTuple.MakeTuple(PythonTuple.EMPTY));
}
public void __setstate__(int state) {
_index = state - 1;
_iterating = _index < _array.Count;
}
void IDisposable.Dispose() { }
[PythonHidden]
public bool MoveNext() {
if (_iterating) {
_index++;
_iterating = (_index < _array.Count);
}
return _iterating;
}
[PythonHidden]
public void Reset() {
_index = -1;
_iterating = true;
}
}
}
}