未验证 提交 71f8f57a 编写于 作者: A Andy Gocke 提交者: GitHub

Add Span and Memory conversion methods for ImmutableArray (#31785)

上级 1ad9a64d
......@@ -6,7 +6,9 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis
......@@ -497,6 +499,20 @@ public static int Count<T>(this ImmutableArray<T> items, Func<T, bool> predicate
return count;
}
[StructLayout(LayoutKind.Sequential)]
private struct ImmutableArrayProxy<T>
{
internal T[] MutableArray;
}
// TODO(https://github.com/dotnet/corefx/issues/34126): Remove when System.Collections.Immutable
// provides a Span API
internal static T[] DangerousGetUnderlyingArray<T>(this ImmutableArray<T> array)
=> Unsafe.As<ImmutableArray<T>, ImmutableArrayProxy<T>>(ref array).MutableArray;
internal static ReadOnlySpan<T> AsSpan<T>(this ImmutableArray<T> array)
=> array.DangerousGetUnderlyingArray();
internal static Dictionary<K, ImmutableArray<T>> ToDictionary<K, T>(this ImmutableArray<T> items, Func<T, K> keySelector, IEqualityComparer<K> comparer = null)
{
if (items.Length == 1)
......
......@@ -17,10 +17,6 @@ internal interface ISymUnmanagedAsyncMethodPropertiesWriter
void DefineKickoffMethod(int kickoffMethod);
void DefineCatchHandlerILOffset(int catchHandlerOffset);
void DefineAsyncStepInfo(
int count,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] yieldOffsets,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] breakpointOffset,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] breakpointMethod);
unsafe void DefineAsyncStepInfo(int count, int* yieldOffsets, int* breakpointOffset, int* breakpointMethod);
}
}
......@@ -9,9 +9,9 @@
namespace Microsoft.DiaSymReader
{
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("B01FAFEB-C450-3A4D-BEEC-B4CEEC01E006"), SuppressUnmanagedCodeSecurity]
internal interface ISymUnmanagedDocumentWriter
internal unsafe interface ISymUnmanagedDocumentWriter
{
void SetSource(uint sourceSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] byte[] source);
void SetCheckSum(Guid algorithmId, uint checkSumSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] checkSum);
void SetSource(uint sourceSize, byte* source);
void SetCheckSum(Guid algorithmId, uint checkSumSize, byte* checkSum);
}
}
......@@ -44,7 +44,7 @@ internal abstract class SymUnmanagedWriter : IDisposable
/// <exception cref="ObjectDisposedException">Object has been disposed.</exception>
/// <exception cref="InvalidOperationException">Writes are not allowed to the underlying stream.</exception>
/// <exception cref="SymUnmanagedWriterException">Error occurred while writing PDB data.</exception>
public abstract int DefineDocument(string name, Guid language, Guid vendor, Guid type, Guid algorithmId, byte[] checksum, byte[] source);
public abstract int DefineDocument(string name, Guid language, Guid vendor, Guid type, Guid algorithmId, ReadOnlySpan<byte> checksum, ReadOnlySpan<byte> source);
/// <summary>
/// Defines sequence points.
......@@ -123,7 +123,12 @@ internal abstract class SymUnmanagedWriter : IDisposable
/// <exception cref="SymUnmanagedWriterException">Error occurred while writing PDB data.</exception>
/// <exception cref="ArgumentNullException"><paramref name="yieldOffsets"/> or <paramref name="resumeOffsets"/> is null</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="yieldOffsets"/> or <paramref name="resumeOffsets"/> differ in length.</exception>
public abstract void SetAsyncInfo(int moveNextMethodToken, int kickoffMethodToken, int catchHandlerOffset, int[] yieldOffsets, int[] resumeOffsets);
public abstract void SetAsyncInfo(
int moveNextMethodToken,
int kickoffMethodToken,
int catchHandlerOffset,
ReadOnlySpan<int> yieldOffsets,
ReadOnlySpan<int> resumeOffsets);
/// <summary>
/// Associates custom debug information blob with the current method.
......
......@@ -137,7 +137,7 @@ public override int DocumentTableCapacity
}
}
public override int DefineDocument(string name, Guid language, Guid vendor, Guid type, Guid algorithmId, byte[] checksum, byte[] source)
public override int DefineDocument(string name, Guid language, Guid vendor, Guid type, Guid algorithmId, ReadOnlySpan<byte> checksum, ReadOnlySpan<byte> source)
{
if (name == null)
{
......@@ -164,7 +164,13 @@ public override int DefineDocument(string name, Guid language, Guid vendor, Guid
{
try
{
documentWriter.SetCheckSum(algorithmId, (uint)checksum.Length, checksum);
unsafe
{
fixed (byte* bytes = checksum)
{
documentWriter.SetCheckSum(algorithmId, (uint)checksum.Length, bytes);
}
}
}
catch (Exception ex)
{
......@@ -176,7 +182,13 @@ public override int DefineDocument(string name, Guid language, Guid vendor, Guid
{
try
{
documentWriter.SetSource((uint)source.Length, source);
unsafe
{
fixed (byte* bytes = source)
{
documentWriter.SetSource((uint)source.Length, bytes);
}
}
}
catch (Exception ex)
{
......@@ -456,8 +468,8 @@ public override void UsingNamespace(string importString)
int moveNextMethodToken,
int kickoffMethodToken,
int catchHandlerOffset,
int[] yieldOffsets,
int[] resumeOffsets)
ReadOnlySpan<int> yieldOffsets,
ReadOnlySpan<int> resumeOffsets)
{
if (yieldOffsets == null) throw new ArgumentNullException(nameof(yieldOffsets));
if (resumeOffsets == null) throw new ArgumentNullException(nameof(resumeOffsets));
......@@ -481,7 +493,15 @@ public override void UsingNamespace(string importString)
try
{
asyncMethodPropertyWriter.DefineAsyncStepInfo(count, yieldOffsets, resumeOffsets, methods);
unsafe
{
fixed (int* yieldPtr = yieldOffsets)
fixed (int* resumePtr = resumeOffsets)
fixed (int* methodsPtr = methods)
{
asyncMethodPropertyWriter.DefineAsyncStepInfo(count, yieldPtr, resumePtr, methodsPtr);
}
}
}
catch (Exception ex)
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
namespace Roslyn.Utilities
{
internal static class ImmutableByteArrayInterop
{
internal static byte[] DangerousGetUnderlyingArray(this ImmutableArray<byte> array)
{
var union = new ArrayUnion();
union.ImmutableArray = array;
return union.MutableArray;
}
internal static ImmutableArray<byte> DangerousCreateFromUnderlyingArray(ref byte[] array)
{
var union = new ArrayUnion();
union.MutableArray = array;
array = null;
return union.ImmutableArray;
}
[StructLayout(LayoutKind.Explicit)]
private struct ArrayUnion
{
[FieldOffset(0)]
internal byte[] MutableArray;
[FieldOffset(0)]
internal ImmutableArray<byte> ImmutableArray;
}
}
internal static class ImmutableInt32ArrayInterop
{
internal static int[] DangerousGetUnderlyingArray(this ImmutableArray<int> array)
{
var union = new ArrayUnion();
union.ImmutableArray = array;
return union.MutableArray;
}
internal static ImmutableArray<int> DangerousCreateFromUnderlyingArray(ref int[] array)
{
var union = new ArrayUnion();
union.MutableArray = array;
array = null;
return union.ImmutableArray;
}
[StructLayout(LayoutKind.Explicit)]
private struct ArrayUnion
{
[FieldOffset(0)]
internal int[] MutableArray;
[FieldOffset(0)]
internal ImmutableArray<int> ImmutableArray;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Roslyn.Utilities;
using static System.Buffers.Binary.BinaryPrimitives;
namespace Microsoft.CodeAnalysis
{
/// <summary>
/// A Span-compatible version of <see cref="System.IO.BinaryReader"/>.
/// </summary>
internal ref struct LittleEndianReader
{
private ReadOnlySpan<byte> _span;
public LittleEndianReader(ReadOnlySpan<byte> span)
{
_span = span;
}
internal uint ReadUInt32()
{
var result = ReadUInt32LittleEndian(_span);
_span = _span.Slice(sizeof(uint));
return result;
}
internal byte ReadByte()
{
var result = _span[0];
_span = _span.Slice(sizeof(byte));
return result;
}
internal ushort ReadUInt16()
{
var result = ReadUInt16LittleEndian(_span);
_span = _span.Slice(sizeof(ushort));
return result;
}
internal ReadOnlySpan<byte> ReadBytes(int byteCount)
{
var result = _span.Slice(0, byteCount);
_span = _span.Slice(byteCount);
return result;
}
internal int ReadInt32()
{
var result = ReadInt32LittleEndian(_span);
_span = _span.Slice(sizeof(int));
return result;
}
internal byte[] ReadReversed(int byteCount)
{
var result = ReadBytes(byteCount).ToArray();
result.ReverseContents();
return result;
}
}
}
......@@ -1131,7 +1131,7 @@ internal unsafe TypeSymbol DecodeLocalVariableTypeOrThrow(ImmutableArray<byte> s
throw new UnsupportedSignatureContent();
}
fixed (byte* ptr = ImmutableByteArrayInterop.DangerousGetUnderlyingArray(signature))
fixed (byte* ptr = signature.AsSpan())
{
var blobReader = new BlobReader(ptr, signature.Length);
var info = DecodeLocalVariableOrThrow(ref blobReader);
......
......@@ -60,6 +60,7 @@
<PackageReference Include="System.Collections.Immutable" Version="$(SystemCollectionsImmutableVersion)" />
<PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" />
<PackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataVersion)" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafeVersion)" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="$(SystemThreadingTasksExtensionsVersion)" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="$(SystemTextEncodingCodePagesVersion)" />
</ItemGroup>
......
......@@ -117,8 +117,8 @@ public void SerializeDebugInfo(IMethodBody methodBody, StandaloneSignatureHandle
methodToken,
MetadataTokens.GetToken(_metadataWriter.GetMethodHandle(asyncMoveNextInfo.KickoffMethod)),
asyncMoveNextInfo.CatchHandlerOffset,
ImmutableInt32ArrayInterop.DangerousGetUnderlyingArray(asyncMoveNextInfo.YieldOffsets),
ImmutableInt32ArrayInterop.DangerousGetUnderlyingArray(asyncMoveNextInfo.ResumeOffsets));
asyncMoveNextInfo.YieldOffsets.AsSpan(),
asyncMoveNextInfo.ResumeOffsets.AsSpan());
}
var compilationOptions = Context.Module.CommonCompilation.Options;
......@@ -573,14 +573,14 @@ private int GetDocumentIndex(DebugSourceDocument document)
}
Guid algorithmId;
byte[] checksum;
byte[] embeddedSource;
ReadOnlySpan<byte> checksum;
ReadOnlySpan<byte> embeddedSource;
DebugSourceInfo info = document.GetSourceInfo();
if (!info.Checksum.IsDefault)
{
algorithmId = info.ChecksumAlgorithmId;
checksum = ImmutableByteArrayInterop.DangerousGetUnderlyingArray(info.Checksum);
checksum = info.Checksum.AsSpan();
}
else
{
......@@ -590,7 +590,7 @@ private int GetDocumentIndex(DebugSourceDocument document)
if (!info.EmbeddedTextBlob.IsDefault)
{
embeddedSource = ImmutableByteArrayInterop.DangerousGetUnderlyingArray(info.EmbeddedTextBlob);
embeddedSource = info.EmbeddedTextBlob.AsSpan();
}
else
{
......
......@@ -91,7 +91,7 @@ internal static bool IsValidPublicKey(ImmutableArray<byte> blob)
return false;
}
BinaryReader blobReader = new BinaryReader(new MemoryStream(blob.DangerousGetUnderlyingArray()));
var blobReader = new LittleEndianReader(blob.AsSpan());
// Signature algorithm ID
var sigAlgId = blobReader.ReadUInt32();
......@@ -153,7 +153,7 @@ internal static bool IsValidPublicKey(ImmutableArray<byte> blob)
uint magic,
uint bitLen,
uint pubExp,
byte[] pubKeyData)
ReadOnlySpan<byte> pubKeyData)
{
var w = new BlobWriter(3 * sizeof(uint) + s_offsetToKeyData + pubKeyData.Length);
w.WriteUInt32(AlgorithmId.RsaSign);
......@@ -171,7 +171,13 @@ internal static bool IsValidPublicKey(ImmutableArray<byte> blob)
// re-add padding for exponent
w.WriteUInt32(pubExp);
w.WriteBytes(pubKeyData);
unsafe
{
fixed (byte* bytes = pubKeyData)
{
w.WriteBytes(bytes, pubKeyData.Length);
}
}
return w.ToImmutableArray();
}
......@@ -187,8 +193,6 @@ public static bool TryParseKey(ImmutableArray<byte> blob, out ImmutableArray<byt
privateKey = null;
snKey = default(ImmutableArray<byte>);
var asArray = blob.DangerousGetUnderlyingArray();
if (IsValidPublicKey(blob))
{
snKey = blob;
......@@ -202,41 +206,39 @@ public static bool TryParseKey(ImmutableArray<byte> blob, out ImmutableArray<byt
try
{
using (MemoryStream memStream = new MemoryStream(asArray))
using (BinaryReader br = new BinaryReader(new MemoryStream(asArray)))
var br = new LittleEndianReader(blob.AsSpan());
byte bType = br.ReadByte(); // BLOBHEADER.bType: Expected to be 0x6 (PUBLICKEYBLOB) or 0x7 (PRIVATEKEYBLOB), though there's no check for backward compat reasons.
byte bVersion = br.ReadByte(); // BLOBHEADER.bVersion: Expected to be 0x2, though there's no check for backward compat reasons.
br.ReadUInt16(); // BLOBHEADER.wReserved
uint algId = br.ReadUInt32(); // BLOBHEADER.aiKeyAlg
uint magic = br.ReadUInt32(); // RSAPubKey.magic: Expected to be 0x31415352 ('RSA1') or 0x32415352 ('RSA2')
var bitLen = br.ReadUInt32(); // Bit Length for Modulus
var pubExp = br.ReadUInt32(); // Exponent
var modulusLength = (int)(bitLen / 8);
if (blob.Length - s_offsetToKeyData < modulusLength)
{
byte bType = br.ReadByte(); // BLOBHEADER.bType: Expected to be 0x6 (PUBLICKEYBLOB) or 0x7 (PRIVATEKEYBLOB), though there's no check for backward compat reasons.
byte bVersion = br.ReadByte(); // BLOBHEADER.bVersion: Expected to be 0x2, though there's no check for backward compat reasons.
br.ReadUInt16(); // BLOBHEADER.wReserved
uint algId = br.ReadUInt32(); // BLOBHEADER.aiKeyAlg
uint magic = br.ReadUInt32(); // RSAPubKey.magic: Expected to be 0x31415352 ('RSA1') or 0x32415352 ('RSA2')
var bitLen = br.ReadUInt32(); // Bit Length for Modulus
var pubExp = br.ReadUInt32(); // Exponent
var modulusLength = (int)(bitLen / 8);
if (blob.Length - s_offsetToKeyData < modulusLength)
{
return false;
}
return false;
}
var modulus = br.ReadBytes(modulusLength);
var modulus = br.ReadBytes(modulusLength);
if (!(bType == PrivateKeyBlobId && magic == RSA2) && !(bType == PublicKeyBlobId && magic == RSA1))
{
return false;
}
if (!(bType == PrivateKeyBlobId && magic == RSA2) && !(bType == PublicKeyBlobId && magic == RSA1))
{
return false;
}
if (bType == PrivateKeyBlobId)
{
privateKey = ToRSAParameters(asArray, true);
// For snKey, rewrite some of the the parameters
algId = AlgorithmId.RsaSign;
magic = RSA1;
}
snKey = CreateSnPublicKeyBlob(PublicKeyBlobId, bVersion, algId, RSA1, bitLen, pubExp, modulus);
return true;
if (bType == PrivateKeyBlobId)
{
privateKey = ToRSAParameters(blob.AsSpan(), true);
// For snKey, rewrite some of the the parameters
algId = AlgorithmId.RsaSign;
magic = RSA1;
}
snKey = CreateSnPublicKeyBlob(PublicKeyBlobId, bVersion, algId, RSA1, bitLen, pubExp, modulus);
return true;
}
catch (Exception)
{
......@@ -248,9 +250,9 @@ public static bool TryParseKey(ImmutableArray<byte> blob, out ImmutableArray<byt
/// Helper for RsaCryptoServiceProvider.ExportParameters()
/// Copied from https://github.com/dotnet/corefx/blob/5fe5f9aae7b2987adc7082f90712b265bee5eefc/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/CapiHelper.Shared.cs
/// </summary>
internal static RSAParameters ToRSAParameters(this byte[] cspBlob, bool includePrivateParameters)
internal static RSAParameters ToRSAParameters(this ReadOnlySpan<byte> cspBlob, bool includePrivateParameters)
{
BinaryReader br = new BinaryReader(new MemoryStream(cspBlob));
var br = new LittleEndianReader(cspBlob);
byte bType = br.ReadByte(); // BLOBHEADER.bType: Expected to be 0x6 (PUBLICKEYBLOB) or 0x7 (PRIVATEKEYBLOB), though there's no check for backward compat reasons.
byte bVersion = br.ReadByte(); // BLOBHEADER.bVersion: Expected to be 0x2, though there's no check for backward compat reasons.
......
......@@ -28,7 +28,7 @@ public override int DocumentTableCapacity
public override void CloseTokensToSourceSpansMap() => _target.CloseTokensToSourceSpansMap();
public override void DefineCustomMetadata(byte[] metadata) => _target.DefineCustomMetadata(metadata);
public override int DefineDocument(string name, Guid language, Guid vendor, Guid type, Guid algorithmId, byte[] checksum, byte[] source)
public override int DefineDocument(string name, Guid language, Guid vendor, Guid type, Guid algorithmId, ReadOnlySpan<byte> checksum, ReadOnlySpan<byte> source)
=> _target.DefineDocument(name, language, vendor, type, algorithmId, checksum, source);
public override bool DefineLocalConstant(string name, object value, int constantSignatureToken)
......@@ -58,7 +58,7 @@ public override void OpenScope(int startOffset)
public override void OpenTokensToSourceSpansMap()
=> _target.OpenTokensToSourceSpansMap();
public override void SetAsyncInfo(int moveNextMethodToken, int kickoffMethodToken, int catchHandlerOffset, int[] yieldOffsets, int[] resumeOffsets)
public override void SetAsyncInfo(int moveNextMethodToken, int kickoffMethodToken, int catchHandlerOffset, ReadOnlySpan<int> yieldOffsets, ReadOnlySpan<int> resumeOffsets)
=> _target.SetAsyncInfo(moveNextMethodToken, kickoffMethodToken, catchHandlerOffset, yieldOffsets, resumeOffsets);
public override void SetEntryPoint(int entryMethodToken)
......
......@@ -38,7 +38,7 @@ public override void DefineCustomMetadata(byte[] metadata)
throw MakeException();
}
public override int DefineDocument(string name, Guid language, Guid vendor, Guid type, Guid algorithmId, byte[] checksum, byte[] source)
public override int DefineDocument(string name, Guid language, Guid vendor, Guid type, Guid algorithmId, ReadOnlySpan<byte> checksum, ReadOnlySpan<byte> source)
{
throw MakeException();
}
......@@ -88,7 +88,7 @@ public override void OpenTokensToSourceSpansMap()
throw MakeException();
}
public override void SetAsyncInfo(int moveNextMethodToken, int kickoffMethodToken, int catchHandlerOffset, int[] yieldOffsets, int[] resumeOffsets)
public override void SetAsyncInfo(int moveNextMethodToken, int kickoffMethodToken, int catchHandlerOffset, ReadOnlySpan<int> yieldOffsets, ReadOnlySpan<int> resumeOffsets)
{
throw MakeException();
}
......
......@@ -3,6 +3,7 @@
using System;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using Roslyn.Utilities;
namespace Roslyn.Test.Utilities
......
......@@ -4,6 +4,7 @@
using System.Collections.Immutable;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using Roslyn.Utilities;
namespace Roslyn.Test.Utilities
......
......@@ -90,7 +90,7 @@ public static bool IsStreamFullSigned(Stream moduleContents)
// RSA parameters start after the public key offset
byte[] publicKeyParams = new byte[publicKeyBlob.Length - CryptoBlobParser.s_publicKeyHeaderSize];
publicKeyBlob.CopyTo(CryptoBlobParser.s_publicKeyHeaderSize, publicKeyParams, 0, publicKeyParams.Length);
var snKey = publicKeyParams.ToRSAParameters(includePrivateParameters: false);
var snKey = CryptoBlobParser.ToRSAParameters(publicKeyParams.AsSpan(), includePrivateParameters: false);
using (var rsa = RSA.Create())
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册