Skip to content
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
38 changes: 38 additions & 0 deletions src/libraries/Common/src/Polyfills/StreamSpanPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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;

namespace System.IO;

/// <summary>Provides downlevel polyfills for Span-based instance methods on <see cref="Stream"/>.</summary>
internal static class StreamSpanPolyfills
{
extension(Stream stream)
{
public int ReadAtLeast(Span<byte> destination, int minimumBytes, bool throwOnEndOfStream = true)
{
byte[] buffer = ArrayPool<byte>.Shared.Rent(Math.Min(destination.Length, 81920));
int totalWritten = 0;
while (totalWritten < minimumBytes && !destination.IsEmpty)
{
int written = stream.Read(buffer, 0, Math.Min(buffer.Length, destination.Length));
if (written == 0)
{
if (throwOnEndOfStream)
{
ThrowEndOfStreamException();
}
break;
}
buffer.AsSpan(0, written).CopyTo(destination);
totalWritten += written;
destination = destination.Slice(written);
}
ArrayPool<byte>.Shared.Return(buffer);
return totalWritten;
Comment thread
jkotas marked this conversation as resolved.

static void ThrowEndOfStreamException() => throw new EndOfStreamException();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
Expand Down Expand Up @@ -27,6 +27,9 @@ The System.Reflection.Metadata library is built-in as part of the shared framewo
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.ReadFile_SafeHandle_IntPtr.cs"
Link="Common\Interop\Windows\kernel32\Interop.ReadFile_SafeHandle_IntPtr.cs"
Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'" />
<Compile Include="$(CommonPath)Polyfills\StreamSpanPolyfills.cs"
Link="Common\Polyfills\StreamSpanPolyfills.cs"
Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'" />
<Compile Include="System\Reflection\Internal\Utilities\PinnedObject.cs" />
<Compile Include="System\Reflection\Internal\Utilities\CriticalDisposableObject.cs" />
<Compile Include="System\Reflection\Internal\Utilities\ExceptionUtilities.cs" />
Expand Down Expand Up @@ -111,8 +114,7 @@ The System.Reflection.Metadata library is built-in as part of the shared framewo
<Compile Include="System\Reflection\Internal\MemoryBlocks\StreamMemoryBlockProvider.cs" />
<Compile Include="System\Reflection\Internal\Utilities\BitArithmetic.cs" />
<Compile Include="System\Reflection\Internal\Utilities\StringUtils.cs" />
<Compile Include="System\Reflection\Internal\Utilities\EncodingHelper.cs" Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'" />
<Compile Include="System\Reflection\Internal\Utilities\EncodingHelper.netcoreapp.cs" Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'" />
<Compile Include="System\Reflection\Internal\Utilities\EncodingHelper.cs" />
<Compile Include="System\Reflection\Internal\Utilities\StreamExtensions.netcoreapp.cs" Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'" />
<Compile Include="System\Reflection\Internal\Utilities\StreamExtensions.netstandard2.0.cs" Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'" />
<Compile Include="System\Reflection\Internal\Utilities\Hash.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ public static unsafe void WriteUTF8(this byte[] buffer, int start, char* charPtr
continue;
}

if (IsSurrogateChar(c))
if (char.IsSurrogate(c))
{
// surrogate pair
if (IsHighSurrogateChar(c) && charPtr < strEnd && IsLowSurrogateChar(*charPtr))
if (char.IsHighSurrogate(c) && charPtr < strEnd && char.IsLowSurrogate(*charPtr))
{
int highSurrogate = c;
int lowSurrogate = *charPtr++;
Expand Down Expand Up @@ -134,14 +134,6 @@ public static unsafe void WriteUTF8(this byte[] buffer, int start, char* charPtr
}
}

internal static unsafe int GetUTF8ByteCount(string str)
{
fixed (char* ptr = str)
{
return GetUTF8ByteCount(ptr, str.Length);
}
}

internal static unsafe int GetUTF8ByteCount(char* str, int charCount)
{
return GetUTF8ByteCount(str, charCount, int.MaxValue, out _);
Expand All @@ -165,7 +157,7 @@ internal static unsafe int GetUTF8ByteCount(char* str, int charCount, int byteLi
{
characterSize = 2;
}
else if (IsHighSurrogateChar(c) && ptr < end && IsLowSurrogateChar(*ptr))
else if (char.IsHighSurrogate(c) && ptr < end && char.IsLowSurrogate(*ptr))
{
// surrogate pair:
characterSize = 4;
Expand All @@ -189,21 +181,6 @@ internal static unsafe int GetUTF8ByteCount(char* str, int charCount, int byteLi
return byteCount;
}

internal static bool IsSurrogateChar(int c)
{
return unchecked((uint)(c - 0xD800)) <= 0xDFFF - 0xD800;
}

internal static bool IsHighSurrogateChar(int c)
{
return unchecked((uint)(c - 0xD800)) <= 0xDBFF - 0xD800;
}

internal static bool IsLowSurrogateChar(int c)
{
return unchecked((uint)(c - 0xDC00)) <= 0xDFFF - 0xDC00;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void ValidateRange(int bufferLength, int start, int byteCount, string byteCountParameterName)
{
Expand Down
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;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
Expand All @@ -13,15 +14,7 @@ namespace System.Reflection.Internal
/// </summary>
internal static unsafe class EncodingHelper
{
// Size of pooled buffers. The vast majority of metadata strings
// are quite small so we don't need to waste memory with large buffers.
public const int PooledBufferSize = 200;

// Use AcquireBuffer(int) and ReleaseBuffer(byte[])
// instead of the pool directly to implement the size check.
private static readonly ObjectPool<byte[]> s_pool = new ObjectPool<byte[]>(() => new byte[PooledBufferSize]);

public static string DecodeUtf8(byte* bytes, int byteCount, byte[] prefix, MetadataStringDecoder utf8Decoder)
public static string DecodeUtf8(byte* bytes, int byteCount, byte[]? prefix, MetadataStringDecoder utf8Decoder)
{
Debug.Assert(utf8Decoder != null);

Expand Down Expand Up @@ -49,7 +42,7 @@ private static string DecodeUtf8Prefixed(byte* bytes, int byteCount, byte[] pref
return string.Empty;
}

byte[] buffer = AcquireBuffer(prefixedByteCount);
byte[] buffer = ArrayPool<byte>.Shared.Rent(prefixedByteCount);

prefix.CopyTo(buffer, 0);
Marshal.Copy((IntPtr)bytes, buffer, prefix.Length, byteCount);
Expand All @@ -60,26 +53,8 @@ private static string DecodeUtf8Prefixed(byte* bytes, int byteCount, byte[] pref
result = utf8Decoder.GetString(prefixedBytes, prefixedByteCount);
}

ReleaseBuffer(buffer);
ArrayPool<byte>.Shared.Return(buffer);
return result;
Comment thread
MihaZupan marked this conversation as resolved.
}

private static byte[] AcquireBuffer(int byteCount)
{
if (byteCount > PooledBufferSize)
{
return new byte[byteCount];
}

return s_pool.Allocate();
}

private static void ReleaseBuffer(byte[] buffer)
{
if (buffer.Length == PooledBufferSize)
{
s_pool.Free(buffer);
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ internal static int Combine(uint newKey, int currentKey)
return unchecked((currentKey * (int)0xA5555529) + (int)newKey);
}

internal static int Combine(bool newKeyPart, int currentKey)
{
return Combine(currentKey, newKeyPart ? 1 : 0);
}

/// <summary>
/// The offset bias value used in the FNV-1a algorithm
/// See http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,29 +46,6 @@ internal static int TryReadAll(this Stream stream, byte[] buffer, int offset, in
return totalBytesRead;
}

#if NET
internal static int TryReadAll(this Stream stream, Span<byte> buffer)
#if NET
=> stream.ReadAtLeast(buffer, buffer.Length, throwOnEndOfStream: false);
#else
{
int totalBytesRead = 0;
while (totalBytesRead < buffer.Length)
{
int bytesRead = stream.Read(buffer.Slice(totalBytesRead));
if (bytesRead == 0)
{
break;
}

totalBytesRead += bytesRead;
}

return totalBytesRead;
}
#endif
#endif

/// <summary>
/// Resolve image size as either the given user-specified size or distance from current position to end-of-stream.
/// Also performs the relevant argument validation and publicly visible caller has same argument names.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using System.Reflection.Internal;
using System.Runtime.InteropServices;
using System.Text;

namespace System.Reflection.Metadata
{
Expand Down Expand Up @@ -469,7 +470,7 @@ private unsafe void WriteUTF8(string str, int start, int length, bool allowUnpai
fixed (char* strPtr = str)
{
char* charPtr = strPtr + start;
int byteCount = BlobUtilities.GetUTF8ByteCount(charPtr, length);
int byteCount = Encoding.UTF8.GetByteCount(charPtr, length);

if (prependSize)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Immutable;
using System.Reflection.Internal;
using System.Runtime.InteropServices;
using System.Text;

namespace System.Reflection.Metadata.Ecma335
{
Expand Down Expand Up @@ -553,10 +554,10 @@ private static ImmutableArray<int> SerializeStringHeap(
int position = stringHeapStartOffset + heapBuilder.Count;

// It is important to use ordinal comparison otherwise we'll use the current culture!
if (prev.EndsWith(entry.Key, StringComparison.Ordinal) && !BlobUtilities.IsLowSurrogateChar(entry.Key[0]))
if (prev.EndsWith(entry.Key, StringComparison.Ordinal) && !char.IsLowSurrogate(entry.Key[0]))
{
// Map over the tail of prev string. Watch for null-terminator of prev string.
stringVirtualIndexToHeapOffsetMap[entry.Value.GetWriterVirtualIndex()] = position - (BlobUtilities.GetUTF8ByteCount(entry.Key) + 1);
stringVirtualIndexToHeapOffsetMap[entry.Value.GetWriterVirtualIndex()] = position - (Encoding.UTF8.GetByteCount(entry.Key) + 1);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,7 @@ internal static void SerializeMetadataHeader(BlobBuilder builder, string metadat
builder.WriteByte(0);
int metadataVersionEnd = builder.Count;

for (int i = 0; i < sizes.MetadataVersionPaddedLength - (metadataVersionEnd - metadataVersionStart); i++)
{
builder.WriteByte(0);
}
builder.WriteBytes(0, sizes.MetadataVersionPaddedLength - (metadataVersionEnd - metadataVersionStart));

// reserved
builder.WriteUInt16(0);
Expand All @@ -74,45 +71,39 @@ internal static void SerializeMetadataHeader(BlobBuilder builder, string metadat
// emit the #Pdb stream first so that only a single page has to be read in order to find out PDB ID
if (sizes.IsStandaloneDebugMetadata)
{
SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.StandalonePdbStreamSize, "#Pdb", builder);
SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.StandalonePdbStreamSize, "#Pdb"u8, builder);
}

// Spec: Some compilers store metadata in a #- stream, which holds an uncompressed, or non-optimized, representation of metadata tables;
// this includes extra metadata -Ptr tables. Such PE files do not form part of ECMA-335 standard.
//
// Note: EnC delta is stored as uncompressed metadata stream.
SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.MetadataTableStreamSize, (sizes.IsCompressed ? "#~" : "#-"), builder);
SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.MetadataTableStreamSize, (sizes.IsCompressed ? "#~"u8 : "#-"u8), builder);

SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.String), "#Strings", builder);
SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.UserString), "#US", builder);
SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.Guid), "#GUID", builder);
SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.Blob), "#Blob", builder);
SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.String), "#Strings"u8, builder);
SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.UserString), "#US"u8, builder);
SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.Guid), "#GUID"u8, builder);
SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.Blob), "#Blob"u8, builder);

