Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Heaps and BlobContentId
Browse files Browse the repository at this point in the history
  • Loading branch information
tmat committed May 13, 2016
1 parent 1dc8999 commit a9442a6
Show file tree
Hide file tree
Showing 25 changed files with 1,069 additions and 359 deletions.
6 changes: 6 additions & 0 deletions src/System.Reflection.Metadata/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -411,4 +411,10 @@
<data name="UnknownSectionName" xml:space="preserve">
<value>Unknown section name: '{0}'.</value>
</data>
<data name="HashTooShort" xml:space="preserve">
<value>Hash must be at least {0}B long.</value>
</data>
<data name="ValueMustBeMultiple" xml:space="preserve">
<value>Value must be multiple of {0}.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
<Compile Include="System\Reflection\Metadata\Internal\MetadataWriterUtilities.cs" />
<Compile Include="System\Reflection\Metadata\MetadataStreamOptions.cs" />
<Compile Include="System\Reflection\Metadata\MetadataReaderProvider.cs" />
<Compile Include="System\Reflection\Metadata\ContentId.cs" />
<Compile Include="System\Reflection\Metadata\BlobContentId.cs" />
<Compile Include="System\Reflection\Metadata\Signatures\PrimitiveSerializationTypeCode.cs" />
<Compile Include="System\Reflection\Metadata\TypeSystem\Handles.TypeSystem.cs" />
<Compile Include="System\Reflection\PortableExecutable\ManagedPEBuilder.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,22 @@ public static void WriteDecimal(this byte[] buffer, int start, decimal value)
}
}

public const int SizeOfGuid = 16;

public static void WriteGuid(this byte[] buffer, int start, Guid value)
{
fixed (byte* ptr = &buffer[start])
{
int* dst = (int*)ptr;
int* src = (int*)&value;

dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
dst[3] = src[3];
}
}

// TODO: Use UTF8Encoding https://github.com/dotnet/corefx/issues/2217
public static void WriteUTF8(this byte[] buffer, int start, char* charPtr, int charCount, int byteCount, bool allowUnpairedSurrogates)
{
Expand Down Expand Up @@ -271,5 +287,60 @@ internal static void ValidateRange(int bufferLength, int start, int byteCount)
Throw.ArgumentOutOfRange(nameof(byteCount));
}
}

internal static int GetUserStringByteLength(int characterCount)
{
return characterCount * 2 + 1;
}

internal static byte GetUserStringTrailingByte(string str)
{
// Write out a trailing byte indicating if the string is really quite simple
foreach (char ch in str)
{
if (ch >= 0x7F)
{
return 1;
}

switch ((int)ch)
{
case 0x1:
case 0x2:
case 0x3:
case 0x4:
case 0x5:
case 0x6:
case 0x7:
case 0x8:
case 0xE:
case 0xF:
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x19:
case 0x1A:
case 0x1B:
case 0x1C:
case 0x1D:
case 0x1E:
case 0x1F:
case 0x27:
case 0x2D:
return 1;

default:
continue;
}
}

return 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public unsafe partial class BlobBuilder

internal const int DefaultChunkSize = 256;

// Must be at least the size of the largest primitive type we write atomically (decimal).
// Must be at least the size of the largest primitive type we write atomically (Guid).
internal const int MinChunkSize = 16;

// Builders are linked like so:
Expand Down Expand Up @@ -450,6 +450,8 @@ public void LinkSuffix(BlobBuilder suffix)
return;
}

bool isEmpty = Count == 0;

// swap buffers of the heads:
var suffixBuffer = suffix._buffer;
uint suffixLength = suffix._length;
Expand All @@ -467,32 +469,35 @@ public void LinkSuffix(BlobBuilder suffix)
// The value is not used, other than for calculating the value of Count property.
suffix._previousLengthOrFrozenSuffixLengthDelta = suffixPreviousLength + oldSuffixLength - suffix.Length;

