// 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.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Scripting.Runtime;
using Microsoft.Scripting.Utils;
using IronPython.Runtime.Operations;
using IronPython.Runtime.Types;
namespace IronPython.Runtime {
///
/// bytearray(string, encoding[, errors]) -> bytearray
/// bytearray(iterable) -> bytearray
///
/// Construct a mutable bytearray object from:
/// - an iterable yielding values in range(256), including:
/// + a list of integer values
/// + a bytes, bytearray, buffer, or array object
/// - a text string encoded using the specified encoding
///
/// bytearray([int]) -> bytearray
///
/// Construct a zero-initialized bytearray of the specified length.
/// (default=0)
///
[PythonType("bytearray")]
public class ByteArray : IList, ICodeFormattable, IBufferProtocol {
private List/*!*/ _bytes;
public ByteArray() {
_bytes = new List(0);
}
private ByteArray(List bytes) {
_bytes = bytes;
}
internal ByteArray(IEnumerable bytes) {
_bytes = new List(bytes);
}
public void __init__() {
_bytes = new List();
}
public void __init__(int source) {
_bytes = new List(source);
for (int i = 0; i < source; i++) {
_bytes.Add(0);
}
}
public void __init__(BigInteger source) {
__init__((int)source);
}
public void __init__([NotNull]IList/*!*/ source) {
_bytes = new List(source);
}
public void __init__(object source) {
__init__(GetBytes(source));
}
public void __init__([NotNull]string @string) {
throw PythonOps.TypeError("string argument without an encoding");
}
public void __init__(CodeContext/*!*/ context, [NotNull]string source, [NotNull]string encoding, [NotNull]string errors = "strict") {
_bytes = new List(StringOps.encode(context, source, encoding, errors));
}
internal static ByteArray Make(List bytes) {
return new ByteArray(bytes);
}
internal List UnsafeByteList {
[PythonHidden]
get => _bytes;
}
#region Public Mutable Sequence API
public void append(int item) {
lock (this) {
_bytes.Add(item.ToByteChecked());
}
}
public void append(object item) {
lock (this) {
_bytes.Add(GetByte(item));
}
}
public void extend([NotNull]IEnumerable/*!*/ seq) {
using (new OrderedLocker(this, seq)) {
// use the original count for if we're extending this w/ this
_bytes.AddRange(seq);
}
}
public void extend(object seq) {
// We don't make use of the length hint when extending the byte array.
// However, in order to match CPython behavior with invalid length hints we
// we need to go through the motions and get the length hint and attempt
// to convert it to an int.
PythonOps.TryInvokeLengthHint(DefaultContext.Default, seq, out int len);
extend(GetBytes(seq));
}
public void insert(int index, int value) {
lock (this) {
if (index >= Count) {
append(value);
return;
}
index = PythonOps.FixSliceIndex(index, Count);
_bytes.Insert(index, value.ToByteChecked());
}
}
public void insert(int index, object value) {
insert(index, Converter.ConvertToIndex(value));
}
public int pop() {
lock (this) {
if (Count == 0) {
throw PythonOps.IndexError("pop off of empty bytearray");
}
int res = _bytes[_bytes.Count - 1];
_bytes.RemoveAt(_bytes.Count - 1);
return res;
}
}
public int pop(int index) {
lock (this) {
if (Count == 0) {
throw PythonOps.IndexError("pop off of empty bytearray");
}
index = PythonOps.FixIndex(index, Count);
int ret = _bytes[index];
_bytes.RemoveAt(index);
return ret;
}
}
private void RemoveByte(byte value) {
var idx = _bytes.IndexOfByte(value, 0, _bytes.Count);
if (idx == -1)
throw PythonOps.ValueError("value not found in bytearray");
_bytes.RemoveAt(idx);
}
public void remove(int value) {
lock (this) {
RemoveByte(value.ToByteChecked());
}
}
public void remove(object value) {
lock (this) {
RemoveByte(GetByte(value));
}
}
public void reverse() {
lock (this) {
List reversed = new List();
for (int i = _bytes.Count - 1; i >= 0; i--) {
reversed.Add(_bytes[i]);
}
_bytes = reversed;
}
}
[SpecialName]
public ByteArray InPlaceAdd(ByteArray other) {
using (new OrderedLocker(this, other)) {
_bytes.AddRange(other._bytes);
return this;
}
}
[SpecialName]
public ByteArray InPlaceAdd(Bytes other) {
lock (this) {
_bytes.AddRange(other);
return this;
}
}
[SpecialName]
public ByteArray InPlaceAdd(MemoryView other) {
lock (this) {
_bytes.AddRange(other.tobytes());
return this;
}
}
[SpecialName]
public ByteArray InPlaceMultiply(int len) {
lock (this) {
_bytes = (this * len)._bytes;
return this;
}
}
#endregion
#region Public Python API surface
public ByteArray/*!*/ capitalize() {
lock (this) {
return new ByteArray(_bytes.Capitalize());
}
}
public ByteArray/*!*/ center(int width) => center(width, (byte)' ');
public ByteArray/*!*/ center(int width, [BytesConversion]IList fillchar)
=> center(width, fillchar.ToByte("center", 2));
private ByteArray center(int width, byte fillchar) {
lock (this) {
List res = _bytes.TryCenter(width, fillchar);
if (res == null) {
return CopyThis();
}
return new ByteArray(res);
}
}
public void clear() => Clear();
public ByteArray copy() => CopyThis();
public int count([BytesConversion]IList/*!*/ sub) => count(sub, null, null);
public int count([BytesConversion]IList/*!*/ sub, int? start) => count(sub, start, null);
public int count([BytesConversion]IList/*!*/ sub, int? start, int? end) {
lock (this) {
return _bytes.CountOf(sub, start ?? 0, end ?? _bytes.Count);
}
}
public int count(int @byte) => count(@byte, null, null);
public int count(int @byte, int? start) => count(@byte, start, null);
public int count(int @byte, int? start, int? end) => count(new[] { @byte.ToByteChecked() }, start, end);
public string decode(CodeContext/*!*/ context, [NotNull]string encoding = "utf-8", [NotNull]string errors = "strict") {
lock (this) {
return StringOps.RawDecode(context, _bytes, encoding, errors);
}
}
public string decode(CodeContext/*!*/ context, [NotNull]Encoding encoding, [NotNull]string errors = "strict") {
lock (this) {
return StringOps.DoDecode(context, _bytes, errors, StringOps.GetEncodingName(encoding, normalize: false), encoding);
}
}
public bool endswith([BytesConversion]IList/*!*/ suffix) {
lock (this) {
return _bytes.EndsWith(suffix);
}
}
public bool endswith([BytesConversion]IList/*!*/ suffix, int start) {
lock (this) {
return _bytes.EndsWith(suffix, start);
}
}
public bool endswith([BytesConversion]IList/*!*/ suffix, int start, int end) {
lock (this) {
return _bytes.EndsWith(suffix, start, end);
}
}
public bool endswith(PythonTuple/*!*/ suffix) {
lock (this) {
return _bytes.EndsWith(suffix);
}
}
public bool endswith(PythonTuple/*!*/ suffix, int start) {
lock (this) {
return _bytes.EndsWith(suffix, start);
}
}
public bool endswith(PythonTuple/*!*/ suffix, int start, int end) {
lock (this) {
return _bytes.EndsWith(suffix, start, end);
}
}
public ByteArray/*!*/ expandtabs() {
return expandtabs(8);
}
public ByteArray/*!*/ expandtabs(int tabsize) {
lock (this) {
return new ByteArray(_bytes.ExpandTabs(tabsize));
}
}
public int find([BytesConversion]IList/*!*/ sub) => find(sub, null, null);
public int find([BytesConversion]IList/*!*/ sub, int start) => find(sub, start, null);
public int find([BytesConversion]IList/*!*/ sub, int? start, int? end) {
lock (this) {
return _bytes.Find(sub, start, end);
}
}
public int find(int @byte) => find(@byte, null, null);
public int find(int @byte, int start) => find(@byte, start, null);
public int find(int @byte, int? start, int? end) {
lock (this) {
return _bytes.IndexOfByte(@byte.ToByteChecked(), start ?? 0, end ?? _bytes.Count);
}
}
public static ByteArray/*!*/ fromhex(string/*!*/ @string) {
return new ByteArray(IListOfByteOps.FromHex(@string));
}
public int index([BytesConversion]IList/*!*/ sub) => index(sub, null, null);
public int index([BytesConversion]IList/*!*/ sub, int? start) => index(sub, start, null);
public int index([BytesConversion]IList/*!*/ sub, int? start, int? end) {
lock (this) {
int res = find(sub, start, end);
if (res == -1) {
throw PythonOps.ValueError("subsection not found");
}
return res;
}
}
public int index(int @byte) => index(@byte, null, null);
public int index(int @byte, int? start) => index(@byte, start, null);
public int index(int @byte, int? start, int? end) {
lock (this) {
int res = find(@byte.ToByteChecked(), start, end);
if (res == -1) {
throw PythonOps.ValueError("subsection not found");
}
return res;
}
}
public bool isalnum() {
lock (this) {
return _bytes.IsAlphaNumeric();
}
}
public bool isalpha() {
lock (this) {
return _bytes.IsLetter();
}
}
public bool isdigit() {
lock (this) {
return _bytes.IsDigit();
}
}
public bool islower() {
lock (this) {
return _bytes.IsLower();
}
}
public bool isspace() {
lock (this) {
return _bytes.IsWhiteSpace();
}
}
///
/// return true if self is a titlecased string and there is at least one
/// character in self; also, uppercase characters may only follow uncased
/// characters (e.g. whitespace) and lowercase characters only cased ones.
/// return false otherwise.
///
public bool istitle() {
lock (this) {
return _bytes.IsTitle();
}
}
public bool isupper() {
lock (this) {
return _bytes.IsUpper();
}
}
///
/// Return a string which is the concatenation of the strings
/// in the sequence seq. The separator between elements is the
/// string providing this method
///
public ByteArray/*!*/ join(object/*!*/ sequence) {
IEnumerator seq = PythonOps.GetEnumerator(sequence);
if (!seq.MoveNext()) {
return new ByteArray();
}
// check if we have just a sequnce of just one value - if so just
// return that value.
object curVal = seq.Current;
if (!seq.MoveNext()) {
return JoinOne(curVal);
}
List ret = new List();
ByteOps.AppendJoin(curVal, 0, ret);
int index = 1;
do {
ret.AddRange(this);
ByteOps.AppendJoin(seq.Current, index, ret);
index++;
} while (seq.MoveNext());
return new ByteArray(ret);
}
public ByteArray/*!*/ join([NotNull]PythonList/*!*/ sequence) {
if (sequence.__len__() == 0) {
return new ByteArray();
}
lock (this) {
if (sequence.__len__() == 1) {
return JoinOne(sequence[0]);
}
List ret = new List();
ByteOps.AppendJoin(sequence._data[0], 0, ret);
for (int i = 1; i < sequence._size; i++) {
ret.AddRange(this);
ByteOps.AppendJoin(sequence._data[i], i, ret);
}
return new ByteArray(ret);
}
}
public ByteArray/*!*/ ljust(int width) {
return ljust(width, (byte)' ');
}
public ByteArray/*!*/ ljust(int width, IList/*!*/ fillchar) {
return ljust(width, fillchar.ToByte("ljust", 2));
}
private ByteArray/*!*/ ljust(int width, byte fillchar) {
lock (this) {
int spaces = width - _bytes.Count;
List ret = new List(width);
ret.AddRange(_bytes);
for (int i = 0; i < spaces; i++) {
ret.Add(fillchar);
}
return new ByteArray(ret);
}
}
public ByteArray/*!*/ lower() {
lock (this) {
return new ByteArray(_bytes.ToLower());
}
}
public ByteArray/*!*/ lstrip() {
lock (this) {
List res = _bytes.LeftStrip();
if (res == null) {
return CopyThis();
}
return new ByteArray(res);
}
}
public ByteArray/*!*/ lstrip([BytesConversion]IList bytes) {
lock (this) {
List res = _bytes.LeftStrip(bytes);
if (res == null) {
return CopyThis();
}
return new ByteArray(res);
}
}
public static Bytes maketrans([BytesConversion]IList from, [BytesConversion]IList to)
=> Bytes.maketrans(from, to);
public PythonTuple/*!*/ partition(IList/*!*/ sep) {
if (sep == null) {
throw PythonOps.TypeError("expected string, got NoneType");
} else if (sep.Count == 0) {
throw PythonOps.ValueError("empty separator");
}
object[] obj = new object[3] { new ByteArray(), new ByteArray(), new ByteArray() };
if (_bytes.Count != 0) {
int index = find(sep);
if (index == -1) {
obj[0] = CopyThis();
} else {
obj[0] = new ByteArray(_bytes.Substring(0, index));
obj[1] = new ByteArray(new List(sep));
obj[2] = new ByteArray(_bytes.Substring(index + sep.Count, _bytes.Count - index - sep.Count));
}
}
return new PythonTuple(obj);
}
public PythonTuple/*!*/ partition([NotNull]PythonList/*!*/ sep) {
return partition(GetBytes(sep));
}
public ByteArray/*!*/ replace([BytesConversion]IList/*!*/ old, [BytesConversion]IList/*!*/ @new, int count = -1) {
if (old == null) {
throw PythonOps.TypeError("expected bytes or bytearray, got NoneType");
} else if (count == 0) {
return CopyThis();
}
return new ByteArray(_bytes.Replace(old, @new, count));
}
public int rfind([BytesConversion]IList/*!*/ sub) => rfind(sub, null, null);
public int rfind([BytesConversion]IList/*!*/ sub, int? start) => rfind(sub, start, null);
public int rfind([BytesConversion]IList/*!*/ sub, int? start, int? end) {
lock (this) {
return _bytes.ReverseFind(sub, start, end);
}
}
public int rfind(int @byte) => rfind(@byte, null, null);
public int rfind(int @byte, int? start) => rfind(@byte, start, null);
public int rfind(int @byte, int? start, int? end) => rfind(new[] { @byte.ToByteChecked() }, start, end);
public int rindex([BytesConversion]IList/*!*/ sub) => rindex(sub, null, null);
public int rindex([BytesConversion]IList/*!*/ sub, int? start) => rindex(sub, start, null);
public int rindex([BytesConversion]IList/*!*/ sub, int? start, int? end) {
int ret = rfind(sub, start, end);
if (ret == -1) {
throw PythonOps.ValueError("subsection not found");
}
return ret;
}
public int rindex(int @byte) => rindex(@byte, null, null);
public int rindex(int @byte, int? start) => rindex(@byte, start, null);
public int rindex(int @byte, int? start, int? end) => rindex(new[] { @byte.ToByteChecked() }, start, end);
public ByteArray/*!*/ rjust(int width) {
return rjust(width, (byte)' ');
}
public ByteArray/*!*/ rjust(int width, [BytesConversion]IList/*!*/ fillchar) {
return rjust(width, fillchar.ToByte("rjust", 2));
}
private ByteArray/*!*/ rjust(int width, int fillchar) {
byte fill = fillchar.ToByteChecked();
lock (this) {
int spaces = width - _bytes.Count;
if (spaces <= 0) {
return CopyThis();
}
List ret = new List(width);
for (int i = 0; i < spaces; i++) {
ret.Add(fill);
}
ret.AddRange(_bytes);
return new ByteArray(ret);
}
}
public PythonTuple/*!*/ rpartition(IList/*!*/ sep) {
if (sep == null) {
throw PythonOps.TypeError("expected string, got NoneType");
} else if (sep.Count == 0) {
throw PythonOps.ValueError("empty separator");
}
lock (this) {
object[] obj = new object[3] { new ByteArray(), new ByteArray(), new ByteArray() };
if (_bytes.Count != 0) {
int index = rfind(sep);
if (index == -1) {
obj[2] = CopyThis();
} else {
obj[0] = new ByteArray(_bytes.Substring(0, index));
obj[1] = new ByteArray(new List(sep));
obj[2] = new ByteArray(_bytes.Substring(index + sep.Count, Count - index - sep.Count));
}
}
return new PythonTuple(obj);
}
}
public PythonTuple/*!*/ rpartition([NotNull]PythonList/*!*/ sep) {
return rpartition(GetBytes(sep));
}
public PythonList/*!*/ rsplit() {
lock (this) {
return _bytes.SplitInternal((byte[])null, -1, x => new ByteArray(x));
}
}
public PythonList/*!*/ rsplit([BytesConversion]IList/*!*/ sep) {
return rsplit(sep, -1);
}
public PythonList/*!*/ rsplit([BytesConversion]IList/*!*/ sep, int maxsplit) {
return _bytes.RightSplit(sep, maxsplit, x => new ByteArray(new List(x)));
}
public ByteArray/*!*/ rstrip() {
lock (this) {
List res = _bytes.RightStrip();
if (res == null) {
return CopyThis();
}
return new ByteArray(res);
}
}
public ByteArray/*!*/ rstrip([BytesConversion]IList bytes) {
lock (this) {
List res = _bytes.RightStrip(bytes);
if (res == null) {
return CopyThis();
}
return new ByteArray(res);
}
}
public PythonList/*!*/ split() {
lock (this) {
return _bytes.SplitInternal((byte[])null, -1, x => new ByteArray(x));
}
}
public PythonList/*!*/ split([BytesConversion]IList sep) {
return split(sep, -1);
}
public PythonList/*!*/ split([BytesConversion]IList sep, int maxsplit) {
lock (this) {
return _bytes.Split(sep, maxsplit, x => new ByteArray(x));
}
}
public PythonList/*!*/ splitlines() {
return splitlines(false);
}
public PythonList/*!*/ splitlines(bool keepends) {
lock (this) {
return _bytes.SplitLines(keepends, x => new ByteArray(x));
}
}
public bool startswith([BytesConversion]IList/*!*/ prefix) {
lock (this) {
return _bytes.StartsWith(prefix);
}
}
public bool startswith([BytesConversion]IList/*!*/ prefix, int start) {
lock (this) {
int len = Count;
if (start > len) {
return false;
} else if (start < 0) {
start += len;
if (start < 0) start = 0;
}
return _bytes.Substring(start).StartsWith(prefix);
}
}
public bool startswith([BytesConversion]IList/*!*/ prefix, int start, int end) {
lock (this) {
return _bytes.StartsWith(prefix, start, end);
}
}
public bool startswith(PythonTuple/*!*/ prefix) {
lock (this) {
return _bytes.StartsWith(prefix);
}
}
public bool startswith(PythonTuple/*!*/ prefix, int start) {
lock (this) {
return _bytes.StartsWith(prefix, start);
}
}
public bool startswith(PythonTuple/*!*/ prefix, int start, int end) {
lock (this) {
return _bytes.StartsWith(prefix, start, end);
}
}
public ByteArray/*!*/ strip() {
lock (this) {
List res = _bytes.Strip();
if (res == null) {
return CopyThis();
}
return new ByteArray(res);
}
}
public ByteArray/*!*/ strip([BytesConversion]IList chars) {
lock (this) {
List res = _bytes.Strip(chars);
if (res == null) {
return CopyThis();
}
return new ByteArray(res);
}
}
public ByteArray/*!*/ swapcase() {
lock (this) {
return new ByteArray(_bytes.SwapCase());
}
}
public ByteArray/*!*/ title() {
lock (this) {
List res = _bytes.Title();
if (res == null) {
return CopyThis();
}
return new ByteArray(res);
}
}
public ByteArray/*!*/ translate([BytesConversion]IList/*!*/ table) {
lock (this) {
if (table != null) {
if (table.Count != 256) {
throw PythonOps.ValueError("translation table must be 256 characters long");
} else if (Count == 0) {
return CopyThis();
}
}
return new ByteArray(_bytes.Translate(table, null));
}
}
public ByteArray/*!*/ translate([BytesConversion]IList/*!*/ table, [BytesConversion]IList/*!*/ delete) {
if (table == null && delete == null) {
throw PythonOps.TypeError("expected bytearray or bytes, got NoneType");
} else if (delete == null) {
throw PythonOps.TypeError("expected bytes or bytearray, got None");
}
lock (this) {
return new ByteArray(_bytes.Translate(table, delete));
}
}
public ByteArray/*!*/ upper() {
lock (this) {
return new ByteArray(_bytes.ToUpper());
}
}
public ByteArray/*!*/ zfill(int width) {
lock (this) {
int spaces = width - Count;
if (spaces <= 0) {
return CopyThis();
}
return new ByteArray(_bytes.ZeroFill(width, spaces));
}
}
public int __alloc__() {
if (_bytes.Count == 0) {
return 0;
}
return _bytes.Count + 1;
}
public bool __contains__([BytesConversion]IList bytes) {
return this.IndexOf(bytes, 0) != -1;
}
public bool __contains__(int value) {
return IndexOf(value.ToByteChecked()) != -1;
}
public bool __contains__(CodeContext/*!*/ context, object value) {
if (value is Extensible) {
return IndexOf(((Extensible)value).Value.ToByteChecked()) != -1;
} else if (value is BigInteger) {
return IndexOf(((BigInteger)value).ToByteChecked()) != -1;
} else if (value is Extensible) {
return IndexOf(((Extensible)value).Value.ToByteChecked()) != -1;
}
throw PythonOps.TypeError("Type {0} doesn't support the buffer API", PythonTypeOps.GetName(value));
}
public PythonTuple __reduce__(CodeContext/*!*/ context) {
return PythonTuple.MakeTuple(
DynamicHelpers.GetPythonType(this),
PythonTuple.MakeTuple(
PythonOps.MakeString(this),
"latin-1"
),
GetType() == typeof(ByteArray) ? null : ObjectOps.ReduceProtocol0(context, this)[2]
);
}
private string Repr() {
lock (this) {
return "bytearray(" + _bytes.BytesRepr() + ")";
}
}
public virtual string/*!*/ __repr__(CodeContext/*!*/ context) => Repr();
public override string ToString() => Repr();
public static ByteArray operator +(ByteArray self, ByteArray other) {
if (self == null) {
throw PythonOps.TypeError("expected ByteArray, got None");
}
List bytes;
lock (self) {
bytes = new List(self._bytes);
}
lock (other) {
bytes.AddRange(other._bytes);
}
return new ByteArray(bytes);
}
public static ByteArray operator +(ByteArray self, Bytes other) {
List bytes;
lock (self) {
bytes = new List(self._bytes);
}
bytes.AddRange(other);
return new ByteArray(bytes);
}
public static ByteArray operator +(ByteArray self, MemoryView other) {
List bytes;
lock (self) {
bytes = new List(self._bytes);
}
bytes.AddRange(other.tobytes());
return new ByteArray(bytes);
}
private static ByteArray MultiplyWorker(ByteArray self, int count) {
lock (self) {
if (count == 1) {
return self.CopyThis();
}
return new ByteArray(self._bytes.Multiply(count));
}
}
public static ByteArray operator *([NotNull]ByteArray self, int count) => MultiplyWorker(self, count);
public static object operator *([NotNull]ByteArray self, [NotNull]Index count)
=> PythonOps.MultiplySequence(MultiplyWorker, self, count, true);
public static ByteArray operator *([NotNull]ByteArray self, object count) {
if (Converter.TryConvertToIndex(count, out int index)) {
return self * index;
}
throw PythonOps.TypeErrorForUnIndexableObject(count);
}
public static ByteArray operator *(int count, [NotNull]ByteArray self) => MultiplyWorker(self, count);
public static object operator *([NotNull]Index count, [NotNull]ByteArray self)
=> PythonOps.MultiplySequence(MultiplyWorker, self, count, false);
public static ByteArray operator *(object count, [NotNull]ByteArray self) {
if (Converter.TryConvertToIndex(count, out int index)) {
return index * self;
}
throw PythonOps.TypeErrorForUnIndexableObject(count);
}
public static bool operator >(ByteArray/*!*/ x, ByteArray y) {
if (y == null) {
return true;
}
using (new OrderedLocker(x, y)) {
return x._bytes.Compare(y._bytes) > 0;
}
}
public static bool operator <(ByteArray/*!*/ x, ByteArray y) {
if (y == null) {
return false;
}
using (new OrderedLocker(x, y)) {
return x._bytes.Compare(y._bytes) < 0;
}
}
public static bool operator >=(ByteArray/*!*/ x, ByteArray y) {
if (y == null) {
return true;
}
using (new OrderedLocker(x, y)) {
return x._bytes.Compare(y._bytes) >= 0;
}
}
public static bool operator <=(ByteArray/*!*/ x, ByteArray y) {
if (y == null) {
return false;
}
using (new OrderedLocker(x, y)) {
return x._bytes.Compare(y._bytes) <= 0;
}
}
public static bool operator >(ByteArray/*!*/ x, Bytes y) {
if (y == null) {
return true;
}
lock (x) {
return x._bytes.Compare(y) > 0;
}
}
public static bool operator <(ByteArray/*!*/ x, Bytes y) {
if (y == null) {
return false;
}
lock (x) {
return x._bytes.Compare(y) < 0;
}
}
public static bool operator >=(ByteArray/*!*/ x, Bytes y) {
if (y == null) {
return true;
}
lock (x) {
return x._bytes.Compare(y) >= 0;
}
}
public static bool operator <=(ByteArray/*!*/ x, Bytes y) {
if (y == null) {
return false;
}
lock (x) {
return x._bytes.Compare(y) <= 0;
}
}
public object this[int index] {
get {
lock (this) {
return ScriptingRuntimeHelpers.Int32ToObject((int)_bytes[PythonOps.FixIndex(index, _bytes.Count)]);
}
}
set {
lock (this) {
_bytes[PythonOps.FixIndex(index, _bytes.Count)] = GetByte(value);
}
}
}
public object this[BigInteger index] {
get {
int iVal;
if (index.AsInt32(out iVal)) {
return this[iVal];
}
throw PythonOps.IndexError("cannot fit long in index");
}
set {
int iVal;
if (index.AsInt32(out iVal)) {
this[iVal] = value;
return;
}
throw PythonOps.IndexError("cannot fit long in index");
}
}
public object this[Slice/*!*/ slice] {
get {
lock (this) {
List res = _bytes.Slice(slice);
if (res == null) {
return new ByteArray();
}
return new ByteArray(res);
}
}
set {
if (slice == null) {
throw PythonOps.TypeError("bytearray indices must be integer or slice, not None");
}
// get a list of the bytes we're going to assign into the slice. We accept:
// integers, longs, etc... - fill in an array of 0 bytes
// list of bytes, indexables, etc...
if (!(value is IList list)) {
IEnumerator ie = PythonOps.GetEnumerator(value);
list = new List();
while (ie.MoveNext()) {
list.Add(GetByte(ie.Current));
}
}
lock (this) {
if (slice.step != null) {
// try to assign back to self: make a copy first
if (this == list) {
value = CopyThis();
} else if (list.Count == 0) {
DeleteItem(slice);
return;
}
IList castedVal = GetBytes(value);
int start, stop, step;
slice.indices(_bytes.Count, out start, out stop, out step);
int n = (step > 0 ? (stop - start + step - 1) : (stop - start + step + 1)) / step;
// we don't use slice.Assign* helpers here because bytearray has different assignment semantics.
if (list.Count < n) {
throw PythonOps.ValueError("too few items in the enumerator. need {0} have {1}", n, castedVal.Count);
}
for (int i = 0, index = start; i < castedVal.Count; i++, index += step) {
if (i >= n) {
if (index == _bytes.Count) {
_bytes.Add(castedVal[i]);
} else {
_bytes.Insert(index, castedVal[i]);
}
} else {
_bytes[index] = castedVal[i];
}
}
} else {
int start, stop, step;
slice.indices(_bytes.Count, out start, out stop, out step);
SliceNoStep(start, stop, list);
}
}
}
}
public object this[object index] {
get {
return this[Converter.ConvertToIndex(index)];
}
set {
this[Converter.ConvertToIndex(index)] = value;
}
}
[SpecialName]
public void DeleteItem(int index) {
_bytes.RemoveAt(PythonOps.FixIndex(index, _bytes.Count));
}
[SpecialName]
public void DeleteItem(Slice/*!*/ slice) {
if (slice == null) {
throw PythonOps.TypeError("list indices must be integers or slices");
}
lock (this) {
int start, stop, step;
// slice is sealed, indices can't be user code...
slice.indices(_bytes.Count, 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 < _bytes.Count; j++, i++) {
_bytes[i] = _bytes[j];
}
_bytes.RemoveRange(i, stop - start);
return;
} else if (step == -1) {
int i = stop + 1;
for (int j = start + 1; j < _bytes.Count; j++, i++) {
_bytes[i] = _bytes[j];
}
_bytes.RemoveRange(i, start - stop);
return;
} else 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) {
_bytes[curr++] = _bytes[move];
} else
skip += step;
move++;
}
while (stop < _bytes.Count) {
_bytes[curr++] = _bytes[stop++];
}
_bytes.RemoveRange(curr, _bytes.Count - curr);
}
}
#endregion
#region Implementation Details
private static ByteArray/*!*/ JoinOne(object/*!*/ curVal) {
if (!(curVal is IList)) {
throw PythonOps.TypeError("can only join an iterable of bytes");
}
return new ByteArray(new List(curVal as IList));
}
private ByteArray/*!*/ CopyThis() {
return new ByteArray(new List(_bytes));
}
private void SliceNoStep(int start, int stop, IList/*!*/ value) {
// always copy from a List object, even if it's a copy of some user defined enumerator. This
// makes it easy to hold the lock for the duration fo the copy.
IList other = GetBytes(value);
lock (this) {
if (start > stop) {
int newSize = Count + other.Count;
List newData = new List(newSize);
int reading = 0;
for (reading = 0; reading < start; reading++) {
newData.Add(_bytes[reading]);
}
for (int i = 0; i < other.Count; i++) {
newData.Add(other[i]);
}
for (; reading < Count; reading++) {
newData.Add(_bytes[reading]);
}
_bytes = newData;
} else if ((stop - start) == other.Count) {
// we are simply replacing values, this is fast...
for (int i = 0; i < other.Count; i++) {
_bytes[i + start] = other[i];
}
} else {
// we are resizing the array (either bigger or smaller), we
// will copy the data array and replace it all at once.
int newSize = Count - (stop - start) + other.Count;
List newData = new List(newSize);
for (int i = 0; i < start; i++) {
newData.Add(_bytes[i]);
}
for (int i = 0; i < other.Count; i++) {
newData.Add(other[i]);
}
for (int i = stop; i < Count; i++) {
newData.Add(_bytes[i]);
}
_bytes = newData;
}
}
}
private static byte GetByte(object/*!*/ value) {
if (Converter.TryConvertToIndex(value, out object index)) {
switch (index) {
case int i: return i.ToByteChecked();
case BigInteger bi: return bi.ToByteChecked();
default: throw new InvalidOperationException(); // unreachable
}
}
throw PythonOps.TypeError("an integer is required");
}
internal static IList/*!*/ GetBytes(object/*!*/ value) {
if (!(value is ListGenericWrapper genWrapper) && value is IList) {
return (IList)value;
}
if (value is IBufferProtocol buffer) {
return buffer.ToBytes(0, null);
}
List ret = new List();
IEnumerator ie = PythonOps.GetEnumerator(value);
while (ie.MoveNext()) {
ret.Add(GetByte(ie.Current));
}
return ret;
}
#endregion
#region IList Members
[PythonHidden]
public int IndexOf(byte item) {
lock (this) {
return _bytes.IndexOf(item);
}
}
[PythonHidden]
public void Insert(int index, byte item) {
_bytes.Insert(index, item);
}
[PythonHidden]
public void RemoveAt(int index) {
_bytes.RemoveAt(index);
}
byte IList.this[int index] {
get {
return _bytes[index];
}
set {
_bytes[index] = value;
}
}
#endregion
#region ICollection Members
[PythonHidden]
public void Add(byte item) {
lock (this) {
_bytes.Add(item);
}
}
[PythonHidden]
public void Clear() {
lock (this) {
_bytes.Clear();
}
}
[PythonHidden]
public bool Contains(byte item) {
lock (this) {
return _bytes.Contains(item);
}
}
[PythonHidden]
public void CopyTo(byte[]/*!*/ array, int arrayIndex) {
lock (this) {
_bytes.CopyTo(array, arrayIndex);
}
}
public int Count {
[PythonHidden]
get {
lock (this) {
return _bytes.Count;
}
}
}
public bool IsReadOnly {
[PythonHidden]
get { return false; }
}
[PythonHidden]
public bool Remove(byte item) {
lock (this) {
return _bytes.Remove(item);
}
}
#endregion
public IEnumerator __iter__() {
return PythonOps.BytesIntEnumerator(this).Key;
}
#region IEnumerable Members
[PythonHidden]
public IEnumerator/*!*/ GetEnumerator() {
return _bytes.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator/*!*/ System.Collections.IEnumerable.GetEnumerator() {
foreach (var _byte in _bytes) {
yield return (int)_byte;
}
}
#endregion
#region Equality Members
public const object __hash__ = null;
public bool __eq__(CodeContext/*!*/ context, [NotNull]ByteArray value) => Equals(value);
public bool __eq__(CodeContext/*!*/ context, [NotNull]MemoryView value) => Equals(value.tobytes());
public bool __eq__(CodeContext/*!*/ context, [NotNull]IBufferProtocol value) => Equals(value.ToBytes(0, null));
[return: MaybeNotImplemented]
public object __eq__(CodeContext/*!*/ context, object value) => NotImplementedType.Value;
public bool __ne__(CodeContext/*!*/ context, [NotNull]ByteArray value) => !__eq__(context, value);
public bool __ne__(CodeContext/*!*/ context, [NotNull]MemoryView value) => !__eq__(context, value);
public bool __ne__(CodeContext/*!*/ context, [NotNull]IBufferProtocol value) => !__eq__(context, value);
[return: MaybeNotImplemented]
public object __ne__(CodeContext/*!*/ context, object value) => NotImplementedType.Value;
private bool Equals(ByteArray other) {
if (Count != other.Count) {
return false;
} else if (Count == 0) {
// 2 empty ByteArrays are equal
return true;
}
using (new OrderedLocker(this, other)) {
for (int i = 0; i < Count; i++) {
if (_bytes[i] != other._bytes[i]) {
return false;
}
}
}
return true;
}
private bool Equals(Bytes other) {
if (Count != other.Count) {
return false;
} else if (Count == 0) {
// 2 empty ByteArrays are equal
return true;
}
lock (this) {
for (int i = 0; i < Count; i++) {
if (_bytes[i] != ((IList)other)[i]) {
return false;
}
}
}
return true;
}
#endregion
#region IBufferProtocol Members
object IBufferProtocol.GetItem(int index) {
lock (this) {
return (int)_bytes[PythonOps.FixIndex(index, _bytes.Count)];
}
}
void IBufferProtocol.SetItem(int index, object value) {
this[index] = value;
}
void IBufferProtocol.SetSlice(Slice index, object value) {
this[index] = value;
}
int IBufferProtocol.ItemCount {
get {
return _bytes.Count;
}
}
string IBufferProtocol.Format {
get { return "B"; }
}
BigInteger IBufferProtocol.ItemSize {
get { return 1; }
}
BigInteger IBufferProtocol.NumberDimensions {
get { return 1; }
}
bool IBufferProtocol.ReadOnly {
get { return false; }
}
IList IBufferProtocol.GetShape(int start, int? end) {
if (end != null) {
return new[] { (BigInteger)end - start };
}
return new[] { (BigInteger)_bytes.Count - start };
}
PythonTuple IBufferProtocol.Strides => PythonTuple.MakeTuple(1);
PythonTuple IBufferProtocol.SubOffsets => null;
Bytes IBufferProtocol.ToBytes(int start, int? end) {
if (start == 0 && end == null) {
return new Bytes(this);
}
return new Bytes((ByteArray)this[new Slice(start, end)]);
}
PythonList IBufferProtocol.ToList(int start, int? end) {
List res = _bytes.Slice(new Slice(start, end));
if (res == null) {
return new PythonList();
}
return new PythonList(res.ToArray());
}
#endregion
}
}