diff --git a/src/core/Akka/IO/SocketEventArgsPool.cs b/src/core/Akka/IO/SocketEventArgsPool.cs index bd8007f0875..e6ff565e6e7 100644 --- a/src/core/Akka/IO/SocketEventArgsPool.cs +++ b/src/core/Akka/IO/SocketEventArgsPool.cs @@ -22,7 +22,7 @@ public interface ISocketEventArgsPool { SocketAsyncEventArgs Acquire(IActorRef actor); void Release(SocketAsyncEventArgs e); - + BufferPoolInfo BufferPoolInfo { get; } } @@ -40,16 +40,17 @@ internal class PreallocatedSocketEventAgrsPool : ISocketEventArgsPool // There is no reason why users or developers would need to touch memory management code because it is // very specific for providing byte buffers for SocketAsyncEventArgs private readonly IBufferPool _bufferPool; - + private readonly EventHandler _onComplete; - public PreallocatedSocketEventAgrsPool(int initSize, IBufferPool bufferPool, EventHandler onComplete) + public PreallocatedSocketEventAgrsPool(int initSize, IBufferPool bufferPool, + EventHandler onComplete) { _bufferPool = bufferPool; _onComplete = onComplete; } - + public SocketAsyncEventArgs Acquire(IActorRef actor) { var buffer = _bufferPool.Rent(); @@ -67,6 +68,7 @@ public void Release(SocketAsyncEventArgs e) { _bufferPool.Release(new ArraySegment(e.Buffer, e.Offset, e.Count)); } + if (e.BufferList != null) { foreach (var segment in e.BufferList) @@ -74,6 +76,7 @@ public void Release(SocketAsyncEventArgs e) _bufferPool.Release(segment); } } + e.Dispose(); } @@ -82,58 +85,20 @@ public void Release(SocketAsyncEventArgs e) internal static class SocketAsyncEventArgsExtensions { - public static void SetBuffer(this SocketAsyncEventArgs args, ByteString data) - { - if (data.IsCompact) - { - var buffer = data.Buffers[0]; - if (args.BufferList != null) - { - // BufferList property setter is not simple member association operation, - // but the getter is. Therefore we first check if we need to clear buffer list - // and only do so if necessary. - args.BufferList = null; - } - args.SetBuffer(buffer.Array, buffer.Offset, buffer.Count); - } - else - { - if (RuntimeDetector.IsMono) - { - // Mono doesn't support BufferList - falback to compacting ByteString - var compacted = data.Compact(); - var buffer = compacted.Buffers[0]; - args.SetBuffer(buffer.Array, buffer.Offset, buffer.Count); - } - else - { - args.SetBuffer(null, 0, 0); - args.BufferList = data.Buffers; - } - } - } - public static void SetBuffer(this SocketAsyncEventArgs args, IEnumerable dataCollection) { - if (RuntimeDetector.IsMono) + // Mono doesn't support BufferList - fallback to compacting ByteString + var dataList = dataCollection.ToList(); + var totalSize = dataList.Sum(d => d.Count); + var bytes = new byte[totalSize]; + var position = 0; + foreach (var byteString in dataList) { - // Mono doesn't support BufferList - falback to compacting ByteString - var dataList = dataCollection.ToList(); - var totalSize = dataList.SelectMany(d => d.Buffers).Sum(d => d.Count); - var bytes = new byte[totalSize]; - var position = 0; - foreach (var byteString in dataList) - { - var copied = byteString.CopyTo(bytes, position, byteString.Count); - position += copied; - } - args.SetBuffer(bytes, 0, bytes.Length); - } - else - { - args.SetBuffer(null, 0, 0); - args.BufferList = dataCollection.SelectMany(d => d.Buffers).ToList(); + var copied = byteString.CopyTo(bytes, position, byteString.Count); + position += copied; } + + args.SetBuffer(bytes, 0, bytes.Length); } } -} +} \ No newline at end of file diff --git a/src/core/Akka/IO/UdpConnection.cs b/src/core/Akka/IO/UdpConnection.cs index 1e1b0aacc81..044edb35e0a 100644 --- a/src/core/Akka/IO/UdpConnection.cs +++ b/src/core/Akka/IO/UdpConnection.cs @@ -192,8 +192,12 @@ private void DoWrite() { var (send, sender) = _pendingSend.Value; var data = send.Payload; - - var bytesWritten = _socket.Send(data.Buffers); + + #if NETSTANDARD2_0 + var bytesWritten = _socket.Send(data.Buffers.ToArray()); + #else + var bytesWritten = _socket.Send(data.Buffers.Span); + #endif if (Udp.Settings.TraceLogging) Log.Debug("Wrote [{0}] bytes to socket", bytesWritten); diff --git a/src/core/Akka/Util/ByteString.cs b/src/core/Akka/Util/ByteString.cs index 7bdb0378ca7..d4ed346a568 100644 --- a/src/core/Akka/Util/ByteString.cs +++ b/src/core/Akka/Util/ByteString.cs @@ -13,6 +13,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Akka.Util.Internal; namespace Akka.IO { @@ -26,20 +27,10 @@ namespace Akka.IO /// and also providing a thread safe way of working with bytes. /// [DebuggerDisplay("(Count = {_count}, Buffers = {_buffers})")] - public sealed class ByteString : IEquatable, IEnumerable + public sealed class ByteString : IEquatable { #region creation methods - /// - /// INTERNAL API: remove this method once public constructor will be removed. - /// - internal static ByteString FromBuffers(IEnumerator buffers) - { - var cached = new List(); - while (buffers.MoveNext()) cached.Add(buffers.Current); - return FromBytes(cached); - } - /// /// Creates a new by copying a provided byte array. /// @@ -67,8 +58,13 @@ public static ByteString CopyFrom(byte[] array, int offset, int count) if (count == 0) return Empty; - if (offset < 0 || offset >= array.Length) throw new ArgumentOutOfRangeException(nameof(offset), $"Provided offset of [{offset}] is outside bounds of an array [{array.Length}]"); - if (count > array.Length - offset) throw new ArgumentException($"Provided length [{count}] of array to copy doesn't fit array length [{array.Length}] within given offset [{offset}]", nameof(count)); + if (offset < 0 || offset >= array.Length) + throw new ArgumentOutOfRangeException(nameof(offset), + $"Provided offset of [{offset}] is outside bounds of an array [{array.Length}]"); + if (count > array.Length - offset) + throw new ArgumentException( + $"Provided length [{count}] of array to copy doesn't fit array length [{array.Length}] within given offset [{offset}]", + nameof(count)); var copy = new byte[count]; Array.Copy(array, offset, copy, 0, count); @@ -83,7 +79,7 @@ public static ByteString CopyFrom(byte[] array, int offset, int count) /// The new public static ByteString CopyFrom(Memory memory) => CopyFrom(memory, 0, memory.Length); - + /// /// Creates a new by copying a . /// @@ -95,8 +91,13 @@ public static ByteString CopyFrom(Memory memory, int offset, int count) { if (count == 0) return Empty; - if (offset < 0 || offset >= memory.Length) throw new ArgumentOutOfRangeException(nameof(offset), $"Provided offset of [{offset}] is outside bounds of an array [{memory.Length}]"); - if (count > memory.Length - offset) throw new ArgumentException($"Provided length [{count}] of array to copy doesn't fit array length [{memory.Length}] within given offset [{offset}]", nameof(count)); + if (offset < 0 || offset >= memory.Length) + throw new ArgumentOutOfRangeException(nameof(offset), + $"Provided offset of [{offset}] is outside bounds of an array [{memory.Length}]"); + if (count > memory.Length - offset) + throw new ArgumentException( + $"Provided length [{count}] of array to copy doesn't fit array length [{memory.Length}] within given offset [{offset}]", + nameof(count)); var copy = new byte[count]; memory.Slice(offset, count).CopyTo(copy); @@ -111,7 +112,7 @@ public static ByteString CopyFrom(Memory memory, int offset, int count) /// The new public static ByteString CopyFrom(Span span) => CopyFrom(span, 0, span.Length); - + /// /// Creates a new by copying a . /// @@ -123,8 +124,13 @@ public static ByteString CopyFrom(Span span, int offset, int count) { if (count == 0) return Empty; - if (offset < 0 || offset >= span.Length) throw new ArgumentOutOfRangeException(nameof(offset), $"Provided offset of [{offset}] is outside bounds of an array [{span.Length}]"); - if (count > span.Length - offset) throw new ArgumentException($"Provided length [{count}] of array to copy doesn't fit array length [{span.Length}] within given offset [{offset}]", nameof(count)); + if (offset < 0 || offset >= span.Length) + throw new ArgumentOutOfRangeException(nameof(offset), + $"Provided offset of [{offset}] is outside bounds of an array [{span.Length}]"); + if (count > span.Length - offset) + throw new ArgumentException( + $"Provided length [{count}] of array to copy doesn't fit array length [{span.Length}] within given offset [{offset}]", + nameof(count)); var copy = new byte[count]; span.Slice(offset, count).CopyTo(copy); @@ -188,8 +194,13 @@ public static ByteString FromBytes(ArraySegment buffer) => public static ByteString FromBytes(byte[] array, int offset, int count) { if (array == null) throw new ArgumentNullException(nameof(array)); - if (offset < 0 || (offset != 0 && offset >= array.Length)) throw new ArgumentOutOfRangeException(nameof(offset), $"Provided offset [{offset}] is outside bounds of an array"); - if (count > array.Length - offset) throw new ArgumentException($"Provided length of array to copy [{count}] doesn't fit array length [{array.Length}] and offset [{offset}].", nameof(count)); + if (offset < 0 || (offset != 0 && offset >= array.Length)) + throw new ArgumentOutOfRangeException(nameof(offset), + $"Provided offset [{offset}] is outside bounds of an array"); + if (count > array.Length - offset) + throw new ArgumentException( + $"Provided length of array to copy [{count}] doesn't fit array length [{array.Length}] and offset [{offset}].", + nameof(count)); if (count == 0) return Empty; @@ -206,12 +217,20 @@ public static ByteString FromBytes(IEnumerable buffers) { if (buffers == null) throw new ArgumentNullException(nameof(buffers)); - var array = (buffers as ByteBuffer[]) ?? buffers.ToArray(); - var count = 0; - foreach (var buffer in array) - count += buffer.Count; - - return new ByteString(array, count); + // copy buffers into a new ReadOnlyMemory + var buffersList = buffers.ToList(); + var newSize = buffersList.Sum(c => c.Count); + var newBuffer = new byte[newSize]; + + // copy all buffers into the new buffer + var offset = 0; + foreach (var buffer in buffersList) + { + Array.Copy(buffer.Array!, buffer.Offset, newBuffer, offset, buffer.Count); + offset += buffer.Count; + } + + return FromBytes(newBuffer); } /// @@ -240,35 +259,31 @@ public static ByteString FromString(string str, Encoding encoding) /// /// An empty . /// - public static ByteString Empty { get; } = new ByteString(new ByteBuffer(new byte[0], 0, 0)); + public static ByteString Empty { get; } = new(new ByteBuffer(Array.Empty(), 0, 0)); #endregion - private readonly int _count; - private readonly ByteBuffer[] _buffers; + private readonly ReadOnlyMemory _buffers; - private ByteString(ByteBuffer[] buffers, int count) + private ByteString(ByteBuffer buffer) { - _buffers = buffers; - _count = count; + _buffers = buffer; } - private ByteString(ByteBuffer buffer) + private ByteString(ReadOnlyMemory buffer) { - _buffers = new[] { buffer }; - _count = buffer.Count; + _buffers = buffer; } private ByteString(byte[] array, int offset, int count) { - _buffers = new[] { new ByteBuffer(array, offset, count) }; - _count = count; + _buffers = array.AsMemory(offset, count); } /// /// Gets a total number of bytes stored inside this . /// - public int Count => _count; + public int Count => _buffers.Length; /// /// Determines if current has compact representation. @@ -276,32 +291,25 @@ private ByteString(byte[] array, int offset, int count) /// block of memory. /// /// TBD - public bool IsCompact => _buffers.Length == 1; + public bool IsCompact => true; /// /// Determines if current is empty. /// - public bool IsEmpty => _count == 0; + public bool IsEmpty => Count == 0; /// - /// Gets sequence of the buffers used underneat. + /// Gets sequence of the buffers used underneath. /// - internal IList Buffers => _buffers; + internal ReadOnlyMemory Buffers => _buffers; /// /// Gets a byte stored under a provided . /// - /// TBD + /// The index of the bytes we need access to public byte this[int index] { - get - { - if (index >= _count) throw new IndexOutOfRangeException("Requested index is outside of the bounds of the ByteString"); - int j; - var i = GetBufferFittingIndex(index, out j); - var buffer = _buffers[i]; - return buffer.Array[buffer.Offset + j]; - } + get { return _buffers.Span[index]; } } /// @@ -311,10 +319,7 @@ public byte this[int index] /// TBD public ByteString Compact() { - if (IsCompact) return this; - - var copy = this.ToArray(); - return new ByteString(copy, 0, copy.Length); + return this; } /// @@ -323,7 +328,7 @@ public ByteString Compact() /// operation. /// /// index inside current , from which slicing should start - public ByteString Slice(int index) => Slice(index, _count - index); + public ByteString Slice(int index) => Slice(index, Count - index); /// /// Slices current , creating a new @@ -335,74 +340,41 @@ public ByteString Compact() /// public ByteString Slice(int index, int count) { - //TODO: this is really stupid, but previous impl didn't throw if arguments - // were out of range. We either have to round them to valid bounds or - // (future version, provide negative-arg slicing like i.e. Python). - if (index < 0) index = 0; - if (index >= _count) index = Math.Max(0, _count - 1); - if (count > _count - index) count = _count - index; - if (count <= 0) return Empty; - - if (index == 0 && count == _count) return this; - - int j; - var i = GetBufferFittingIndex(index, out j); - var init = _buffers[i]; - - var copied = Math.Min(init.Count - j, count); - var newBuffers = new ByteBuffer[_buffers.Length - i]; - newBuffers[0] = new ByteBuffer(init.Array, init.Offset + j, copied); - - i++; - var k = 1; - for (; i < _buffers.Length; i++, k++) - { - if (copied >= count) break; - - var buffer = _buffers[i]; - var toCopy = Math.Min(buffer.Count, count - copied); - newBuffers[k] = new ByteBuffer(buffer.Array, buffer.Offset, toCopy); - copied += toCopy; - } - - if (k < newBuffers.Length) - newBuffers = newBuffers.Take(k).ToArray(); - - return new ByteString(newBuffers, count); - } - - /// - /// Given an in current tries to - /// find which buffer will be used to contain that index and return its range. - /// An offset within the buffer itself will be stored in . - /// - /// - /// - /// - private int GetBufferFittingIndex(int index, out int indexWithinBuffer) - { - if (index == 0) - { - indexWithinBuffer = 0; - return 0; - } - - var j = index; - for (var i = 0; i < _buffers.Length; i++) - { - var buffer = _buffers[i]; - if (j >= buffer.Count) - { - j -= buffer.Count; - } - else - { - indexWithinBuffer = j; - return i; - } - } - - throw new IndexOutOfRangeException($"Requested index [{index}] is outside of the bounds of current ByteString."); + return new ByteString(_buffers.Slice(index, count)); + // //TODO: this is really stupid, but previous impl didn't throw if arguments + // // were out of range. We either have to round them to valid bounds or + // // (future version, provide negative-arg slicing like i.e. Python). + // if (index < 0) index = 0; + // if (index >= _count) index = Math.Max(0, _count - 1); + // if (count > _count - index) count = _count - index; + // if (count <= 0) return Empty; + // + // if (index == 0 && count == _count) return this; + // + // int j; + // var i = GetBufferFittingIndex(index, out j); + // var init = _buffers[i]; + // + // var copied = Math.Min(init.Count - j, count); + // var newBuffers = new ByteBuffer[_buffers.Length - i]; + // newBuffers[0] = new ByteBuffer(init.Array, init.Offset + j, copied); + // + // i++; + // var k = 1; + // for (; i < _buffers.Length; i++, k++) + // { + // if (copied >= count) break; + // + // var buffer = _buffers[i]; + // var toCopy = Math.Min(buffer.Count, count - copied); + // newBuffers[k] = new ByteBuffer(buffer.Array, buffer.Offset, toCopy); + // copied += toCopy; + // } + // + // if (k < newBuffers.Length) + // newBuffers = newBuffers.Take(k).ToArray(); + // + // return new ByteString(newBuffers, count); } /// @@ -413,14 +385,7 @@ private int GetBufferFittingIndex(int index, out int indexWithinBuffer) /// public int IndexOf(byte b) { - var idx = 0; - foreach (var x in this) - { - if (x == b) return idx; - idx++; - } - - return -1; + return IndexOf(b, 0); } /// @@ -430,22 +395,7 @@ public int IndexOf(byte b) /// public int IndexOf(byte b, int from) { - if (from >= _count) return -1; - - int j; - var i = GetBufferFittingIndex(from, out j); - var idx = from; - for (; i < _buffers.Length; i++) - { - var buffer = _buffers[i]; - for (; j < buffer.Count; j++, idx++) - { - if (buffer.Array[buffer.Offset + j] == b) return idx; - } - j = 0; - } - - return -1; + return _buffers.Span.Slice(from).IndexOf(b); } /// @@ -458,39 +408,7 @@ public int IndexOf(byte b, int from) /// public bool HasSubstring(ByteString other, int index) { - // quick check: if subsequence is longer than remaining size, return false - if (other.Count > _count - index) return false; - - int thisIdx = 0, otherIdx = 0; - var i = GetBufferFittingIndex(index, out thisIdx); - var j = 0; - while (j < other._buffers.Length) - { - var buffer = _buffers[i]; - var otherBuffer = other._buffers[j]; - - while (thisIdx < buffer.Count && otherIdx < otherBuffer.Count) - { - if (buffer.Array[buffer.Offset + thisIdx] != otherBuffer.Array[otherBuffer.Offset + otherIdx]) - return false; - - thisIdx++; - otherIdx++; - } - - if (thisIdx >= buffer.Count) - { - i++; - thisIdx = 0; - } - if (otherIdx >= otherBuffer.Count) - { - j++; - otherIdx = 0; - } - } - - return true; + return _buffers.Span.Slice(index).IndexOfAny(other.Buffers.Span) >= 0; } /// @@ -499,12 +417,10 @@ public bool HasSubstring(ByteString other, int index) /// TBD public byte[] ToArray() { - if (_count == 0) + if (Count == 0) return Array.Empty(); - var copy = new byte[_count]; - this.CopyTo(copy, 0, _count); - return copy; + return _buffers.ToArray(); } /// @@ -518,16 +434,14 @@ public ByteString Concat(ByteString other) if (other == null) throw new ArgumentNullException(nameof(other), "Cannot append null to ByteString."); if (other.IsEmpty) return this; - if (this.IsEmpty) return other; + if (IsEmpty) return other; - var count = _count + other._count; - var len1 = _buffers.Length; - var len2 = other._buffers.Length; - var array = new ByteBuffer[len1 + len2]; - Array.Copy(this._buffers, 0, array, 0, len1); - Array.Copy(other._buffers, 0, array, len1, len2); - return new ByteString(array, count); + var count = Count + other.Count; + var dest = new byte[count]; + Buffers.CopyTo(dest); + other.Buffers.Span.CopyTo(dest.AsSpan(other.Count)); + return new ByteString(dest); } /// @@ -539,23 +453,15 @@ public ByteString Concat(ByteString other) public int CopyTo(byte[] buffer, int index, int count) { if (buffer == null) throw new ArgumentNullException(nameof(buffer)); - if (index < 0 || index >= buffer.Length) throw new ArgumentOutOfRangeException(nameof(index), "Provided index is outside the bounds of the buffer to copy to."); - if (count > buffer.Length - index) throw new ArgumentException("Provided number of bytes to copy won't fit into provided buffer", nameof(count)); - - count = Math.Min(count, _count); - var remaining = count; - var position = index; - foreach (var b in _buffers) - { - var toCopy = Math.Min(b.Count, remaining); - Array.Copy(b.Array, b.Offset, buffer, position, toCopy); - position += toCopy; - remaining -= toCopy; - - if (remaining == 0) return count; - } - - return 0; + if (index < 0 || index >= buffer.Length) + throw new ArgumentOutOfRangeException(nameof(index), + "Provided index is outside the bounds of the buffer to copy to."); + if (count > buffer.Length - index) + throw new ArgumentException("Provided number of bytes to copy won't fit into provided buffer", + nameof(count)); + + var span = buffer.AsSpan(); + return CopyTo(ref span, index, count); } /// @@ -565,7 +471,7 @@ public int CopyTo(byte[] buffer, int index, int count) /// The number of bytes copied public int CopyTo(ref Memory buffer) => CopyTo(ref buffer, 0, buffer.Length); - + /// /// Copies content of a current into a provided /// starting from in that @@ -574,26 +480,15 @@ public int CopyTo(ref Memory buffer) /// The number of bytes copied public int CopyTo(ref Memory buffer, int index, int count) { - if (index < 0 || index >= buffer.Length) throw new ArgumentOutOfRangeException(nameof(index), "Provided index is outside the bounds of the buffer to copy to."); - if (count > buffer.Length - index) throw new ArgumentException("Provided number of bytes to copy won't fit into provided buffer", nameof(count)); - - count = Math.Min(count, _count); - var remaining = count; - var position = index; - foreach (var b in _buffers) - { - var toCopy = Math.Min(b.Count, remaining); - - var bufferSpan = buffer.Span.Slice(position, toCopy); - b.AsSpan().CopyTo(bufferSpan); - - position += toCopy; - remaining -= toCopy; - - if (remaining == 0) return count; - } - - return 0; + if (index < 0 || index >= buffer.Length) + throw new ArgumentOutOfRangeException(nameof(index), + "Provided index is outside the bounds of the buffer to copy to."); + if (count > buffer.Length - index) + throw new ArgumentException("Provided number of bytes to copy won't fit into provided buffer", + nameof(count)); + + var span = buffer.Span; + return CopyTo(ref span, index, count); } /// @@ -603,7 +498,7 @@ public int CopyTo(ref Memory buffer, int index, int count) /// The number of bytes copied public int CopyTo(ref Span buffer) => CopyTo(ref buffer, 0, buffer.Length); - + /// /// Copies content of a current into a provided /// starting from in that @@ -612,26 +507,19 @@ public int CopyTo(ref Span buffer) /// The number of bytes copied public int CopyTo(ref Span buffer, int index, int count) { - if (index < 0 || index >= buffer.Length) throw new ArgumentOutOfRangeException(nameof(index), "Provided index is outside the bounds of the buffer to copy to."); - if (count > buffer.Length - index) throw new ArgumentException("Provided number of bytes to copy won't fit into provided buffer", nameof(count)); + if (index < 0 || index >= buffer.Length) + throw new ArgumentOutOfRangeException(nameof(index), + "Provided index is outside the bounds of the buffer to copy to."); + if (count > buffer.Length - index) + throw new ArgumentException("Provided number of bytes to copy won't fit into provided buffer", + nameof(count)); - count = Math.Min(count, _count); - var remaining = count; - var position = index; - foreach (var b in _buffers) - { - var toCopy = Math.Min(b.Count, remaining); + var span = Buffers.Span; + var actualCount = Math.Min(count, Count); - var bufferSpan = buffer.Slice(position, toCopy); - b.AsSpan().CopyTo(bufferSpan); - - position += toCopy; - remaining -= toCopy; + span.Slice(0, actualCount).CopyTo(buffer.Slice(index)); - if (remaining == 0) return count; - } - - return 0; + return actualCount; } /// @@ -643,10 +531,11 @@ public void WriteTo(Stream stream) { if (stream == null) throw new ArgumentNullException(nameof(stream)); - foreach (var buffer in _buffers) - { - stream.Write(buffer.Array, buffer.Offset, buffer.Count); - } +#if NETSTANDARD2_0 + stream.Write(Buffers.ToArray(), 0, Buffers.Length); +#else + stream.Write(Buffers.Span); +#endif } /// @@ -658,65 +547,40 @@ public async Task WriteToAsync(Stream stream) { if (stream == null) throw new ArgumentNullException(nameof(stream)); - foreach (var buffer in _buffers) - { - await stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count); - } +#if NETSTANDARD2_0 + await stream.WriteAsync(Buffers.ToArray(), 0, Buffers.Length); +#else + await stream.WriteAsync(Buffers); +#endif } public override bool Equals(object obj) => Equals(obj as ByteString); public override int GetHashCode() { - var hashCode = 0; - foreach (var b in this) - { - hashCode = (hashCode * 397) ^ b.GetHashCode(); - } - return hashCode; + return Buffers.GetHashCode(); } public bool Equals(ByteString other) { if (ReferenceEquals(other, this)) return true; if (ReferenceEquals(other, null)) return false; - if (_count != other._count) return false; - - using (var thisEnum = this.GetEnumerator()) - using (var otherEnum = other.GetEnumerator()) - { - while (thisEnum.MoveNext() && otherEnum.MoveNext()) - { - if (thisEnum.Current != otherEnum.Current) return false; - } - } + if (Count != other.Count) return false; - return true; + var span = Buffers.Span; + var otherSpan = other.Buffers.Span; + return span.SequenceEqual(otherSpan); } - public IEnumerator GetEnumerator() - { - foreach (var buffer in _buffers) - { - for (int i = buffer.Offset; i < buffer.Offset + buffer.Count; i++) - { - yield return buffer.Array[i]; - } - } - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public override string ToString() => ToString(Encoding.UTF8); public string ToString(Encoding encoding) { - if (IsCompact) - return encoding.GetString(_buffers[0].Array, _buffers[0].Offset, _buffers[0].Count); - - byte[] buffer = ToArray(); - - return encoding.GetString(buffer); +#if NETSTANDARD2_0 + return encoding.GetString(Buffers.ToArray()); +#else + return encoding.GetString(Buffers.Span); +#endif } public static bool operator ==(ByteString x, ByteString y) => Equals(x, y); @@ -724,15 +588,15 @@ public string ToString(Encoding encoding) public static bool operator !=(ByteString x, ByteString y) => !Equals(x, y); public static explicit operator ByteString(byte[] bytes) => ByteString.CopyFrom(bytes); - - public static explicit operator byte[] (ByteString byteString) => byteString.ToArray(); - + + public static explicit operator byte[](ByteString byteString) => byteString.ToArray(); + public static ByteString operator +(ByteString x, ByteString y) => x.Concat(y); } - + public enum ByteOrder { BigEndian, LittleEndian } -} +} \ No newline at end of file