// First and last chunks:
//
// [First]->[]->[Last] <- [this] [SuffixFirst]->[]->[SuffixLast] <- [suffix]
// ^___________| ^_________________|
//
// Degenerate cases:
// this == First == Last and/or suffix == SuffixFirst == SuffixLast.
var first = FirstChunk;
var suffixFirst = suffix.FirstChunk;
var last = _nextOrPrevious;
var suffixLast = suffix._nextOrPrevious;
if (!isEmpty)
{
// First and last chunks:
//
// [First]->[]->[Last] <- [this] [SuffixFirst]->[]->[SuffixLast] <- [suffix]
// ^___________| ^_________________|
//
// Degenerate cases:
// this == First == Last and/or suffix == SuffixFirst == SuffixLast.
var first = FirstChunk;
var suffixFirst = suffix.FirstChunk;
var last = _nextOrPrevious;
var suffixLast = suffix._nextOrPrevious;

// Relink like so:
// [First]->[]->[Last] -> [suffix] -> [SuffixFirst]->[]->[SuffixLast] <- [this]
// ^_______________________________________________________|
_nextOrPrevious = suffixLast;
suffix._nextOrPrevious = (suffixFirst != suffix) ? suffixFirst : (first != this) ? first : suffix;
// Relink like so:
// [First]->[]->[Last] -> [suffix] -> [SuffixFirst]->[]->[SuffixLast] <- [this]
// ^_______________________________________________________|
_nextOrPrevious = suffixLast;
suffix._nextOrPrevious = (suffixFirst != suffix) ? suffixFirst : (first != this) ? first : suffix;

if (last != this)
{
last._nextOrPrevious = suffix;
}
if (last != this)
{
last._nextOrPrevious = suffix;
}

if (suffixLast != suffix)
{
suffixLast._nextOrPrevious = (first != this) ? first : suffix;
if (suffixLast != suffix)
{
suffixLast._nextOrPrevious = (first != this) ? first : suffix;
}
}

CheckInvariants();
Expand Down Expand Up @@ -596,7 +601,9 @@ private int ReserveBytesImpl(int byteCount)

private int ReserveBytesPrimitive(int byteCount)
{
Debug.Assert(byteCount < MinChunkSize);
// If the primitive doesn't fit to the current chuck we'll allocate a new chunk that is at least MinChunkSize.
// That chunk has to fit the primitive otherwise we might keep allocating new chunks and never never end up with one that fits.
Debug.Assert(byteCount <= MinChunkSize);
return ReserveBytesImpl(byteCount);
}

Expand All @@ -606,7 +613,7 @@ public void WriteBytes(byte value, int byteCount)
{
if (byteCount < 0)
{
throw new ArgumentOutOfRangeException(nameof(byteCount));
Throw.ArgumentOutOfRange(nameof(byteCount));
}

if (!IsHead)
Expand Down Expand Up @@ -887,6 +894,13 @@ public void WriteDecimal(decimal value)
_buffer.WriteDecimal(start, value);
}

/// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</exception>
public void WriteGuid(Guid value)
{
int start = ReserveBytesPrimitive(BlobUtilities.SizeOfGuid);
_buffer.WriteGuid(start, value);
}

/// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</exception>
public void WriteDateTime(DateTime value)
{
Expand Down Expand Up @@ -966,10 +980,12 @@ public void WriteUTF16(string value)
}

/// <summary>
/// Writes string in SerString format (see ECMA-335-II 23.3 Custom attributes):
/// Writes string in SerString format (see ECMA-335-II 23.3 Custom attributes).
/// </summary>
/// <remarks>
/// The string is UTF8 encoded and prefixed by the its size in bytes.
/// Null string is represented as a single byte 0xFF.
/// </summary>
/// </remarks>
/// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</exception>
public void WriteSerializedString(string value)
{
Expand All @@ -982,12 +998,39 @@ public void WriteSerializedString(string value)
WriteUTF8(value, 0, value.Length, allowUnpairedSurrogates: true, prependSize: true);
}

/// <summary>
/// Writes string in User String (#US) heap format (see ECMA-335-II 24.2.4 #US and #Blob heaps):
/// </summary>
/// <remarks>
/// The string is UTF16 encoded and prefixed by the its size in bytes.
///
/// This final byte holds the value 1 if and only if any UTF16 character within the string has any bit set in its top byte,
/// or its low byte is any of the following: 0x01–0x08, 0x0E–0x1F, 0x27, 0x2D, 0x7F. Otherwise, it holds 0.
/// The 1 signifies Unicode characters that require handling beyond that normally provided for 8-bit encoding sets.
/// </remarks>
/// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</exception>
public void WriteUserString(string value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}

WriteCompressedInteger(BlobUtilities.GetUserStringByteLength(value.Length));
WriteUTF16(value);
WriteByte(BlobUtilities.GetUserStringTrailingByte(value));
}

/// <summary>
/// Writes UTF8 encoded string at the current position.
/// </summary>
/// <param name="value">Constant value.</param>
/// <param name="allowUnpairedSurrogates">
/// True to encode unpaired surrogates as specified, otherwise replace them with U+FFFD character.
/// </param>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
/// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</exception>
public void WriteUTF8(string value, bool allowUnpairedSurrogates)
public void WriteUTF8(string value, bool allowUnpairedSurrogates = true)
{
if (value == null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Reflection.Internal;

namespace System.Reflection.Metadata
{
public struct BlobContentId
{
public Guid Guid { get; }
public uint Stamp { get; }

public BlobContentId(Guid guid, uint stamp)
{
Guid = guid;
Stamp = stamp;
}

public bool IsDefault => Guid == default(Guid) && Stamp == 0;

public static BlobContentId FromHash(ImmutableArray<byte> hashCode)
{
return FromHash(ImmutableByteArrayInterop.DangerousGetUnderlyingArray(hashCode));
}

public unsafe static BlobContentId FromHash(byte[] hashCode)
{
const int minHashSize = 20;

if (hashCode == null)
{
throw new ArgumentNullException(nameof(hashCode));
}

if (hashCode.Length < minHashSize)
{
throw new ArgumentException(SR.Format(SR.HashTooShort, minHashSize), nameof(hashCode));
}

Guid guid = default(Guid);
byte* guidPtr = (byte*)&guid;
for (var i = 0; i < BlobUtilities.SizeOfGuid; i++)
{
guidPtr[i] = hashCode[i];
}

// modify the guid data so it decodes to the form of a "random" guid ala rfc4122
guidPtr[7] = (byte)((guidPtr[7] & 0x0f) | (4 << 4));
guidPtr[8] = (byte)((guidPtr[8] & 0x3f) | (2 << 6));

// compute a random-looking stamp from the remaining bits, but with the upper bit set
uint stamp = 0x80000000u | ((uint)hashCode[19] << 24 | (uint)hashCode[18] << 16 | (uint)hashCode[17] << 8 | hashCode[16]);

return new BlobContentId(guid, stamp);
}

public static Func<IEnumerable<Blob>, BlobContentId> GetTimeBasedProvider()
{
// In the PE File Header this is a "Time/Date Stamp" whose description is "Time and date
// the file was created in seconds since January 1st 1970 00:00:00 or 0"
// However, when we want to make it deterministic we fill it in (later) with bits from the hash of the full PE file.
uint timestamp = (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
return content => new BlobContentId(Guid.NewGuid(), timestamp);
}
}
}

0 comments on commit a9442a6

Please sign in to comment.