< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 272
Coverable lines: 272
Total lines: 537
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 50
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobWriter.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.Immutable;
 5using System.Diagnostics;
 6using System.IO;
 7using System.Reflection.Internal;
 8using System.Runtime.InteropServices;
 9using System.Text;
 10
 11namespace System.Reflection.Metadata
 12{
 13    // TODO: argument checking
 14    public struct BlobWriter
 15    {
 16        // writable slice:
 17        private readonly byte[] _buffer;
 18        private readonly int _start;
 19        private readonly int _end;  // exclusive
 20
 21        // position in buffer relative to the beginning of the array:
 22        private int _position;
 23
 24        public BlobWriter(int size)
 025            : this(new byte[size])
 026        {
 027        }
 28
 29        public BlobWriter(byte[] buffer)
 030            : this(buffer, 0, buffer.Length)
 031        {
 032        }
 33
 34        public BlobWriter(Blob blob)
 035            : this(blob.Buffer, blob.Start, blob.Length)
 036        {
 037        }
 38
 39        public BlobWriter(byte[] buffer, int start, int count)
 040        {
 041            Debug.Assert(buffer != null);
 042            Debug.Assert(count >= 0);
 043            Debug.Assert(count <= buffer.Length - start);
 44
 045            _buffer = buffer;
 046            _start = start;
 047            _position = start;
 048            _end = start + count;
 049        }
 50
 051        internal bool IsDefault => _buffer == null;
 52
 53        /// <summary>
 54        /// Compares the current content of this writer with another one.
 55        /// </summary>
 56        public bool ContentEquals(BlobWriter other)
 057        {
 058            return Length == other.Length && _buffer.AsSpan(_start, Length).SequenceEqual(other._buffer.AsSpan(other._st
 059        }
 60
 61        public int Offset
 62        {
 63            get
 064            {
 065                return _position - _start;
 066            }
 67            set
 068            {
 069                if (value < 0 || _start > _end - value)
 070                {
 071                    Throw.ValueArgumentOutOfRange();
 72                }
 73
 074                _position = _start + value;
 075            }
 76        }
 77
 078        public int Length => _end - _start;
 079        public int RemainingBytes => _end - _position;
 080        public Blob Blob => new Blob(_buffer, _start, Length);
 81
 82        public byte[] ToArray()
 083        {
 084            return ToArray(0, Offset);
 085        }
 86
 87        /// <exception cref="ArgumentOutOfRangeException">Range specified by <paramref name="start"/> and <paramref name
 88        public byte[] ToArray(int start, int byteCount)
 089        {
 090            BlobUtilities.ValidateRange(Length, start, byteCount, nameof(byteCount));
 91
 092            return _buffer.AsSpan(_start + start, byteCount).ToArray();
 093        }
 94
 95        public ImmutableArray<byte> ToImmutableArray()
 096        {
 097            return ToImmutableArray(0, Offset);
 098        }
 99
 100        /// <exception cref="ArgumentOutOfRangeException">Range specified by <paramref name="start"/> and <paramref name
 101        public ImmutableArray<byte> ToImmutableArray(int start, int byteCount)
 0102        {
 0103            BlobUtilities.ValidateRange(Length, start, byteCount, nameof(byteCount));
 104
 0105            return ImmutableArray.Create(_buffer.AsSpan(_start + start, byteCount));
 0106        }
 107
 108        private int Advance(int value)
 0109        {
 0110            Debug.Assert(value >= 0);
 111
 0112            int position = _position;
 0113            if (position > _end - value)
 0114            {
 0115                Throw.OutOfBounds();
 116            }
 117
 0118            _position = position + value;
 0119            return position;
 0120        }
 121
 122        /// <exception cref="ArgumentOutOfRangeException"><paramref name="byteCount"/> is negative.</exception>
 123        public void WriteBytes(byte value, int byteCount)
 0124        {
 0125            if (byteCount < 0)
 0126            {
 0127                Throw.ArgumentOutOfRange(nameof(byteCount));
 128            }
 129
 0130            int start = Advance(byteCount);
 0131            _buffer.AsSpan(start, byteCount).Fill(value);
 0132        }
 133
 134        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
 135        /// <exception cref="ArgumentOutOfRangeException"><paramref name="byteCount"/> is negative.</exception>
 136        public unsafe void WriteBytes(byte* buffer, int byteCount)
 0137        {
 0138            if (buffer is null)
 0139            {
 0140                Throw.ArgumentNull(nameof(buffer));
 141            }
 142
 0143            if (byteCount < 0)
 0144            {
 0145                Throw.ArgumentOutOfRange(nameof(byteCount));
 146            }
 147
 0148            WriteBytes(new ReadOnlySpan<byte>(buffer, byteCount));
 0149        }
 150
 151        internal void WriteBytes(ReadOnlySpan<byte> buffer)
 0152        {
 0153            int start = Advance(buffer.Length);
 0154            buffer.CopyTo(_buffer.AsSpan(start));
 0155        }
 156
 157        /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
 158        public void WriteBytes(BlobBuilder source)
 0159        {
 0160            if (source is null)
 0161            {
 0162                Throw.ArgumentNull(nameof(source));
 163            }
 164
 0165            source.WriteContentTo(ref this);
 0166        }
 167
 168        /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
 169        /// <exception cref="ArgumentOutOfRangeException"><paramref name="byteCount"/> is negative.</exception>
 170        public int WriteBytes(Stream source, int byteCount)
 0171        {
 0172            if (source is null)
 0173            {
 0174                Throw.ArgumentNull(nameof(source));
 175            }
 176
 0177            if (byteCount < 0)
 0178            {
 0179                Throw.ArgumentOutOfRange(nameof(byteCount));
 180            }
 181
 0182            int start = Advance(byteCount);
 0183            int bytesRead = source.TryReadAll(_buffer, start, byteCount);
 0184            _position = start + bytesRead;
 0185            return bytesRead;
 0186        }
 187
 188        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
 189        public void WriteBytes(ImmutableArray<byte> buffer)
 0190        {
 0191            if (buffer.IsDefault)
 0192            {
 0193                Throw.ArgumentNull(nameof(buffer));
 194            }
 195
 0196            WriteBytes(buffer.AsSpan());
 0197        }
 198
 199        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
 200        /// <exception cref="ArgumentOutOfRangeException">Range specified by <paramref name="start"/> and <paramref name
 201        public void WriteBytes(ImmutableArray<byte> buffer, int start, int byteCount)
 0202        {
 0203            if (buffer.IsDefault)
 0204            {
 0205                Throw.ArgumentNull(nameof(buffer));
 206            }
 207
 0208            BlobUtilities.ValidateRange(buffer.Length, start, byteCount, nameof(byteCount));
 209
 0210            WriteBytes(buffer.AsSpan(start, byteCount));
 0211        }
 212
 213        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
 214        public void WriteBytes(byte[] buffer)
 0215        {
 0216            if (buffer is null)
 0217            {
 0218                Throw.ArgumentNull(nameof(buffer));
 219            }
 220
 0221            WriteBytes(buffer.AsSpan());
 0222        }
 223
 224        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
 225        /// <exception cref="ArgumentOutOfRangeException">Range specified by <paramref name="start"/> and <paramref name
 226        public void WriteBytes(byte[] buffer, int start, int byteCount)
 0227        {
 0228            if (buffer is null)
 0229            {
 0230                Throw.ArgumentNull(nameof(buffer));
 231            }
 232
 0233            BlobUtilities.ValidateRange(buffer.Length, start, byteCount, nameof(byteCount));
 234
 0235            WriteBytes(buffer.AsSpan(start, byteCount));
 0236        }
 237
 238        public void PadTo(int offset)
 0239        {
 0240            WriteBytes(0, offset - Offset);
 0241        }
 242
 243        public void Align(int alignment)
 0244        {
 0245            int offset = Offset;
 0246            WriteBytes(0, BitArithmetic.Align(offset, alignment) - offset);
 0247        }
 248
 249        public void WriteBoolean(bool value)
 0250        {
 0251            WriteByte((byte)(value ? 1 : 0));
 0252        }
 253
 254        public void WriteByte(byte value)
 0255        {
 0256            int start = Advance(sizeof(byte));
 0257            _buffer[start] = value;
 0258        }
 259
 260        public void WriteSByte(sbyte value)
 0261        {
 0262            WriteByte(unchecked((byte)value));
 0263        }
 264
 265        public void WriteDouble(double value)
 0266        {
 0267            int start = Advance(sizeof(double));
 0268            _buffer.WriteDouble(start, value);
 0269        }
 270
 271        public void WriteSingle(float value)
 0272        {
 0273            int start = Advance(sizeof(float));
 0274            _buffer.WriteSingle(start, value);
 0275        }
 276
 277        public void WriteInt16(short value)
 0278        {
 0279            WriteUInt16(unchecked((ushort)value));
 0280        }
 281
 282        public void WriteUInt16(ushort value)
 0283        {
 0284            int start = Advance(sizeof(ushort));
 0285            _buffer.WriteUInt16(start, value);
 0286        }
 287
 288        public void WriteInt16BE(short value)
 0289        {
 0290            WriteUInt16BE(unchecked((ushort)value));
 0291        }
 292
 293        public void WriteUInt16BE(ushort value)
 0294        {
 0295            int start = Advance(sizeof(ushort));
 0296            _buffer.WriteUInt16BE(start, value);
 0297        }
 298
 299        public void WriteInt32BE(int value)
 0300        {
 0301            WriteUInt32BE(unchecked((uint)value));
 0302        }
 303
 304        public void WriteUInt32BE(uint value)
 0305        {
 0306            int start = Advance(sizeof(uint));
 0307            _buffer.WriteUInt32BE(start, value);
 0308        }
 309
 310        public void WriteInt32(int value)
 0311        {
 0312            WriteUInt32(unchecked((uint)value));
 0313        }
 314
 315        public void WriteUInt32(uint value)
 0316        {
 0317            int start = Advance(sizeof(uint));
 0318            _buffer.WriteUInt32(start, value);
 0319        }
 320
 321        public void WriteInt64(long value)
 0322        {
 0323            WriteUInt64(unchecked((ulong)value));
 0324        }
 325
 326        public void WriteUInt64(ulong value)
 0327        {
 0328            int start = Advance(sizeof(ulong));
 0329            _buffer.WriteUInt64(start, value);
 0330        }
 331
 332        public void WriteDecimal(decimal value)
 0333        {
 0334            int start = Advance(BlobUtilities.SizeOfSerializedDecimal);
 0335            _buffer.WriteDecimal(start, value);
 0336        }
 337
 338        public void WriteGuid(Guid value)
 0339        {
 0340            int start = Advance(BlobUtilities.SizeOfGuid);
 0341            _buffer.WriteGuid(start, value);
 0342        }
 343
 344        public void WriteDateTime(DateTime value)
 0345        {
 0346            WriteInt64(value.Ticks);
 0347        }
 348
 349        /// <summary>
 350        /// Writes a reference to a heap (heap offset) or a table (row number).
 351        /// </summary>
 352        /// <param name="reference">Heap offset or table row number.</param>
 353        /// <param name="isSmall">True to encode the reference as 16-bit integer, false to encode as 32-bit integer.</pa
 354        public void WriteReference(int reference, bool isSmall)
 0355        {
 356            // This code is a very hot path, hence we don't check if the reference actually fits 2B.
 357
 0358            if (isSmall)
 0359            {
 0360                Debug.Assert(unchecked((ushort)reference) == reference);
 0361                WriteUInt16((ushort)reference);
 0362            }
 363            else
 0364            {
 0365                WriteInt32(reference);
 0366            }
 0367        }
 368
 369        /// <summary>
 370        /// Writes UTF-16 (little-endian) encoded string at the current position.
 371        /// </summary>
 372        /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
 373        public void WriteUTF16(char[] value)
 0374        {
 0375            if (value is null)
 0376            {
 0377                Throw.ArgumentNull(nameof(value));
 378            }
 379
 0380            WriteUTF16(value.AsSpan());
 0381        }
 382
 383        /// <summary>
 384        /// Writes UTF-16 (little-endian) encoded string at the current position.
 385        /// </summary>
 386        /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
 387        public void WriteUTF16(string value)
 0388        {
 0389            if (value is null)
 0390            {
 0391                Throw.ArgumentNull(nameof(value));
 392            }
 393
 0394            WriteUTF16(value.AsSpan());
 0395        }
 396
 397        private void WriteUTF16(ReadOnlySpan<char> value)
 0398        {
 0399            if (BitConverter.IsLittleEndian)
 0400            {
 0401                WriteBytes(MemoryMarshal.AsBytes(value));
 0402            }
 403            else
 0404            {
 0405                foreach (char c in value)
 0406                {
 0407                    WriteUInt16(c);
 0408                }
 0409            }
 0410        }
 411
 412        /// <summary>
 413        /// Writes string in SerString format (see ECMA-335-II 23.3 Custom attributes).
 414        /// </summary>
 415        /// <remarks>
 416        /// The string is UTF-8 encoded and prefixed by the its size in bytes.
 417        /// Null string is represented as a single byte 0xFF.
 418        /// </remarks>
 419        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 420        public void WriteSerializedString(string? str)
 0421        {
 0422            if (str == null)
 0423            {
 0424                WriteByte(0xff);
 0425                return;
 426            }
 427
 0428            WriteUTF8(str, 0, str.Length, allowUnpairedSurrogates: true, prependSize: true);
 0429        }
 430
 431        /// <summary>
 432        /// Writes string in User String (#US) heap format (see ECMA-335-II 24.2.4 #US and #Blob heaps):
 433        /// </summary>
 434        /// <remarks>
 435        /// The string is UTF-16 encoded and prefixed by the its size in bytes.
 436        ///
 437        /// This final byte holds the value 1 if and only if any UTF-16 character within the string has any bit set in i
 438        /// or its low byte is any of the following: 0x01-0x08, 0x0E-0x1F, 0x27, 0x2D, 0x7F. Otherwise, it holds 0.
 439        /// The 1 signifies Unicode characters that require handling beyond that normally provided for 8-bit encoding se
 440        /// </remarks>
 441        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 442        public void WriteUserString(string value)
 0443        {
 0444            if (value is null)
 0445            {
 0446                Throw.ArgumentNull(nameof(value));
 447            }
 448
 0449            WriteCompressedInteger(BlobUtilities.GetUserStringByteLength(value.Length));
 0450            WriteUTF16(value);
 0451            WriteByte(BlobUtilities.GetUserStringTrailingByte(value));
 0452        }
 453
 454        /// <summary>
 455        /// Writes UTF-8 encoded string at the current position.
 456        /// </summary>
 457        /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
 458        public void WriteUTF8(string value, bool allowUnpairedSurrogates)
 0459        {
 0460            if (value is null)
 0461            {
 0462                Throw.ArgumentNull(nameof(value));
 463            }
 464
 0465            WriteUTF8(value, 0, value.Length, allowUnpairedSurrogates, prependSize: false);
 0466        }
 467
 468        private unsafe void WriteUTF8(string str, int start, int length, bool allowUnpairedSurrogates, bool prependSize)
 0469        {
 0470            fixed (char* strPtr = str)
 0471            {
 0472                char* charPtr = strPtr + start;
 0473                int byteCount = Encoding.UTF8.GetByteCount(charPtr, length);
 474
 0475                if (prependSize)
 0476                {
 0477                    WriteCompressedInteger(byteCount);
 0478                }
 479
 0480                int startOffset = Advance(byteCount);
 0481                _buffer.WriteUTF8(startOffset, charPtr, length, byteCount, allowUnpairedSurrogates);
 0482            }
 0483        }
 484
 485        /// <summary>
 486        /// Implements compressed signed integer encoding as defined by ECMA-335-II chapter 23.2: Blobs and signatures.
 487        /// </summary>
 488        /// <remarks>
 489        /// If the value lies between -64 (0xFFFFFFC0) and 63 (0x3F), inclusive, encode as a one-byte integer:
 490        /// bit 7 clear, value bits 5 through 0 held in bits 6 through 1, sign bit (value bit 31) in bit 0.
 491        ///
 492        /// If the value lies between -8192 (0xFFFFE000) and 8191 (0x1FFF), inclusive, encode as a two-byte integer:
 493        /// 15 set, bit 14 clear, value bits 12 through 0 held in bits 13 through 1, sign bit(value bit 31) in bit 0.
 494        ///
 495        /// If the value lies between -268435456 (0xF000000) and 268435455 (0x0FFFFFFF), inclusive, encode as a four-byt
 496        /// 31 set, 30 set, bit 29 clear, value bits 27 through 0 held in bits 28 through 1, sign bit(value bit 31) in b
 497        /// </remarks>
 498        /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> can't be represented as a compressed 
 499        public void WriteCompressedSignedInteger(int value)
 0500        {
 0501            BlobWriterImpl.WriteCompressedSignedInteger(ref this, value);
 0502        }
 503
 504        /// <summary>
 505        /// Implements compressed unsigned integer encoding as defined by ECMA-335-II chapter 23.2: Blobs and signatures
 506        /// </summary>
 507        /// <remarks>
 508        /// If the value lies between 0 (0x00) and 127 (0x7F), inclusive,
 509        /// encode as a one-byte integer (bit 7 is clear, value held in bits 6 through 0).
 510        ///
 511        /// If the value lies between 28 (0x80) and 214 - 1 (0x3FFF), inclusive,
 512        /// encode as a 2-byte integer with bit 15 set, bit 14 clear(value held in bits 13 through 0).
 513        ///
 514        /// Otherwise, encode as a 4-byte integer, with bit 31 set, bit 30 set, bit 29 clear (value held in bits 28 thro
 515        /// </remarks>
 516        /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> can't be represented as a compressed 
 517        public void WriteCompressedInteger(int value)
 0518        {
 0519            BlobWriterImpl.WriteCompressedInteger(ref this, unchecked((uint)value));
 0520        }
 521
 522        /// <summary>
 523        /// Writes a constant value (see ECMA-335 Partition II section 22.9) at the current position.
 524        /// </summary>
 525        /// <exception cref="ArgumentException"><paramref name="value"/> is not of a constant type.</exception>
 526        public void WriteConstant(object? value)
 0527        {
 0528            BlobWriterImpl.WriteConstant(ref this, value);
 0529        }
 530
 531        public void Clear()
 0532        {
 0533            _position = _start;
 0534        }
 535    }
 536}
 537

Methods/Properties

.ctor(System.Int32)
.ctor(System.Byte[])
.ctor(System.Reflection.Metadata.Blob)
.ctor(System.Byte[],System.Int32,System.Int32)
IsDefault()
ContentEquals(System.Reflection.Metadata.BlobWriter)
Offset()
Offset(System.Int32)
Length()
RemainingBytes()
Blob()
ToArray()
ToArray(System.Int32,System.Int32)
ToImmutableArray()
ToImmutableArray(System.Int32,System.Int32)
Advance(System.Int32)
WriteBytes(System.Byte,System.Int32)
WriteBytes(System.Byte*,System.Int32)
WriteBytes(System.ReadOnlySpan`1<System.Byte>)
WriteBytes(System.Reflection.Metadata.BlobBuilder)
WriteBytes(System.IO.Stream,System.Int32)
WriteBytes(System.Collections.Immutable.ImmutableArray`1<System.Byte>)
WriteBytes(System.Collections.Immutable.ImmutableArray`1<System.Byte>,System.Int32,System.Int32)
WriteBytes(System.Byte[])
WriteBytes(System.Byte[],System.Int32,System.Int32)
PadTo(System.Int32)
Align(System.Int32)
WriteBoolean(System.Boolean)
WriteByte(System.Byte)
WriteSByte(System.SByte)
WriteDouble(System.Double)
WriteSingle(System.Single)
WriteInt16(System.Int16)
WriteUInt16(System.UInt16)
WriteInt16BE(System.Int16)
WriteUInt16BE(System.UInt16)
WriteInt32BE(System.Int32)
WriteUInt32BE(System.UInt32)
WriteInt32(System.Int32)
WriteUInt32(System.UInt32)
WriteInt64(System.Int64)
WriteUInt64(System.UInt64)
WriteDecimal(System.Decimal)
WriteGuid(System.Guid)
WriteDateTime(System.DateTime)
WriteReference(System.Int32,System.Boolean)
WriteUTF16(System.Char[])
WriteUTF16(System.String)
WriteUTF16(System.ReadOnlySpan`1<System.Char>)
WriteSerializedString(System.String)
WriteUserString(System.String)
WriteUTF8(System.String,System.Boolean)
WriteUTF8(System.String,System.Int32,System.Int32,System.Boolean,System.Boolean)
WriteCompressedSignedInteger(System.Int32)
WriteCompressedInteger(System.Int32)
WriteConstant(System.Object)
Clear()