if (sizes.IsEncDelta)
{
SerializeStreamHeader(ref offsetFromStartOfMetadata, 0, "#JTD", builder);
SerializeStreamHeader(ref offsetFromStartOfMetadata, 0, "#JTD"u8, builder);
}

int endOffset = builder.Count;
Debug.Assert(endOffset - startOffset == sizes.MetadataHeaderSize);
}

private static void SerializeStreamHeader(ref int offsetFromStartOfMetadata, int alignedStreamSize, string streamName, BlobBuilder builder)
private static void SerializeStreamHeader(ref int offsetFromStartOfMetadata, int alignedStreamSize, ReadOnlySpan<byte> streamName, BlobBuilder builder)
{
// 4 for the first uint (offset), 4 for the second uint (padded size), length of stream name + 1 for null terminator (then padded)
int sizeOfStreamHeader = MetadataSizes.GetMetadataStreamHeaderSize(streamName);
builder.WriteInt32(offsetFromStartOfMetadata);
builder.WriteInt32(alignedStreamSize);
foreach (char ch in streamName)
{
builder.WriteByte((byte)ch);
}
builder.WriteBytes(streamName);

// After offset, size, and stream name, write 0-bytes until we reach our padded size.
for (uint i = 8 + (uint)streamName.Length; i < sizeOfStreamHeader; i++)
{
builder.WriteByte(0);
}
builder.WriteBytes(0, sizeOfStreamHeader - (8 + streamName.Length));

offsetFromStartOfMetadata += alignedStreamSize;
}
Expand Down
Loading
Loading