Skip to content
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;

namespace System.Reflection.Metadata
Expand All @@ -12,6 +13,7 @@ internal static class BlobWriterImpl
internal const int MaxCompressedIntegerValue = 0x1fffffff;
internal const int MinSignedCompressedIntegerValue = unchecked((int)0xF0000000);
internal const int MaxSignedCompressedIntegerValue = 0x0FFFFFFF;
internal const int MaxScalarConstantSize = sizeof(ulong);

internal static int GetCompressedIntegerSize(int value)
{
Expand Down Expand Up @@ -144,6 +146,109 @@ internal static void WriteCompressedSignedInteger(BlobBuilder writer, int value)
}
}

/// <summary>
/// Writes a scalar (non-string) constant to a span.
/// </summary>
/// <param name="bytes">The span where the content will be encoded.</param>
/// <param name="value">The constant value.</param>
Comment on lines +152 to +153
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation should clarify that bytes must be at least sizeof(ulong) bytes in length to accommodate the largest scalar type. Additionally, it should document the expected behavior when value is null (returns 4 bytes for a zero uint).

Suggested change
/// <param name="bytes">The span where the content will be encoded.</param>
/// <param name="value">The constant value.</param>
/// <param name="bytes">
/// The span where the content will be encoded. Must be at least <c>sizeof(ulong)</c> bytes in length to accommodate the largest scalar type.
/// </param>
/// <param name="value">
/// The constant value. If <paramref name="value"/> is <c>null</c>, writes 4 bytes representing a zero <c>uint</c>.
/// </param>

Copilot uses AI. Check for mistakes.
/// <returns>The number of bytes that was written.</returns>
internal static int WriteScalarConstant(Span<byte> bytes, object? value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing two copies of this method below are called WriteConstant. Use the same name for all 3 for consistency?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing two methods also accept strings besides scalar values.

{
if (value == null)
{
// The encoding of Type for the nullref value for FieldInit is ELEMENT_TYPE_CLASS with a Value of a 32-bit.
BinaryPrimitives.WriteUInt32LittleEndian(bytes, 0);
return sizeof(uint);
}
Comment on lines +155 to +162

var type = value.GetType();
if (type.IsEnum)
{
type = Enum.GetUnderlyingType(type);
}

if (type == typeof(bool))
Comment on lines +149 to +170
{
bytes[0] = (byte)((bool)value ? 1 : 0);
return sizeof(bool);
}
else if (type == typeof(int))
{
BinaryPrimitives.WriteInt32LittleEndian(bytes, (int)value);
return sizeof(int);
}
else if (type == typeof(byte))
{
bytes[0] = (byte)value;
return sizeof(byte);
}
else if (type == typeof(char))
{
BinaryPrimitives.WriteUInt16LittleEndian(bytes, (char)value);
return sizeof(char);
}
else if (type == typeof(double))
{
#if NET
BinaryPrimitives.WriteDoubleLittleEndian(bytes, (double)value);
#else
double v = (double)value;
unsafe
{
BinaryPrimitives.WriteUInt64LittleEndian(bytes, *(ulong*)(&v));
}
#endif
return sizeof(double);
}
else if (type == typeof(short))
{
BinaryPrimitives.WriteInt16LittleEndian(bytes, (short)value);
return sizeof(short);
}
else if (type == typeof(long))
{
BinaryPrimitives.WriteInt64LittleEndian(bytes, (long)value);
return sizeof(long);
}
else if (type == typeof(sbyte))
{
bytes[0] = (byte)(sbyte)value;
return sizeof(sbyte);
}
else if (type == typeof(float))
{
#if NET
BinaryPrimitives.WriteSingleLittleEndian(bytes, (float)value);
#else
float v = (float)value;
unsafe
{
BinaryPrimitives.WriteUInt32LittleEndian(bytes, *(uint*)(&v));
}
#endif
return sizeof(float);
}
else if (type == typeof(ushort))
{
BinaryPrimitives.WriteUInt16LittleEndian(bytes, (ushort)value);
return sizeof(ushort);
}
else if (type == typeof(uint))
{
BinaryPrimitives.WriteUInt32LittleEndian(bytes, (uint)value);
return sizeof(uint);
}
else if (type == typeof(ulong))
{
BinaryPrimitives.WriteUInt64LittleEndian(bytes, (ulong)value);
return sizeof(ulong);
}
else
{
throw new ArgumentException(SR.Format(SR.InvalidConstantValueOfType, type));
}
}

internal static void WriteConstant(ref BlobWriter writer, object? value)
{
if (value == null)
Expand All @@ -154,7 +259,7 @@ internal static void WriteConstant(ref BlobWriter writer, object? value)
}

var type = value.GetType();
if (type.GetTypeInfo().IsEnum)
if (type.IsEnum)
{
type = Enum.GetUnderlyingType(type);
}
Expand Down Expand Up @@ -227,7 +332,7 @@ internal static void WriteConstant(BlobBuilder writer, object? value)
}

var type = value.GetType();
if (type.GetTypeInfo().IsEnum)
if (type.IsEnum)
{
type = Enum.GetUnderlyingType(type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,9 @@ public BlobHandle GetOrAddConstantBlob(object? value)
return GetOrAddBlobUTF16(str);
}

var builder = PooledBlobBuilder.GetInstance();
builder.WriteConstant(value);
var result = GetOrAddBlob(builder);
builder.Free();
return result;
Span<byte> buffer = stackalloc byte[BlobWriterImpl.MaxScalarConstantSize];
int length = BlobWriterImpl.WriteScalarConstant(buffer, value);
return GetOrAddBlob(buffer.Slice(0, length));
}

/// <summary>
Expand Down
Loading