Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use more spans in System.Reflection.Metadata et. al. #76574

Merged
merged 16 commits into from
Nov 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal abstract class AbstractMemoryBlock : IDisposable
/// </remarks>
public virtual unsafe ImmutableArray<byte> GetContentUnchecked(int start, int length)
{
var result = BlobUtilities.ReadImmutableBytes(Pointer + start, length);
var result = new ReadOnlySpan<byte>(Pointer + start, length).ToImmutableArray();
GC.KeepAlive(this);
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public override Stream GetStream(out StreamConstraints constraints)
}

/// <exception cref="IOException">IO error while mapping memory or not enough memory to create the mapping.</exception>
private unsafe bool TryCreateMemoryMappedFileBlock(long start, int size, [NotNullWhen(true)]out MemoryMappedFileBlock? block)
private unsafe bool TryCreateMemoryMappedFileBlock(long start, int size, [NotNullWhen(true)] out MemoryMappedFileBlock? block)
{
if (_lazyMemoryMap == null)
{
Expand All @@ -142,7 +142,8 @@ private unsafe bool TryCreateMemoryMappedFileBlock(long start, int size, [NotNul
access: MemoryMappedFileAccess.Read,
inheritability: HandleInheritability.None,
leaveOpen: true);
} catch (UnauthorizedAccessException e)
}
catch (UnauthorizedAccessException e)
{
throw new IOException(e.Message, e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Immutable;
using System.Buffers.Binary;
using System.Diagnostics;
using System.Reflection.Internal;
using System.Runtime.CompilerServices;
Expand All @@ -12,36 +11,11 @@ namespace System.Reflection
{
internal static unsafe class BlobUtilities
{
public static byte[] ReadBytes(byte* buffer, int byteCount)
{
if (byteCount == 0)
{
return Array.Empty<byte>();
}

byte[] result = new byte[byteCount];
Marshal.Copy((IntPtr)buffer, result, 0, byteCount);
return result;
}

public static ImmutableArray<byte> ReadImmutableBytes(byte* buffer, int byteCount)
{
byte[]? bytes = ReadBytes(buffer, byteCount);
return ImmutableByteArrayInterop.DangerousCreateFromUnderlyingArray(ref bytes);
}

public static void WriteBytes(this byte[] buffer, int start, byte value, int byteCount)
{
Debug.Assert(buffer.Length > 0);

fixed (byte* bufferPtr = &buffer[0])
{
byte* startPtr = bufferPtr + start;
for (int i = 0; i < byteCount; i++)
{
startPtr[i] = value;
}
}
new Span<byte>(buffer, start, byteCount).Fill(value);
}

public static void WriteDouble(this byte[] buffer, int start, double value)
Expand All @@ -60,63 +34,20 @@ public static void WriteByte(this byte[] buffer, int start, byte value)
buffer[start] = value;
}

public static void WriteUInt16(this byte[] buffer, int start, ushort value)
{
fixed (byte* ptr = &buffer[start])
{
unchecked
{
ptr[0] = (byte)value;
ptr[1] = (byte)(value >> 8);
}
}
}
public static void WriteUInt16(this byte[] buffer, int start, ushort value) =>
Unsafe.WriteUnaligned(ref buffer[start], !BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value);

public static void WriteUInt16BE(this byte[] buffer, int start, ushort value)
{
fixed (byte* ptr = &buffer[start])
{
unchecked
{
ptr[0] = (byte)(value >> 8);
ptr[1] = (byte)value;
}
}
}
public static void WriteUInt16BE(this byte[] buffer, int start, ushort value) =>
Unsafe.WriteUnaligned(ref buffer[start], BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value);

public static void WriteUInt32BE(this byte[] buffer, int start, uint value)
{
fixed (byte* ptr = &buffer[start])
{
unchecked
{
ptr[0] = (byte)(value >> 24);
ptr[1] = (byte)(value >> 16);
ptr[2] = (byte)(value >> 8);
ptr[3] = (byte)value;
}
}
}
public static void WriteUInt32BE(this byte[] buffer, int start, uint value) =>
Unsafe.WriteUnaligned(ref buffer[start], BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value);

public static void WriteUInt32(this byte[] buffer, int start, uint value)
{
fixed (byte* ptr = &buffer[start])
{
unchecked
{
ptr[0] = (byte)value;
ptr[1] = (byte)(value >> 8);
ptr[2] = (byte)(value >> 16);
ptr[3] = (byte)(value >> 24);
}
}
}
public static void WriteUInt32(this byte[] buffer, int start, uint value) =>
Unsafe.WriteUnaligned(ref buffer[start], !BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value);

public static void WriteUInt64(this byte[] buffer, int start, ulong value)
{
WriteUInt32(buffer, start, unchecked((uint)value));
WriteUInt32(buffer, start + 4, unchecked((uint)(value >> 32)));
}
public static void WriteUInt64(this byte[] buffer, int start, ulong value) =>
Unsafe.WriteUnaligned(ref buffer[start], !BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value);

public const int SizeOfSerializedDecimal = sizeof(byte) + 3 * sizeof(uint);

Expand All @@ -137,6 +68,11 @@ public static void WriteDecimal(this byte[] buffer, int start, decimal value)

public static void WriteGuid(this byte[] buffer, int start, Guid value)
{
#if NETCOREAPP
bool written = value.TryWriteBytes(buffer.AsSpan(start));
// This function is not public, callers have to ensure that enough space is available.
Debug.Assert(written);
#else
fixed (byte* dst = &buffer[start])
{
byte* src = (byte*)&value;
Expand Down Expand Up @@ -167,6 +103,7 @@ public static void WriteGuid(this byte[] buffer, int start, Guid value)
dst[14] = src[14];
dst[15] = src[15];
}
#endif
}

public static void WriteUTF8(this byte[] buffer, int start, char* charPtr, int charCount, int byteCount, bool allowUnpairedSurrogates)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Immutable;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace System.Reflection.Internal
{
Expand All @@ -22,16 +23,10 @@ namespace System.Reflection.Internal
///
/// This implementation is scoped to byte arrays as that is all that the metadata reader needs.
///
/// Also, since we don't have access to immutable collection internals, we use a trick involving
/// overlapping a <see cref="ImmutableArray{Byte}"/> with an array reference. While
/// unverifiable, it is valid. See ECMA-335, section II.10.7 Controlling instance layout:
/// Also, since we don't have access to immutable collection internals, we use
/// <see cref="Unsafe.As{TFrom, TTo}(ref TFrom)"/>.
///
/// "It is possible to overlap fields in this way, though offsets occupied by an object reference
/// shall not overlap with offsets occupied by a built-in value type or a part of
/// another object reference. While one object reference can completely overlap another, this is
/// unverifiable."
///
/// Furthermore, the fact that <see cref="ImmutableArray{Byte}"/> backed by a single byte array
/// The fact that <see cref="ImmutableArray{Byte}"/> is backed by a single byte array
/// field is something inherent to the design of ImmutableArray in order to get its performance
/// characteristics and therefore something we (Microsoft) are comfortable defining as a contract that
/// can be depended upon as below.
Expand Down Expand Up @@ -59,9 +54,7 @@ internal static ImmutableArray<byte> DangerousCreateFromUnderlyingArray(ref byte
byte[] givenArray = array!;
array = null;

ByteArrayUnion union = default;
union.UnderlyingArray = givenArray;
return union.ImmutableArray;
return Unsafe.As<byte[], ImmutableArray<byte>>(ref givenArray);
}

/// <summary>
Expand All @@ -81,19 +74,7 @@ internal static ImmutableArray<byte> DangerousCreateFromUnderlyingArray(ref byte
/// <returns>The underlying array, or null if <see cref="ImmutableArray{T}.IsDefault"/> is true.</returns>
internal static byte[]? DangerousGetUnderlyingArray(ImmutableArray<byte> array)
{
ByteArrayUnion union = default;
union.ImmutableArray = array;
return union.UnderlyingArray;
}

[StructLayout(LayoutKind.Explicit)]
private struct ByteArrayUnion
{
[FieldOffset(0)]
internal byte[]? UnderlyingArray;

[FieldOffset(0)]
internal ImmutableArray<byte> ImmutableArray;
return Unsafe.As<ImmutableArray<byte>, byte[]>(ref array);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers.Binary;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
Expand Down Expand Up @@ -126,11 +127,8 @@ internal uint PeekUInt32(int offset)
{
CheckBounds(offset, sizeof(uint));

unchecked
{
byte* ptr = Pointer + offset;
return (uint)(ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24));
}
uint result = Unsafe.ReadUnaligned<uint>(Pointer + offset);
return BitConverter.IsLittleEndian ? result : BinaryPrimitives.ReverseEndianness(result);
}

/// <summary>
Expand Down Expand Up @@ -187,11 +185,8 @@ internal ushort PeekUInt16(int offset)
{
CheckBounds(offset, sizeof(ushort));

unchecked
{
byte* ptr = Pointer + offset;
return (ushort)(ptr[0] | (ptr[1] << 8));
}
ushort result = Unsafe.ReadUnaligned<ushort>(Pointer + offset);
return BitConverter.IsLittleEndian ? result : BinaryPrimitives.ReverseEndianness(result);
}

// When reference has tag bits.
Expand Down Expand Up @@ -250,7 +245,7 @@ internal Guid PeekGuid(int offset)
byte* ptr = Pointer + offset;
if (BitConverter.IsLittleEndian)
{
return *(Guid*)ptr;
return Unsafe.ReadUnaligned<Guid>(ptr);
}
else
{
Expand Down Expand Up @@ -512,7 +507,7 @@ internal int CompareUtf8NullTerminatedStringWithAsciiString(int offset, string a
internal byte[] PeekBytes(int offset, int byteCount)
{
CheckBounds(offset, byteCount);
return BlobUtilities.ReadBytes(Pointer + offset, byteCount);
return new ReadOnlySpan<byte>(Pointer + offset, byteCount).ToArray();
}

internal int IndexOf(byte b, int start)
Expand Down
Loading