Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use spans in BlobWriter, reducing pinning and ImmutableArrayInterop.
  • Loading branch information
teo-tsirpanis committed Oct 27, 2022
commit a2acd50c51db545fa345543fb6d91b53b6c82a8f
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
namespace System.Reflection.Metadata
{
// TODO: argument checking
public unsafe struct BlobWriter
public struct BlobWriter
{
// writable slice:
private readonly byte[] _buffer;
Expand Down Expand Up @@ -88,9 +88,7 @@ public byte[] ToArray(int start, int byteCount)
{
BlobUtilities.ValidateRange(Length, start, byteCount, nameof(byteCount));

var result = new byte[byteCount];
Array.Copy(_buffer, _start + start, result, 0, byteCount);
return result;
return _buffer.AsSpan(_start + start, byteCount).ToArray();
}

public ImmutableArray<byte> ToImmutableArray()
Expand All @@ -101,8 +99,9 @@ public ImmutableArray<byte> ToImmutableArray()
/// <exception cref="ArgumentOutOfRangeException">Range specified by <paramref name="start"/> and <paramref name="byteCount"/> falls outside of the bounds of the buffer content.</exception>
public ImmutableArray<byte> ToImmutableArray(int start, int byteCount)
{
byte[]? array = ToArray(start, byteCount);
return ImmutableByteArrayInterop.DangerousCreateFromUnderlyingArray(ref array);
BlobUtilities.ValidateRange(Length, start, byteCount, nameof(byteCount));

return ImmutableArray.Create(_buffer.AsSpan(_start + start, byteCount));
}

private int Advance(int value)
Expand All @@ -128,14 +127,7 @@ public void WriteBytes(byte value, int byteCount)
}

int start = Advance(byteCount);
fixed (byte* buffer = _buffer)
{
byte* ptr = buffer + start;
for (int i = 0; i < byteCount; i++)
{
ptr[i] = value;
}
}
_buffer.AsSpan(start, byteCount).Fill(value);
}

/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
Expand All @@ -152,13 +144,13 @@ public unsafe void WriteBytes(byte* buffer, int byteCount)
Throw.ArgumentOutOfRange(nameof(byteCount));
}

WriteBytesUnchecked(buffer, byteCount);
WriteBytesUnchecked(new ReadOnlySpan<byte>(buffer, byteCount));
}

private unsafe void WriteBytesUnchecked(byte* buffer, int byteCount)
private void WriteBytesUnchecked(ReadOnlySpan<byte> buffer)
{
int start = Advance(byteCount);
Marshal.Copy((IntPtr)buffer, _buffer, start, byteCount);
int start = Advance(buffer.Length);
buffer.CopyTo(_buffer.AsSpan(start));
}

/// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
Expand Down Expand Up @@ -195,25 +187,42 @@ public int WriteBytes(Stream source, int byteCount)
/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
public void WriteBytes(ImmutableArray<byte> buffer)
{
WriteBytes(buffer, 0, buffer.IsDefault ? 0 : buffer.Length);
if (buffer.IsDefault)
{
Throw.ArgumentNull(nameof(buffer));
}

WriteBytesUnchecked(buffer.AsSpan());
}

/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException">Range specified by <paramref name="start"/> and <paramref name="byteCount"/> falls outside of the bounds of the <paramref name="buffer"/>.</exception>
public void WriteBytes(ImmutableArray<byte> buffer, int start, int byteCount)
{
WriteBytes(ImmutableByteArrayInterop.DangerousGetUnderlyingArray(buffer)!, start, byteCount);
if (buffer.IsDefault)
{
Throw.ArgumentNull(nameof(buffer));
}

BlobUtilities.ValidateRange(buffer.Length, start, byteCount, nameof(byteCount));

WriteBytesUnchecked(buffer.AsSpan(start, byteCount));
}

/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
public unsafe void WriteBytes(byte[] buffer)
public void WriteBytes(byte[] buffer)
{
WriteBytes(buffer, 0, buffer?.Length ?? 0);
if (buffer is null)
{
Throw.ArgumentNull(nameof(buffer));
}

WriteBytesUnchecked(buffer);
}

/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException">Range specified by <paramref name="start"/> and <paramref name="byteCount"/> falls outside of the bounds of the <paramref name="buffer"/>.</exception>
public unsafe void WriteBytes(byte[] buffer, int start, int byteCount)
public void WriteBytes(byte[] buffer, int start, int byteCount)
{
if (buffer is null)
{
Expand All @@ -222,16 +231,7 @@ public unsafe void WriteBytes(byte[] buffer, int start, int byteCount)

BlobUtilities.ValidateRange(buffer.Length, start, byteCount, nameof(byteCount));

// an empty array has no element pointer:
if (buffer.Length == 0)
{
return;
}

fixed (byte* ptr = &buffer[0])
{
WriteBytes(ptr + start, byteCount);
}
WriteBytesUnchecked(buffer.AsSpan(start, byteCount));
}

public void PadTo(int offset)
Expand Down Expand Up @@ -376,25 +376,7 @@ public void WriteUTF16(char[] value)
Throw.ArgumentNull(nameof(value));
}

if (value.Length == 0)
{
return;
}

if (BitConverter.IsLittleEndian)
{
fixed (char* ptr = &value[0])
{
WriteBytesUnchecked((byte*)ptr, value.Length * sizeof(char));
}
}
else
{
for (int i = 0; i < value.Length; i++)
{
WriteUInt16((ushort)value[i]);
}
}
WriteUTF16(value.AsSpan());
}

/// <summary>
Expand All @@ -408,18 +390,20 @@ public void WriteUTF16(string value)
Throw.ArgumentNull(nameof(value));
}

WriteUTF16(value.AsSpan());
}

private void WriteUTF16(ReadOnlySpan<char> value)
{
if (BitConverter.IsLittleEndian)
{
fixed (char* ptr = value)
{
WriteBytesUnchecked((byte*)ptr, value.Length * sizeof(char));
}
WriteBytesUnchecked(MemoryMarshal.AsBytes(value));
}
else
{
for (int i = 0; i < value.Length; i++)
foreach (char c in value)
{
WriteUInt16((ushort)value[i]);
WriteUInt16(c);
}
}
}
Expand Down Expand Up @@ -480,7 +464,7 @@ public void WriteUTF8(string value, bool allowUnpairedSurrogates)
WriteUTF8(value, 0, value.Length, allowUnpairedSurrogates, prependSize: false);
}

private void WriteUTF8(string str, int start, int length, bool allowUnpairedSurrogates, bool prependSize)
private unsafe void WriteUTF8(string str, int start, int length, bool allowUnpairedSurrogates, bool prependSize)
{
fixed (char* strPtr = str)
{
Expand Down