From 8e8a9d3b2bfc53ffabc179dfbfafa67ab3609339 Mon Sep 17 00:00:00 2001 From: Swen Mun Date: Tue, 16 May 2023 03:21:42 +0900 Subject: [PATCH 1/2] Elide unnecessary value copies --- Bencodex.Tests/EncoderTest.cs | 2 +- Bencodex/Decoder.cs | 4 ++++ Bencodex/Encoder.cs | 23 +++++++++++++---------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Bencodex.Tests/EncoderTest.cs b/Bencodex.Tests/EncoderTest.cs index 962404d..f0bd308 100644 --- a/Bencodex.Tests/EncoderTest.cs +++ b/Bencodex.Tests/EncoderTest.cs @@ -164,7 +164,7 @@ public void EncodeInteger() public void EncodeBinary() { var buffer = new byte[20]; - long size = Encoder.EncodeBinary(new Binary("hello world", Encoding.ASCII), buffer, 2L); + long size = Encoder.EncodeBinary(new Binary("hello world", Encoding.ASCII).ByteArray.AsSpan(), buffer, 2L); Assert.Equal(14L, size); AssertEqual( new byte[20] diff --git a/Bencodex/Decoder.cs b/Bencodex/Decoder.cs index c5094d4..065ffbd 100644 --- a/Bencodex/Decoder.cs +++ b/Bencodex/Decoder.cs @@ -409,7 +409,11 @@ private Text ReadTextAfterPrefix() string textContent; try { +#if NETSTANDARD2_0 textContent = Encoding.UTF8.GetString(bytes); +#else + textContent = Encoding.UTF8.GetString(bytes.AsSpan()); +#endif } catch (ArgumentException e) { diff --git a/Bencodex/Encoder.cs b/Bencodex/Encoder.cs index a1685aa..88dea71 100644 --- a/Bencodex/Encoder.cs +++ b/Bencodex/Encoder.cs @@ -60,7 +60,11 @@ public static void Encode(IValue value, Stream output, IOffloadOptions? offloadO } byte[] buffer = Encode(value, offloadOptions); +#if NETSTANDARD2_0 output.Write(buffer, 0, buffer.Length); +#else + output.Write(buffer.AsSpan()); +#endif } internal static long EstimateLength(IValue value, IOffloadOptions? offloadOptions) @@ -145,23 +149,22 @@ internal static long EncodeInteger(in Integer value, byte[] buffer, long offset) return 1L + digits.Length + 1L; } - internal static long EncodeBinary(in Binary value, byte[] buffer, long offset) + internal static long EncodeBinary(in ReadOnlySpan bin, byte[] buffer, long offset) { - long len = value.ByteArray.Length; + long len = bin.Length; long lenStrLength = EncodeDigits(len, buffer, offset); offset += lenStrLength; buffer[offset] = 0x3a; // ':' offset++; - if (offset + len <= int.MaxValue) + long ct = 0; + foreach (byte b in bin) { - value.ByteArray.CopyTo(buffer, (int)offset); - return lenStrLength + 1L + len; + buffer[offset + ct] = b; + ct++; } - byte[] b = value.ToByteArray(); - Array.Copy(b, 0L, buffer, offset, b.LongLength); - return lenStrLength + 1L + b.LongLength; + return lenStrLength + 1L + ct; } internal static long EncodeText(in Text value, byte[] buffer, long offset) @@ -240,7 +243,7 @@ long offset actualBytes += pair.Key switch { Text tk => EncodeText(tk, buffer, offset + actualBytes), - Binary bk => EncodeBinary(bk, buffer, offset + actualBytes), + Binary bk => EncodeBinary(bk.ByteArray.AsSpan(), buffer, offset + actualBytes), { } k => Encode(k, offloadOptions, buffer, offset + actualBytes), }; if (offloadOptions is { } oo && !oo.Embeds(pair.Value)) @@ -322,7 +325,7 @@ long offset Null _ => EncodeNull(buffer, offset), Types.Boolean b => EncodeBoolean(b, buffer, offset), Integer i => EncodeInteger(i, buffer, offset), - Binary bin => EncodeBinary(bin, buffer, offset), + Binary bin => EncodeBinary(bin.ByteArray.AsSpan(), buffer, offset), Text t => EncodeText(t, buffer, offset), List l => EncodeList(l, offloadOptions, buffer, offset), Dictionary d => EncodeDictionary(d, offloadOptions, buffer, offset), From 9061b577dbfccc4be3b61bda043a34a4173ca7e0 Mon Sep 17 00:00:00 2001 From: Swen Mun Date: Tue, 16 May 2023 04:37:46 +0900 Subject: [PATCH 2/2] More copy-less --- Bencodex/Decoder.cs | 25 +++++------ Bencodex/Types/Binary.cs | 26 +++++++---- Bencodex/Types/Dictionary.cs | 84 ++++++++++++++++++------------------ Bencodex/Types/List.cs | 52 ++++++++++++---------- 4 files changed, 99 insertions(+), 88 deletions(-) diff --git a/Bencodex/Decoder.cs b/Bencodex/Decoder.cs index 065ffbd..a06048f 100644 --- a/Bencodex/Decoder.cs +++ b/Bencodex/Decoder.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.IO; using System.Numerics; +using System.Runtime.CompilerServices; using System.Text; using Bencodex.Misc; using Bencodex.Types; @@ -12,7 +13,6 @@ namespace Bencodex { internal sealed class Decoder { - private readonly byte[] _tinyBuffer = new byte[1]; private readonly Stream _stream; private readonly IndirectValue.Loader? _indirectValueLoader; private byte _lastRead; @@ -74,7 +74,7 @@ private IValue DecodeValue() return ReadTextAfterPrefix(); case 0x6c: // 'l' - var indirElements = new List(); + var indirBuilder = ImmutableArray.CreateBuilder(); while (true) { byte b = ReadByte() ?? throw new DecodingException( @@ -87,17 +87,17 @@ private IValue DecodeValue() else if (b == indir) { Fingerprint fp = DecodeFingerprint(); - indirElements.Add(new IndirectValue(fp)); + indirBuilder.Add(new IndirectValue(fp)); continue; } Back(); IValue element = DecodeValue(); - indirElements.Add(new IndirectValue(element)); + indirBuilder.Add(new IndirectValue(element)); } return new Bencodex.Types.List( - indirElements.ToImmutableArray(), + indirBuilder.MoveToImmutable().AsSpan(), _indirectValueLoader ); @@ -255,15 +255,15 @@ private byte[] Read(byte[] buffer) return _lastRead; } - int read = _stream.Read(_tinyBuffer, 0, 1); + int read = _stream.ReadByte(); if (read > 0) { - _lastRead = _tinyBuffer[0]; + _lastRead = (byte)read; } _offset++; _didBack = false; - return read == 0 ? (byte?)null : _tinyBuffer[0]; + return read == -1 ? (byte?)null : _lastRead; } private void Back() @@ -363,12 +363,7 @@ Func converter ) { byte[] buffer = ReadDigits(takeMinusSign, delimiter); - var digits = new char[buffer.Length]; - for (int i = 0; i < buffer.Length; i++) - { - digits[i] = (char)buffer[i]; - } - + var digits = Unsafe.As(ref buffer); return converter(new string(digits), CultureInfo.InvariantCulture); } @@ -399,7 +394,7 @@ Func converter private Binary ReadBinary() { (byte[] bytes, _) = ReadByteArray(); - return new Binary(bytes); + return new Binary(bytes.AsSpan()); } private Text ReadTextAfterPrefix() diff --git a/Bencodex/Types/Binary.cs b/Bencodex/Types/Binary.cs index 0553847..7eddfb2 100644 --- a/Bencodex/Types/Binary.cs +++ b/Bencodex/Types/Binary.cs @@ -30,13 +30,24 @@ namespace Bencodex.Types private readonly int?[] _hashCode; private readonly ImmutableArray?[] _digest; - public Binary(ImmutableArray value) + public Binary(ReadOnlySpan value) { - _value = value; + var builder = ImmutableArray.CreateBuilder(value.Length); + foreach (byte b in value) + { + builder.Add(b); + } + + _value = builder.MoveToImmutable(); _hashCode = new int?[1]; _digest = new[] { (ImmutableArray?)null }; } + public Binary(ImmutableArray value) + : this(value.AsSpan()) + { + } + public Binary(params byte[] value) : this(value is byte[] bytes ? ImmutableArray.Create(bytes) @@ -90,7 +101,7 @@ public Fingerprint Fingerprint public string Inspection => Inspect(true); public static implicit operator Binary(ImmutableArray bytes) => - new Binary(bytes); + new Binary(bytes.AsSpan()); public static implicit operator ImmutableArray(Binary binary) => binary.ByteArray; @@ -179,7 +190,7 @@ byte ParseNibble(char hex) bytes.Add((byte)(ParseNibble(upper) << 4 | ParseNibble(lower))); } - return new Binary(bytes.MoveToImmutable()); + return new Binary(bytes.MoveToImmutable().AsSpan()); } #if !NETSTANDARD2_0 @@ -225,8 +236,7 @@ public static Binary FromBase64(ReadOnlySpan base64) public static Binary FromBase64(string base64) { byte[] bytes = Convert.FromBase64String(base64); - ImmutableArray moved = Unsafe.As>(ref bytes); - return new Binary(moved); + return new Binary(bytes.AsSpan()); } bool IEquatable>.Equals(ImmutableArray other) => @@ -325,10 +335,10 @@ public byte[] ToByteArray() { if (ByteArray.IsDefaultOrEmpty) { - return new byte[0]; + return Array.Empty(); } - return ByteArray.ToBuilder().ToArray(); + return ByteArray.ToArray(); } /// diff --git a/Bencodex/Types/Dictionary.cs b/Bencodex/Types/Dictionary.cs index 39a55a1..5d773e9 100644 --- a/Bencodex/Types/Dictionary.cs +++ b/Bencodex/Types/Dictionary.cs @@ -212,7 +212,7 @@ public Dictionary(IEnumerable> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable>> pairs) - : this(pairs.Select(p => new KeyValuePair(p.Key, new Binary(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(p.Key, new Binary(p.Value.AsSpan())))) { } @@ -382,7 +382,7 @@ public Dictionary(IEnumerable> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable>> pairs) - : this(pairs.Select(p => new KeyValuePair(p.Key, new Binary(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(p.Key, new Binary(p.Value.AsSpan())))) { } @@ -552,7 +552,7 @@ public Dictionary(IEnumerable> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Text(p.Key), new Binary(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(new Text(p.Key), new Binary(p.Value.AsSpan())))) { } @@ -722,7 +722,7 @@ public Dictionary(IEnumerable> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), new Binary(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), new Binary(p.Value.AsSpan())))) { } @@ -742,7 +742,7 @@ public Dictionary(IEnumerable> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, IValue>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), p.Value))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), p.Value))) { } @@ -752,7 +752,7 @@ public Dictionary(IEnumerable, IValue>> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, Boolean>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), p.Value))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), p.Value))) { } @@ -762,7 +762,7 @@ public Dictionary(IEnumerable, Boolean>> pairs /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, Integer>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), p.Value))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), p.Value))) { } @@ -772,7 +772,7 @@ public Dictionary(IEnumerable, Integer>> pairs /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, Binary>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), p.Value))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), p.Value))) { } @@ -782,7 +782,7 @@ public Dictionary(IEnumerable, Binary>> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, Text>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), p.Value))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), p.Value))) { } @@ -792,7 +792,7 @@ public Dictionary(IEnumerable, Text>> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, List>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), p.Value))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), p.Value))) { } @@ -802,7 +802,7 @@ public Dictionary(IEnumerable, List>> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, Dictionary>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), p.Value))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), p.Value))) { } @@ -812,7 +812,7 @@ public Dictionary(IEnumerable, Dictionary>> pa /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, bool>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), new Boolean(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), new Boolean(p.Value)))) { } @@ -822,7 +822,7 @@ public Dictionary(IEnumerable, bool>> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, short>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), new Integer(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), new Integer(p.Value)))) { } @@ -832,7 +832,7 @@ public Dictionary(IEnumerable, short>> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, ushort>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), new Integer(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), new Integer(p.Value)))) { } @@ -842,7 +842,7 @@ public Dictionary(IEnumerable, ushort>> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, int>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), new Integer(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), new Integer(p.Value)))) { } @@ -852,7 +852,7 @@ public Dictionary(IEnumerable, int>> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, uint>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), new Integer(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), new Integer(p.Value)))) { } @@ -862,7 +862,7 @@ public Dictionary(IEnumerable, uint>> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, long>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), new Integer(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), new Integer(p.Value)))) { } @@ -872,7 +872,7 @@ public Dictionary(IEnumerable, long>> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, ulong>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), new Integer(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), new Integer(p.Value)))) { } @@ -882,7 +882,7 @@ public Dictionary(IEnumerable, ulong>> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, byte[]>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), new Binary(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), new Binary(p.Value)))) { } @@ -892,7 +892,7 @@ public Dictionary(IEnumerable, byte[]>> pairs) /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, ImmutableArray>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), new Binary(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), new Binary(p.Value.AsSpan())))) { } @@ -902,7 +902,7 @@ public Dictionary(IEnumerable, ImmutableArray< /// Key-value pairs to include. If there are duplicated keys, /// later pairs overwrite earlier ones. public Dictionary(IEnumerable, string>> pairs) - : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key), new Text(p.Value)))) + : this(pairs.Select(p => new KeyValuePair(new Binary(p.Key.AsSpan()), new Text(p.Value)))) { } @@ -1105,7 +1105,7 @@ public IEnumerator> GetEnumerator() /// The bytes key to locate. /// if the dictionary contains the specified key; /// otherwise, . - public bool ContainsKey(ImmutableArray key) => ContainsKey(new Binary(key)); + public bool ContainsKey(ImmutableArray key) => ContainsKey(new Binary(key.AsSpan())); /// Determines whether the dictionary contains the specified bytes key. /// The bytes key to locate. @@ -1233,7 +1233,7 @@ public Dictionary Add(Binary key, byte[] value) => Add(key, new Binary(value)); public Dictionary Add(Binary key, ImmutableArray value) => - Add(key, new Binary(value)); + Add(key, new Binary(value.AsSpan())); public Dictionary Add(Binary key, string value) => Add(key, new Text(value)); @@ -1475,7 +1475,7 @@ public Dictionary SetItem(Text key, byte[] value) => SetItem(key, new Binary(value)); public Dictionary SetItem(Text key, ImmutableArray value) => - SetItem(key, new Binary(value)); + SetItem(key, new Binary(value.AsSpan())); public Dictionary SetItem(Text key, string value) => SetItem(key, new Text(value)); @@ -1583,31 +1583,31 @@ public Dictionary SetItem(string key, byte[] value) => SetItem(key, new Binary(value)); public Dictionary SetItem(string key, ImmutableArray value) => - SetItem(key, new Binary(value)); + SetItem(key, new Binary(value.AsSpan())); public Dictionary SetItem(string key, string value) => SetItem(key, new Text(value)); public Dictionary SetItem(ImmutableArray key, IValue value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(ImmutableArray key, Boolean value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(ImmutableArray key, Integer value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(ImmutableArray key, Binary value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(ImmutableArray key, Text value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(ImmutableArray key, List value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(ImmutableArray key, Dictionary value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(ImmutableArray key, bool value) => SetItem(key, new Boolean(value)); @@ -1637,31 +1637,31 @@ public Dictionary SetItem(ImmutableArray key, byte[] value) => SetItem(key, new Binary(value)); public Dictionary SetItem(ImmutableArray key, ImmutableArray value) => - SetItem(key, new Binary(value)); + SetItem(key, new Binary(value.AsSpan())); public Dictionary SetItem(ImmutableArray key, string value) => SetItem(key, new Text(value)); public Dictionary SetItem(byte[] key, IValue value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(byte[] key, Boolean value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(byte[] key, Integer value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(byte[] key, Binary value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(byte[] key, Text value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(byte[] key, List value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(byte[] key, Dictionary value) => - SetItem(new Binary(key), value); + SetItem(new Binary(key.AsSpan()), value); public Dictionary SetItem(byte[] key, bool value) => SetItem(key, new Boolean(value)); @@ -1688,10 +1688,10 @@ public Dictionary SetItem(byte[] key, BigInteger value) => SetItem(key, new Integer(value)); public Dictionary SetItem(byte[] key, byte[] value) => - SetItem(key, new Binary(value)); + SetItem(key, new Binary(value.AsSpan())); public Dictionary SetItem(byte[] key, ImmutableArray value) => - SetItem(key, new Binary(value)); + SetItem(key, new Binary(value.AsSpan())); public Dictionary SetItem(byte[] key, string value) => SetItem(key, new Text(value)); diff --git a/Bencodex/Types/List.cs b/Bencodex/Types/List.cs index 8c417bf..05e8d5e 100644 --- a/Bencodex/Types/List.cs +++ b/Bencodex/Types/List.cs @@ -23,7 +23,7 @@ public sealed class List : /// /// The empty list. /// - public static readonly List Empty = new List(ImmutableArray.Empty, null) + public static readonly List Empty = new List(ImmutableArray.Empty.AsSpan(), null) { EncodingLength = 2L, }; @@ -52,7 +52,7 @@ public List(params IValue[] elements) /// /// The element values to include. public List(IEnumerable elements) - : this(elements.Select(v => new IndirectValue(v)).ToImmutableArray(), null) + : this(elements.Select(v => new IndirectValue(v)).ToArray().AsSpan(), null) { } @@ -61,7 +61,7 @@ public List(IEnumerable elements) /// /// The element values to include. public List(IEnumerable elements) - : this(elements.Select(v => (IValue)v)) + : this(elements.Cast()) { } @@ -70,7 +70,7 @@ public List(IEnumerable elements) /// /// The element values to include. public List(IEnumerable elements) - : this(elements.Select(v => (IValue)v)) + : this(elements.Cast()) { } @@ -79,7 +79,7 @@ public List(IEnumerable elements) /// /// The element values to include. public List(IEnumerable elements) - : this(elements.Select(v => (IValue)v)) + : this(elements.Cast()) { } @@ -88,7 +88,7 @@ public List(IEnumerable elements) /// /// The element values to include. public List(IEnumerable elements) - : this(elements.Select(v => (IValue)v)) + : this(elements.Cast()) { } @@ -178,7 +178,7 @@ public List(IEnumerable elements) /// /// The element values to include. public List(IEnumerable> elements) - : this(elements.Select(v => new Binary(v))) + : this(elements.Select(v => new Binary(v.AsSpan()))) { } @@ -200,20 +200,26 @@ public List(IEnumerable elements) /// unloaded values are needed. public List(IEnumerable indirectValues, IndirectValue.Loader loader) : this( - indirectValues is ImmutableArray ia + (indirectValues is ImmutableArray ia ? ia - : indirectValues.ToImmutableArray(), + : indirectValues.ToImmutableArray()).AsSpan(), loader ) { } internal List( - in ImmutableArray indirectValues, + in ReadOnlySpan indirectValues, IndirectValue.Loader? loader ) { - _values = indirectValues; + var builder = ImmutableArray.CreateBuilder(indirectValues.Length); + foreach (IndirectValue iv in indirectValues) + { + builder.Add(iv); + } + + _values = builder.MoveToImmutable(); Loader = loader; _hash = null; } @@ -350,7 +356,7 @@ IImmutableList IImmutableList.Add(IValue value) => /// The value to add to the list. /// A new list with the value added. public List Add(IValue value) => - new List(_values.Add(new IndirectValue(value)), Loader) + new List(_values.Add(new IndirectValue(value)).AsSpan(), Loader) { EncodingLength = _encodingLength < 2L ? -1 : _encodingLength + value.EncodingLength, }; @@ -461,7 +467,7 @@ public List Add(byte[] value) => /// a Bencodex instance. /// A new list with the value added. public List Add(ImmutableArray value) => - Add(new Binary(value)); + Add(new Binary(value.AsSpan())); /// Makes a copy of the list, and adds the specified /// to the end of the copied list. @@ -474,7 +480,7 @@ public List Add(string value) => IImmutableList IImmutableList.AddRange( IEnumerable items ) => - new List(_values.AddRange(items.Select(v => new IndirectValue(v))), Loader); + new List(_values.AddRange(items.Select(v => new IndirectValue(v))).AsSpan(), Loader); IImmutableList IImmutableList.Clear() => List.Empty; @@ -495,7 +501,7 @@ IEqualityComparer equalityComparer ); IImmutableList IImmutableList.Insert(int index, IValue element) => - new List(_values.Insert(index, new IndirectValue(element)), Loader); + new List(_values.Insert(index, new IndirectValue(element)).AsSpan(), Loader); IImmutableList IImmutableList.InsertRange( int index, @@ -503,7 +509,7 @@ IEnumerable items ) { IEnumerable vs = items.Select(v => new IndirectValue(v)); - return new List(_values.InsertRange(index, vs), Loader); + return new List(_values.InsertRange(index, vs).AsSpan(), Loader); } [Obsolete("This operation immediately loads all unloaded values in the range on " + @@ -530,7 +536,7 @@ IEqualityComparer equalityComparer _values.Remove( new IndirectValue(value), new IndirectValueEqualityComparer(equalityComparer, Loader) - ), + ).AsSpan(), Loader ); @@ -538,10 +544,10 @@ IEqualityComparer equalityComparer IImmutableList IImmutableList.RemoveAll( Predicate match ) => - new List(_values.RemoveAll(iv => match(iv.GetValue(Loader))), Loader); + new List(_values.RemoveAll(iv => match(iv.GetValue(Loader))).AsSpan(), Loader); IImmutableList IImmutableList.RemoveAt(int index) => - new List(_values.RemoveAt(index), Loader); + new List(_values.RemoveAt(index).AsSpan(), Loader); [Obsolete("This operation immediately loads all unloaded values on the memory.")] IImmutableList IImmutableList.RemoveRange( @@ -552,12 +558,12 @@ IEqualityComparer equalityComparer _values.RemoveRange( items.Select(v => new IndirectValue(v)), new IndirectValueEqualityComparer(equalityComparer, Loader) - ), + ).AsSpan(), Loader ); IImmutableList IImmutableList.RemoveRange(int index, int count) => - new List(_values.RemoveRange(index, count), Loader); + new List(_values.RemoveRange(index, count).AsSpan(), Loader); [Obsolete("This operation immediately loads all unloaded values on the memory.")] IImmutableList IImmutableList.Replace( @@ -570,12 +576,12 @@ IEqualityComparer equalityComparer new IndirectValue(oldValue), new IndirectValue(newValue), new IndirectValueEqualityComparer(equalityComparer, Loader) - ), + ).AsSpan(), Loader ); IImmutableList IImmutableList.SetItem(int index, IValue value) => - new List(_values.SetItem(index, new IndirectValue(value)), Loader); + new List(_values.SetItem(index, new IndirectValue(value)).AsSpan(), Loader); /// public string Inspect(bool loadAll)