< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 677
Coverable lines: 677
Total lines: 1298
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 196
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: .ctor(...)0%220%
File 1: AllocateChunk(...)100%110%
File 1: FreeChunk()100%110%
File 1: Clear()0%880%
File 1: Free()100%110%
File 1: ClearChunk()100%110%
File 1: CheckInvariants()0%880%
File 1: GetChunks()0%220%
File 1: GetBlobs()0%220%
File 1: ContentEquals(...)0%20200%
File 1: ToArray()100%110%
File 1: ToArray(...)0%660%
File 1: ToImmutableArray()100%110%
File 1: ToImmutableArray(...)100%110%
File 1: TryGetSpan(...)0%220%
File 1: WriteContentTo(...)0%440%
File 1: WriteContentTo(...)0%440%
File 1: WriteContentTo(...)0%440%
File 1: LinkPrefix(...)0%20200%
File 1: LinkSuffix(...)0%22220%
File 1: AddLength(...)100%110%
File 1: Expand(...)0%880%
File 1: ReserveBytes(...)0%220%
File 1: ReserveBytesImpl(...)0%220%
File 1: ReserveBytesPrimitive(...)100%110%
File 1: WriteBytes(...)0%660%
File 1: WriteBytes(...)0%660%
File 1: WriteBytesUnchecked(...)0%220%
File 1: TryWriteBytes(...)0%12120%
File 1: WriteBytes(...)0%220%
File 1: WriteBytes(...)0%220%
File 1: WriteBytes(...)0%220%
File 1: WriteBytes(...)0%220%
File 1: WriteBytes(...)0%220%
File 1: PadTo(...)100%110%
File 1: Align(...)100%110%
File 1: WriteBoolean(...)0%220%
File 1: WriteByte(...)100%110%
File 1: WriteSByte(...)100%110%
File 1: WriteDouble(...)100%110%
File 1: WriteSingle(...)100%110%
File 1: WriteInt16(...)100%110%
File 1: WriteUInt16(...)100%110%
File 1: WriteInt16BE(...)100%110%
File 1: WriteUInt16BE(...)100%110%
File 1: WriteInt32BE(...)100%110%
File 1: WriteUInt32BE(...)100%110%
File 1: WriteInt32(...)100%110%
File 1: WriteUInt32(...)100%110%
File 1: WriteInt64(...)100%110%
File 1: WriteUInt64(...)100%110%
File 1: WriteDecimal(...)100%110%
File 1: WriteGuid(...)100%110%
File 1: WriteDateTime(...)100%110%
File 1: WriteReference(...)0%220%
File 1: WriteUTF16(...)0%440%
File 1: WriteUTF16(...)0%440%
File 1: WriteUTF16(...)0%660%
File 1: WriteSerializedString(...)0%220%
File 1: WriteUserString(...)0%220%
File 1: WriteUTF8(...)0%220%
File 1: WriteUTF8(...)0%10100%
File 1: WriteCompressedSignedInteger(...)100%110%
File 1: WriteCompressedInteger(...)100%110%
File 1: WriteConstant(...)100%110%
File 1: GetDebuggerDisplay()0%220%
File 1: Display(...)0%220%
File 1: ClearAndFreeChunk()100%110%
File 2: .ctor(...)100%110%
File 2: MoveNext()0%440%
File 2: Reset()100%110%
File 2: System.IDisposable.Dispose()100%110%
File 2: GetEnumerator()100%110%
File 2: System.Collections.Generic.IEnumerable<System.Reflection.Metadata.BlobBuilder>.GetEnumerator()100%110%
File 2: System.Collections.IEnumerable.GetEnumerator()100%110%
File 2: .ctor(...)100%110%
File 2: MoveNext()100%110%
File 2: Reset()100%110%
File 2: System.IDisposable.Dispose()100%110%
File 2: GetEnumerator()100%110%
File 2: System.Collections.Generic.IEnumerable<System.Reflection.Metadata.Blob>.GetEnumerator()100%110%
File 2: System.Collections.IEnumerable.GetEnumerator()100%110%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobBuilder.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.CompilerServices;
 9using System.Runtime.InteropServices;
 10
 11namespace System.Reflection.Metadata
 12{
 13    [DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
 14    public partial class BlobBuilder
 15    {
 16        // The implementation is akin to StringBuilder.
 17        // The differences:
 18        // - BlobBuilder allows efficient sequential write of the built content to a stream.
 19        // - BlobBuilder allows for chunk allocation customization. A custom allocator can use pooling strategy, for exa
 20
 21        internal const int DefaultChunkSize = 256;
 22
 23        // Must be at least the size of the largest primitive type we write atomically (Guid).
 24        internal const int MinChunkSize = 16;
 25
 26        // Builders are linked like so:
 27        //
 28        // [1:first]->[2]->[3:last]<-[4:head]
 29        //     ^_______________|
 30        //
 31        // In this case the content represented is a sequence (1,2,3,4).
 32        // This structure optimizes for append write operations and sequential enumeration from the start of the chain.
 33        // Data can only be written to the head node. Other nodes are "frozen".
 34        private BlobBuilder _nextOrPrevious;
 035        private BlobBuilder FirstChunk => _nextOrPrevious._nextOrPrevious;
 36
 37        // The sum of lengths of all preceding chunks (not including the current chunk),
 38        // or a difference between original buffer length of a builder that was linked as a suffix to another builder,
 39        // and the current length of the buffer (not that the buffers are swapped when suffix linking).
 40        private int _previousLengthOrFrozenSuffixLengthDelta;
 41
 42        private byte[] _buffer;
 43
 44        // The length of data in the buffer in lower 31 bits.
 45        // Head: highest bit is 0, length may be 0.
 46        // Non-head: highest bit is 1, lower 31 bits are not all 0.
 47        private uint _length;
 48
 49        private const uint IsFrozenMask = 0x80000000;
 050        internal bool IsHead => (_length & IsFrozenMask) == 0;
 051        private int Length => (int)(_length & ~IsFrozenMask);
 052        private uint FrozenLength => _length | IsFrozenMask;
 053        private Span<byte> Span => _buffer.AsSpan(0, Length);
 54
 055        public BlobBuilder(int capacity = DefaultChunkSize)
 056        {
 057            if (capacity < 0)
 058            {
 059                Throw.ArgumentOutOfRange(nameof(capacity));
 60            }
 61
 062            _nextOrPrevious = this;
 063            _buffer = new byte[Math.Max(MinChunkSize, capacity)];
 064        }
 65
 66        protected virtual BlobBuilder AllocateChunk(int minimalSize)
 067        {
 068            return new BlobBuilder(Math.Max(_buffer.Length, minimalSize));
 069        }
 70
 71        protected virtual void FreeChunk()
 072        {
 73            // nop
 074        }
 75
 76        public void Clear()
 077        {
 078            if (!IsHead)
 079            {
 080                Throw.InvalidOperationBuilderAlreadyLinked();
 81            }
 82
 83            // Swap buffer with the first chunk.
 84            // Note that we need to keep holding on all allocated buffers,
 85            // so that builders with custom allocator can release them.
 086            var first = FirstChunk;
 087            if (first != this)
 088            {
 089                var firstBuffer = first._buffer;
 090                first._length = FrozenLength;
 091                first._buffer = _buffer;
 092                _buffer = firstBuffer;
 093            }
 94
 95            // free all chunks except for the current one
 096            foreach (BlobBuilder chunk in GetChunks())
 097            {
 098                if (chunk != this)
 099                {
 0100                    chunk.ClearAndFreeChunk();
 0101                }
 0102            }
 103
 0104            ClearChunk();
 0105        }
 106
 107        protected void Free()
 0108        {
 0109            Clear();
 0110            FreeChunk();
 0111        }
 112
 113        // internal for testing
 114        internal void ClearChunk()
 0115        {
 0116            _length = 0;
 0117            _previousLengthOrFrozenSuffixLengthDelta = 0;
 0118            _nextOrPrevious = this;
 0119        }
 120
 121        [Conditional("DEBUG")]
 122        private void CheckInvariants()
 0123        {
 0124            Debug.Assert(_buffer != null);
 0125            Debug.Assert(Length >= 0 && Length <= _buffer.Length);
 0126            Debug.Assert(_nextOrPrevious != null);
 127
 0128            if (IsHead)
 0129            {
 0130                Debug.Assert(_previousLengthOrFrozenSuffixLengthDelta >= 0);
 131
 132                // last chunk:
 0133                int totalLength = 0;
 0134                foreach (var chunk in GetChunks())
 0135                {
 0136                    Debug.Assert(chunk.IsHead || chunk.Length > 0);
 0137                    totalLength += chunk.Length;
 0138                }
 139
 0140                Debug.Assert(totalLength == Count);
 0141            }
 0142        }
 143
 0144        public int Count => _previousLengthOrFrozenSuffixLengthDelta + Length;
 145
 146        private int PreviousLength
 147        {
 148            get
 0149            {
 0150                Debug.Assert(IsHead);
 0151                return _previousLengthOrFrozenSuffixLengthDelta;
 0152            }
 153            set
 0154            {
 0155                Debug.Assert(IsHead);
 0156                _previousLengthOrFrozenSuffixLengthDelta = value;
 0157            }
 158        }
 159
 0160        protected int FreeBytes => _buffer.Length - Length;
 161
 162        // internal for testing
 0163        protected internal int ChunkCapacity => _buffer.Length;
 164
 165        // internal for testing
 166        internal Chunks GetChunks()
 0167        {
 0168            if (!IsHead)
 0169            {
 0170                Throw.InvalidOperationBuilderAlreadyLinked();
 171            }
 172
 0173            return new Chunks(this);
 0174        }
 175
 176        /// <summary>
 177        /// Returns a sequence of all blobs that represent the content of the builder.
 178        /// </summary>
 179        /// <exception cref="InvalidOperationException">Content is not available, the builder has been linked with anoth
 180        public Blobs GetBlobs()
 0181        {
 0182            if (!IsHead)
 0183            {
 0184                Throw.InvalidOperationBuilderAlreadyLinked();
 185            }
 186
 0187            return new Blobs(this);
 0188        }
 189
 190        /// <summary>
 191        /// Compares the current content of this writer with another one.
 192        /// </summary>
 193        /// <exception cref="InvalidOperationException">Content is not available, the builder has been linked with anoth
 194        public bool ContentEquals(BlobBuilder other)
 0195        {
 0196            if (!IsHead)
 0197            {
 0198                Throw.InvalidOperationBuilderAlreadyLinked();
 199            }
 200
 0201            if (ReferenceEquals(this, other))
 0202            {
 0203                return true;
 204            }
 205
 0206            if (other == null)
 0207            {
 0208                return false;
 209            }
 210
 0211            if (!other.IsHead)
 0212            {
 0213                Throw.InvalidOperationBuilderAlreadyLinked();
 214            }
 215
 0216            if (Count != other.Count)
 0217            {
 0218                return false;
 219            }
 220
 0221            var leftEnumerator = GetChunks();
 0222            var rightEnumerator = other.GetChunks();
 0223            int leftStart = 0;
 0224            int rightStart = 0;
 225
 0226            bool leftContinues = leftEnumerator.MoveNext();
 0227            bool rightContinues = rightEnumerator.MoveNext();
 228
 0229            while (leftContinues && rightContinues)
 0230            {
 0231                Debug.Assert(leftStart == 0 || rightStart == 0);
 232
 0233                var left = leftEnumerator.Current;
 0234                var right = rightEnumerator.Current;
 235
 0236                int minLength = Math.Min(left.Length - leftStart, right.Length - rightStart);
 0237                if (!left._buffer.AsSpan(leftStart, minLength).SequenceEqual(right._buffer.AsSpan(rightStart, minLength)
 0238                {
 0239                    return false;
 240                }
 241
 0242                leftStart += minLength;
 0243                rightStart += minLength;
 244
 245                // nothing remains in left chunk to compare:
 0246                if (leftStart == left.Length)
 0247                {
 0248                    leftContinues = leftEnumerator.MoveNext();
 0249                    leftStart = 0;
 0250                }
 251
 252                // nothing remains in left chunk to compare:
 0253                if (rightStart == right.Length)
 0254                {
 0255                    rightContinues = rightEnumerator.MoveNext();
 0256                    rightStart = 0;
 0257                }
 0258            }
 259
 0260            return leftContinues == rightContinues;
 0261        }
 262
 263        /// <exception cref="InvalidOperationException">Content is not available, the builder has been linked with anoth
 264        public byte[] ToArray()
 0265        {
 0266            return ToArray(0, Count);
 0267        }
 268
 269        /// <exception cref="ArgumentOutOfRangeException">Range specified by <paramref name="start"/> and <paramref name
 270        /// <exception cref="InvalidOperationException">Content is not available, the builder has been linked with anoth
 271        public byte[] ToArray(int start, int byteCount)
 0272        {
 0273            BlobUtilities.ValidateRange(Count, start, byteCount, nameof(byteCount));
 274
 0275            var result = new byte[byteCount];
 276
 0277            int chunkStart = 0;
 0278            int bufferStart = start;
 0279            int bufferEnd = start + byteCount;
 0280            foreach (var chunk in GetChunks())
 0281            {
 0282                int chunkEnd = chunkStart + chunk.Length;
 0283                Debug.Assert(bufferStart >= chunkStart);
 284
 0285                if (chunkEnd > bufferStart)
 0286                {
 0287                    int bytesToCopy = Math.Min(bufferEnd, chunkEnd) - bufferStart;
 0288                    Debug.Assert(bytesToCopy >= 0);
 289
 0290                    Array.Copy(chunk._buffer, bufferStart - chunkStart, result, bufferStart - start, bytesToCopy);
 0291                    bufferStart += bytesToCopy;
 292
 0293                    if (bufferStart == bufferEnd)
 0294                    {
 0295                        break;
 296                    }
 0297                }
 298
 0299                chunkStart = chunkEnd;
 0300            }
 301
 0302            Debug.Assert(bufferStart == bufferEnd);
 303
 0304            return result;
 0305        }
 306
 307        /// <exception cref="InvalidOperationException">Content is not available, the builder has been linked with anoth
 308        public ImmutableArray<byte> ToImmutableArray()
 0309        {
 0310            return ToImmutableArray(0, Count);
 0311        }
 312
 313        /// <exception cref="ArgumentOutOfRangeException">Range specified by <paramref name="start"/> and <paramref name
 314        /// <exception cref="InvalidOperationException">Content is not available, the builder has been linked with anoth
 315        public ImmutableArray<byte> ToImmutableArray(int start, int byteCount)
 0316        {
 0317            byte[]? array = ToArray(start, byteCount);
 0318            return ImmutableCollectionsMarshal.AsImmutableArray(array);
 0319        }
 320
 321        internal bool TryGetSpan(out ReadOnlySpan<byte> buffer)
 0322        {
 0323            if (_nextOrPrevious == this)
 0324            {
 325                // If the blob builder has one chunk, we can just return it and avoid copies.
 0326                buffer = Span;
 0327                return true;
 328            }
 329
 0330            buffer = default;
 0331            return false;
 0332        }
 333
 334        /// <exception cref="ArgumentNullException"><paramref name="destination"/> is null.</exception>
 335        /// <exception cref="InvalidOperationException">Content is not available, the builder has been linked with anoth
 336        public void WriteContentTo(Stream destination)
 0337        {
 0338            if (destination is null)
 0339            {
 0340                Throw.ArgumentNull(nameof(destination));
 341            }
 342
 0343            foreach (var chunk in GetChunks())
 0344            {
 0345                destination.Write(chunk._buffer, 0, chunk.Length);
 0346            }
 0347        }
 348
 349        /// <exception cref="ArgumentNullException"><paramref name="destination"/> is default(<see cref="BlobWriter"/>).
 350        /// <exception cref="InvalidOperationException">Content is not available, the builder has been linked with anoth
 351        public void WriteContentTo(ref BlobWriter destination)
 0352        {
 0353            if (destination.IsDefault)
 0354            {
 0355                Throw.ArgumentNull(nameof(destination));
 356            }
 357
 0358            foreach (var chunk in GetChunks())
 0359            {
 0360                destination.WriteBytes(chunk.Span);
 0361            }
 0362        }
 363
 364        /// <exception cref="ArgumentNullException"><paramref name="destination"/> is null.</exception>
 365        /// <exception cref="InvalidOperationException">Content is not available, the builder has been linked with anoth
 366        public void WriteContentTo(BlobBuilder destination)
 0367        {
 0368            if (destination is null)
 0369            {
 0370                Throw.ArgumentNull(nameof(destination));
 371            }
 372
 0373            foreach (var chunk in GetChunks())
 0374            {
 0375                destination.WriteBytes(chunk.Span);
 0376            }
 0377        }
 378
 379        /// <exception cref="ArgumentNullException"><paramref name="prefix"/> is null.</exception>
 380        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 381        public void LinkPrefix(BlobBuilder prefix)
 0382        {
 0383            if (prefix is null)
 0384            {
 0385                Throw.ArgumentNull(nameof(prefix));
 386            }
 387
 388            // TODO: consider copying data from right to left while there is space
 389
 0390            if (!prefix.IsHead || !IsHead)
 0391            {
 0392                Throw.InvalidOperationBuilderAlreadyLinked();
 393            }
 394
 395            // avoid chaining empty chunks:
 0396            if (prefix.Count == 0)
 0397            {
 0398                prefix.ClearAndFreeChunk();
 0399                return;
 400            }
 401
 0402            PreviousLength += prefix.Count;
 403
 404            // prefix is not a head anymore:
 0405            prefix._length = prefix.FrozenLength;
 406
 407            // First and last chunks:
 408            //
 409            // [PrefixFirst]->[]->[PrefixLast] <- [prefix]    [First]->[]->[Last] <- [this]
 410            //       ^_________________|                          ^___________|
 411            //
 412            // Degenerate cases:
 413            // this == First == Last and/or prefix == PrefixFirst == PrefixLast.
 0414            var first = FirstChunk;
 0415            var prefixFirst = prefix.FirstChunk;
 0416            var last = _nextOrPrevious;
 0417            var prefixLast = prefix._nextOrPrevious;
 418
 419            // Relink like so:
 420            // [PrefixFirst]->[]->[PrefixLast] -> [prefix] -> [First]->[]->[Last] <- [this]
 421            //      ^________________________________________________________|
 422
 0423            _nextOrPrevious = (last != this) ? last : prefix;
 0424            prefix._nextOrPrevious = (first != this) ? first : (prefixFirst != prefix) ? prefixFirst : prefix;
 425
 0426            if (last != this)
 0427            {
 0428                last._nextOrPrevious = (prefixFirst != prefix) ? prefixFirst : prefix;
 0429            }
 430
 0431            if (prefixLast != prefix)
 0432            {
 0433                prefixLast._nextOrPrevious = prefix;
 0434            }
 435
 0436            prefix.CheckInvariants();
 0437            CheckInvariants();
 0438        }
 439
 440        /// <exception cref="ArgumentNullException"><paramref name="suffix"/> is null.</exception>
 441        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 442        public void LinkSuffix(BlobBuilder suffix)
 0443        {
 0444            if (suffix is null)
 0445            {
 0446                Throw.ArgumentNull(nameof(suffix));
 447            }
 448
 449            // TODO: consider copying data from right to left while there is space
 450
 0451            if (!IsHead || !suffix.IsHead)
 0452            {
 0453                Throw.InvalidOperationBuilderAlreadyLinked();
 454            }
 455
 456            // avoid chaining empty chunks:
 0457            if (suffix.Count == 0)
 0458            {
 0459                suffix.ClearAndFreeChunk();
 0460                return;
 461            }
 462
 0463            bool isEmpty = Count == 0;
 464
 465            // swap buffers of the heads:
 0466            var suffixBuffer = suffix._buffer;
 0467            uint suffixLength = suffix._length;
 0468            int suffixPreviousLength = suffix.PreviousLength;
 0469            int oldSuffixLength = suffix.Length;
 0470            suffix._buffer = _buffer;
 0471            suffix._length = FrozenLength; // suffix is not a head anymore
 0472            _buffer = suffixBuffer;
 0473            _length = suffixLength;
 474
 0475            PreviousLength += suffix.Length + suffixPreviousLength;
 476
 477            // Update the _previousLength of the suffix so that suffix.Count = suffix._previousLength + suffix.Length do
 478            // Note that the resulting previous length might be negative.
 479            // The value is not used, other than for calculating the value of Count property.
 0480            suffix._previousLengthOrFrozenSuffixLengthDelta = suffixPreviousLength + oldSuffixLength - suffix.Length;
 481
 0482            if (isEmpty)
 0483            {
 0484                var suffixLast = suffix._nextOrPrevious;
 0485                _nextOrPrevious = (suffixLast != suffix) ? suffixLast : this;
 0486            }
 487            else
 0488            {
 489                // First and last chunks:
 490                //
 491                // [First]->[]->[Last] <- [this]    [SuffixFirst]->[]->[SuffixLast]  <- [suffix]
 492                //    ^___________|                       ^_________________|
 493                //
 494                // Degenerate cases:
 495                // this == First == Last and/or suffix == SuffixFirst == SuffixLast.
 0496                var first = FirstChunk;
 0497                var suffixFirst = suffix.FirstChunk;
 0498                var last = _nextOrPrevious;
 0499                var suffixLast = suffix._nextOrPrevious;
 500
 501                // Relink like so:
 502                // [First]->[]->[Last] -> [suffix] -> [SuffixFirst]->[]->[SuffixLast]  <- [this]
 503                //    ^_______________________________________________________|
 0504                _nextOrPrevious = suffixLast;
 0505                suffix._nextOrPrevious = (suffixFirst != suffix) ? suffixFirst : (first != this) ? first : suffix;
 506
 0507                if (last != this)
 0508                {
 0509                    last._nextOrPrevious = suffix;
 0510                }
 511
 0512                if (suffixLast != suffix)
 0513                {
 0514                    suffixLast._nextOrPrevious = (first != this) ? first : suffix;
 0515                }
 0516            }
 517
 0518            CheckInvariants();
 0519            suffix.CheckInvariants();
 0520        }
 521
 522        private void AddLength(int value)
 0523        {
 0524            _length += (uint)value;
 0525        }
 526
 527        [MethodImpl(MethodImplOptions.NoInlining)]
 528        private void Expand(int newLength)
 0529        {
 530            // TODO: consider converting the last chunk to a smaller one if there is too much empty space left
 531
 532            // May happen only if the derived class attempts to write to a builder that is not last,
 533            // or if a builder prepended to another one is not discarded.
 0534            if (!IsHead)
 0535            {
 0536                Throw.InvalidOperationBuilderAlreadyLinked();
 537            }
 538
 0539            var newChunk = AllocateChunk(Math.Max(newLength, MinChunkSize));
 0540            if (newChunk.ChunkCapacity < newLength)
 0541            {
 542                // The overridden allocator didn't provide large enough buffer:
 0543                throw new InvalidOperationException(SR.Format(SR.ReturnedBuilderSizeTooSmall, GetType(), nameof(Allocate
 544            }
 545
 0546            var newBuffer = newChunk._buffer;
 547
 0548            if (_length == 0)
 0549            {
 550                // If the first write into an empty buffer needs more space than the buffer provides, swap the buffers.
 0551                newChunk._buffer = _buffer;
 0552                _buffer = newBuffer;
 0553            }
 554            else
 0555            {
 556                // Otherwise append the new buffer.
 0557                var last = _nextOrPrevious;
 0558                var first = FirstChunk;
 559
 0560                if (last == this)
 0561                {
 562                    // single chunk in the chain
 0563                    _nextOrPrevious = newChunk;
 0564                }
 565                else
 0566                {
 0567                    newChunk._nextOrPrevious = first;
 0568                    last._nextOrPrevious = newChunk;
 0569                    _nextOrPrevious = newChunk;
 0570                }
 571
 0572                newChunk._buffer = _buffer;
 0573                newChunk._length = FrozenLength;
 0574                newChunk._previousLengthOrFrozenSuffixLengthDelta = PreviousLength;
 575
 0576                _buffer = newBuffer;
 0577                PreviousLength += Length;
 0578                _length = 0;
 0579            }
 580
 0581            CheckInvariants();
 0582        }
 583
 584        /// <summary>
 585        /// Reserves a contiguous block of bytes.
 586        /// </summary>
 587        /// <exception cref="ArgumentOutOfRangeException"><paramref name="byteCount"/> is negative.</exception>
 588        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 589        public Blob ReserveBytes(int byteCount)
 0590        {
 0591            if (byteCount < 0)
 0592            {
 0593                Throw.ArgumentOutOfRange(nameof(byteCount));
 594            }
 595
 0596            int start = ReserveBytesImpl(byteCount);
 0597            Array.Clear(_buffer, start, byteCount);
 0598            return new Blob(_buffer, start, byteCount);
 0599        }
 600
 601        private int ReserveBytesImpl(int byteCount)
 0602        {
 0603            Debug.Assert(byteCount >= 0);
 604
 605            // If write is attempted to a frozen builder we fall back
 606            // to expand where an exception is thrown:
 0607            uint result = _length;
 0608            if (result > _buffer.Length - byteCount)
 0609            {
 0610                Expand(byteCount);
 0611                result = 0;
 0612            }
 613
 0614            _length = result + (uint)byteCount;
 0615            return (int)result;
 0616        }
 617
 618        private int ReserveBytesPrimitive(int byteCount)
 0619        {
 620            // If the primitive doesn't fit to the current chuck we'll allocate a new chunk that is at least MinChunkSiz
 621            // That chunk has to fit the primitive otherwise we might keep allocating new chunks and never end up with o
 0622            Debug.Assert(byteCount <= MinChunkSize);
 0623            return ReserveBytesImpl(byteCount);
 0624        }
 625
 626        /// <exception cref="ArgumentOutOfRangeException"><paramref name="byteCount"/> is negative.</exception>
 627        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 628        public void WriteBytes(byte value, int byteCount)
 0629        {
 0630            if (byteCount < 0)
 0631            {
 0632                Throw.ArgumentOutOfRange(nameof(byteCount));
 633            }
 634
 0635            if (!IsHead)
 0636            {
 0637                Throw.InvalidOperationBuilderAlreadyLinked();
 638            }
 639
 0640            int bytesToCurrent = Math.Min(FreeBytes, byteCount);
 641
 0642            _buffer.WriteBytes(Length, value, bytesToCurrent);
 0643            AddLength(bytesToCurrent);
 644
 0645            int remaining = byteCount - bytesToCurrent;
 0646            if (remaining > 0)
 0647            {
 0648                Expand(remaining);
 649
 0650                _buffer.WriteBytes(0, value, remaining);
 0651                AddLength(remaining);
 0652            }
 0653        }
 654
 655        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
 656        /// <exception cref="ArgumentOutOfRangeException"><paramref name="byteCount"/> is negative.</exception>
 657        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 658        public unsafe void WriteBytes(byte* buffer, int byteCount)
 0659        {
 0660            if (buffer is null)
 0661            {
 0662                Throw.ArgumentNull(nameof(buffer));
 663            }
 664
 0665            if (byteCount < 0)
 0666            {
 0667                Throw.ArgumentOutOfRange(nameof(byteCount));
 668            }
 669
 0670            if (!IsHead)
 0671            {
 0672                Throw.InvalidOperationBuilderAlreadyLinked();
 673            }
 674
 0675            WriteBytesUnchecked(new ReadOnlySpan<byte>(buffer, byteCount));
 0676        }
 677
 678        private void WriteBytesUnchecked(ReadOnlySpan<byte> buffer)
 0679        {
 0680            int bytesToCurrent = Math.Min(FreeBytes, buffer.Length);
 681
 0682            buffer.Slice(0, bytesToCurrent).CopyTo(_buffer.AsSpan(Length));
 683
 0684            AddLength(bytesToCurrent);
 685
 0686            ReadOnlySpan<byte> remaining = buffer.Slice(bytesToCurrent);
 0687            if (!remaining.IsEmpty)
 0688            {
 0689                Expand(remaining.Length);
 690
 0691                remaining.CopyTo(_buffer);
 0692                AddLength(remaining.Length);
 0693            }
 0694        }
 695
 696        /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
 697        /// <exception cref="ArgumentOutOfRangeException"><paramref name="byteCount"/> is negative.</exception>
 698        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 699        /// <returns>Bytes successfully written from the <paramref name="source" />.</returns>
 700        public int TryWriteBytes(Stream source, int byteCount)
 0701        {
 0702            if (source is null)
 0703            {
 0704                Throw.ArgumentNull(nameof(source));
 705            }
 706
 0707            if (byteCount < 0)
 0708            {
 0709                throw new ArgumentOutOfRangeException(nameof(byteCount));
 710            }
 711
 0712            if (byteCount == 0)
 0713            {
 0714                return 0;
 715            }
 716
 0717            int bytesRead = 0;
 0718            int bytesToCurrent = Math.Min(FreeBytes, byteCount);
 719
 0720            if (bytesToCurrent > 0)
 0721            {
 0722                bytesRead = source.TryReadAll(_buffer, Length, bytesToCurrent);
 0723                AddLength(bytesRead);
 724
 0725                if (bytesRead != bytesToCurrent)
 0726                {
 0727                    return bytesRead;
 728                }
 0729            }
 730
 0731            int remaining = byteCount - bytesToCurrent;
 0732            if (remaining > 0)
 0733            {
 0734                Expand(remaining);
 0735                bytesRead = source.TryReadAll(_buffer, 0, remaining);
 0736                AddLength(bytesRead);
 737
 0738                bytesRead += bytesToCurrent;
 0739            }
 740
 0741            return bytesRead;
 0742        }
 743
 744        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
 745        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 746        public void WriteBytes(ImmutableArray<byte> buffer)
 0747        {
 0748            if (buffer.IsDefault)
 0749            {
 0750                Throw.ArgumentNull(nameof(buffer));
 751            }
 752
 0753            WriteBytes(buffer.AsSpan());
 0754        }
 755
 756        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
 757        /// <exception cref="ArgumentOutOfRangeException">Range specified by <paramref name="start"/> and <paramref name
 758        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 759        public void WriteBytes(ImmutableArray<byte> buffer, int start, int byteCount)
 0760        {
 0761            if (buffer.IsDefault)
 0762            {
 0763                Throw.ArgumentNull(nameof(buffer));
 764            }
 765
 0766            BlobUtilities.ValidateRange(buffer.Length, start, byteCount, nameof(byteCount));
 767
 0768            WriteBytes(buffer.AsSpan(start, byteCount));
 0769        }
 770
 771        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
 772        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 773        public void WriteBytes(byte[] buffer)
 0774        {
 0775            if (buffer is null)
 0776            {
 0777                Throw.ArgumentNull(nameof(buffer));
 778            }
 779
 0780            WriteBytes(buffer.AsSpan());
 0781        }
 782
 783        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
 784        /// <exception cref="ArgumentOutOfRangeException">Range specified by <paramref name="start"/> and <paramref name
 785        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 786        public void WriteBytes(byte[] buffer, int start, int byteCount)
 0787        {
 0788            if (buffer is null)
 0789            {
 0790                Throw.ArgumentNull(nameof(buffer));
 791            }
 792
 0793            BlobUtilities.ValidateRange(buffer.Length, start, byteCount, nameof(byteCount));
 794
 0795            WriteBytes(buffer.AsSpan(start, byteCount));
 0796        }
 797
 798        internal void WriteBytes(ReadOnlySpan<byte> buffer)
 0799        {
 0800            if (!IsHead)
 0801            {
 0802                Throw.InvalidOperationBuilderAlreadyLinked();
 803            }
 804
 0805            WriteBytesUnchecked(buffer);
 0806        }
 807
 808        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 809        public void PadTo(int position)
 0810        {
 0811            WriteBytes(0, position - Count);
 0812        }
 813
 814        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 815        public void Align(int alignment)
 0816        {
 0817            int position = Count;
 0818            WriteBytes(0, BitArithmetic.Align(position, alignment) - position);
 0819        }
 820
 821        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 822        public void WriteBoolean(bool value)
 0823        {
 0824            WriteByte((byte)(value ? 1 : 0));
 0825        }
 826
 827        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 828        public void WriteByte(byte value)
 0829        {
 0830            int start = ReserveBytesPrimitive(sizeof(byte));
 0831            _buffer[start] = value;
 0832        }
 833
 834        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 835        public void WriteSByte(sbyte value)
 0836        {
 0837            WriteByte(unchecked((byte)value));
 0838        }
 839
 840        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 841        public void WriteDouble(double value)
 0842        {
 0843            int start = ReserveBytesPrimitive(sizeof(double));
 0844            _buffer.WriteDouble(start, value);
 0845        }
 846
 847        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 848        public void WriteSingle(float value)
 0849        {
 0850            int start = ReserveBytesPrimitive(sizeof(float));
 0851            _buffer.WriteSingle(start, value);
 0852        }
 853
 854        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 855        public void WriteInt16(short value)
 0856        {
 0857            WriteUInt16(unchecked((ushort)value));
 0858        }
 859
 860        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 861        public void WriteUInt16(ushort value)
 0862        {
 0863            int start = ReserveBytesPrimitive(sizeof(ushort));
 0864            _buffer.WriteUInt16(start, value);
 0865        }
 866
 867        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 868        public void WriteInt16BE(short value)
 0869        {
 0870            WriteUInt16BE(unchecked((ushort)value));
 0871        }
 872
 873        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 874        public void WriteUInt16BE(ushort value)
 0875        {
 0876            int start = ReserveBytesPrimitive(sizeof(ushort));
 0877            _buffer.WriteUInt16BE(start, value);
 0878        }
 879
 880        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 881        public void WriteInt32BE(int value)
 0882        {
 0883            WriteUInt32BE(unchecked((uint)value));
 0884        }
 885
 886        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 887        public void WriteUInt32BE(uint value)
 0888        {
 0889            int start = ReserveBytesPrimitive(sizeof(uint));
 0890            _buffer.WriteUInt32BE(start, value);
 0891        }
 892
 893        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 894        public void WriteInt32(int value)
 0895        {
 0896            WriteUInt32(unchecked((uint)value));
 0897        }
 898
 899        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 900        public void WriteUInt32(uint value)
 0901        {
 0902            int start = ReserveBytesPrimitive(sizeof(uint));
 0903            _buffer.WriteUInt32(start, value);
 0904        }
 905
 906        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 907        public void WriteInt64(long value)
 0908        {
 0909            WriteUInt64(unchecked((ulong)value));
 0910        }
 911
 912        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 913        public void WriteUInt64(ulong value)
 0914        {
 0915            int start = ReserveBytesPrimitive(sizeof(ulong));
 0916            _buffer.WriteUInt64(start, value);
 0917        }
 918
 919        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 920        public void WriteDecimal(decimal value)
 0921        {
 0922            int start = ReserveBytesPrimitive(BlobUtilities.SizeOfSerializedDecimal);
 0923            _buffer.WriteDecimal(start, value);
 0924        }
 925
 926        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 927        public void WriteGuid(Guid value)
 0928        {
 0929            int start = ReserveBytesPrimitive(BlobUtilities.SizeOfGuid);
 0930            _buffer.WriteGuid(start, value);
 0931        }
 932
 933        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 934        public void WriteDateTime(DateTime value)
 0935        {
 0936            WriteInt64(value.Ticks);
 0937        }
 938
 939        /// <summary>
 940        /// Writes a reference to a heap (heap offset) or a table (row number).
 941        /// </summary>
 942        /// <param name="reference">Heap offset or table row number.</param>
 943        /// <param name="isSmall">True to encode the reference as 16-bit integer, false to encode as 32-bit integer.</pa
 944        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 945        public void WriteReference(int reference, bool isSmall)
 0946        {
 947            // This code is a very hot path, hence we don't check if the reference actually fits 2B.
 948
 0949            if (isSmall)
 0950            {
 0951                Debug.Assert(unchecked((ushort)reference) == reference);
 0952                WriteUInt16((ushort)reference);
 0953            }
 954            else
 0955            {
 0956                WriteInt32(reference);
 0957            }
 0958        }
 959
 960        /// <summary>
 961        /// Writes UTF-16 (little-endian) encoded string at the current position.
 962        /// </summary>
 963        /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
 964        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 965        public void WriteUTF16(char[] value)
 0966        {
 0967            if (value is null)
 0968            {
 0969                Throw.ArgumentNull(nameof(value));
 970            }
 971
 0972            if (!IsHead)
 0973            {
 0974                Throw.InvalidOperationBuilderAlreadyLinked();
 975            }
 976
 0977            WriteUTF16(value.AsSpan());
 0978        }
 979
 980        /// <summary>
 981        /// Writes UTF-16 (little-endian) encoded string at the current position.
 982        /// </summary>
 983        /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
 984        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 985        public void WriteUTF16(string value)
 0986        {
 0987            if (value is null)
 0988            {
 0989                Throw.ArgumentNull(nameof(value));
 990            }
 991
 0992            if (!IsHead)
 0993            {
 0994                Throw.InvalidOperationBuilderAlreadyLinked();
 995            }
 996
 0997            WriteUTF16(value.AsSpan());
 0998        }
 999
 1000        private void WriteUTF16(ReadOnlySpan<char> value)
 01001        {
 01002            if (!IsHead)
 01003            {
 01004                Throw.InvalidOperationBuilderAlreadyLinked();
 1005            }
 1006
 01007            if (BitConverter.IsLittleEndian)
 01008            {
 01009                WriteBytesUnchecked(MemoryMarshal.AsBytes(value));
 01010            }
 1011            else
 01012            {
 01013                foreach (char c in value)
 01014                {
 01015                    WriteUInt16(c);
 01016                }
 01017            }
 01018        }
 1019
 1020        /// <summary>
 1021        /// Writes string in SerString format (see ECMA-335-II 23.3 Custom attributes).
 1022        /// </summary>
 1023        /// <remarks>
 1024        /// The string is UTF-8 encoded and prefixed by the its size in bytes.
 1025        /// Null string is represented as a single byte 0xFF.
 1026        /// </remarks>
 1027        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 1028        public void WriteSerializedString(string? value)
 01029        {
 01030            if (value == null)
 01031            {
 01032                WriteByte(0xff);
 01033                return;
 1034            }
 1035
 01036            WriteUTF8(value, 0, value.Length, allowUnpairedSurrogates: true, prependSize: true);
 01037        }
 1038
 1039        /// <summary>
 1040        /// Writes string in User String (#US) heap format (see ECMA-335-II 24.2.4 #US and #Blob heaps):
 1041        /// </summary>
 1042        /// <remarks>
 1043        /// The string is UTF-16 encoded and prefixed by the its size in bytes.
 1044        ///
 1045        /// This final byte holds the value 1 if and only if any UTF-16 character within the string has any bit set in i
 1046        /// or its low byte is any of the following: 0x01-0x08, 0x0E-0x1F, 0x27, 0x2D, 0x7F. Otherwise, it holds 0.
 1047        /// The 1 signifies Unicode characters that require handling beyond that normally provided for 8-bit encoding se
 1048        /// </remarks>
 1049        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 1050        public void WriteUserString(string value)
 01051        {
 01052            if (value is null)
 01053            {
 01054                Throw.ArgumentNull(nameof(value));
 1055            }
 1056
 01057            WriteCompressedInteger(BlobUtilities.GetUserStringByteLength(value.Length));
 01058            WriteUTF16(value);
 01059            WriteByte(BlobUtilities.GetUserStringTrailingByte(value));
 01060        }
 1061
 1062        /// <summary>
 1063        /// Writes UTF-8 encoded string at the current position.
 1064        /// </summary>
 1065        /// <param name="value">Constant value.</param>
 1066        /// <param name="allowUnpairedSurrogates">
 1067        /// True to encode unpaired surrogates as specified, otherwise replace them with U+FFFD character.
 1068        /// </param>
 1069        /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
 1070        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 1071        public void WriteUTF8(string value, bool allowUnpairedSurrogates = true)
 01072        {
 01073            if (value is null)
 01074            {
 01075                Throw.ArgumentNull(nameof(value));
 1076            }
 1077
 01078            WriteUTF8(value, 0, value.Length, allowUnpairedSurrogates, prependSize: false);
 01079        }
 1080
 1081        internal unsafe void WriteUTF8(string str, int start, int length, bool allowUnpairedSurrogates, bool prependSize
 01082        {
 01083            Debug.Assert(start >= 0);
 01084            Debug.Assert(length >= 0);
 01085            Debug.Assert(start + length <= str.Length);
 1086
 01087            if (!IsHead)
 01088            {
 01089                Throw.InvalidOperationBuilderAlreadyLinked();
 1090            }
 1091
 01092            fixed (char* strPtr = str)
 01093            {
 01094                char* currentPtr = strPtr + start;
 1095                char* nextPtr;
 1096
 1097                // the max size of compressed int is 4B:
 01098                int byteLimit = FreeBytes - (prependSize ? sizeof(uint) : 0);
 1099
 01100                int bytesToCurrent = BlobUtilities.GetUTF8ByteCount(currentPtr, length, byteLimit, out nextPtr);
 01101                int charsToCurrent = (int)(nextPtr - currentPtr);
 01102                int charsToNext = length - charsToCurrent;
 01103                int bytesToNext = BlobUtilities.GetUTF8ByteCount(nextPtr, charsToNext);
 1104
 01105                if (prependSize)
 01106                {
 01107                    WriteCompressedInteger(bytesToCurrent + bytesToNext);
 01108                }
 1109
 01110                _buffer.WriteUTF8(Length, currentPtr, charsToCurrent, bytesToCurrent, allowUnpairedSurrogates);
 01111                AddLength(bytesToCurrent);
 1112
 01113                if (bytesToNext > 0)
 01114                {
 01115                    Expand(bytesToNext);
 1116
 01117                    _buffer.WriteUTF8(0, nextPtr, charsToNext, bytesToNext, allowUnpairedSurrogates);
 01118                    AddLength(bytesToNext);
 01119                }
 01120            }
 01121        }
 1122
 1123        /// <summary>
 1124        /// Implements compressed signed integer encoding as defined by ECMA-335-II chapter 23.2: Blobs and signatures.
 1125        /// </summary>
 1126        /// <remarks>
 1127        /// If the value lies between -64 (0xFFFFFFC0) and 63 (0x3F), inclusive, encode as a one-byte integer:
 1128        /// bit 7 clear, value bits 5 through 0 held in bits 6 through 1, sign bit (value bit 31) in bit 0.
 1129        ///
 1130        /// If the value lies between -8192 (0xFFFFE000) and 8191 (0x1FFF), inclusive, encode as a two-byte integer:
 1131        /// 15 set, bit 14 clear, value bits 12 through 0 held in bits 13 through 1, sign bit(value bit 31) in bit 0.
 1132        ///
 1133        /// If the value lies between -268435456 (0xF000000) and 268435455 (0x0FFFFFFF), inclusive, encode as a four-byt
 1134        /// 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
 1135        /// </remarks>
 1136        /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> can't be represented as a compressed 
 1137        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 1138        public void WriteCompressedSignedInteger(int value)
 01139        {
 01140            BlobWriterImpl.WriteCompressedSignedInteger(this, value);
 01141        }
 1142
 1143        /// <summary>
 1144        /// Implements compressed unsigned integer encoding as defined by ECMA-335-II chapter 23.2: Blobs and signatures
 1145        /// </summary>
 1146        /// <remarks>
 1147        /// If the value lies between 0 (0x00) and 127 (0x7F), inclusive,
 1148        /// encode as a one-byte integer (bit 7 is clear, value held in bits 6 through 0).
 1149        ///
 1150        /// If the value lies between 28 (0x80) and 214 - 1 (0x3FFF), inclusive,
 1151        /// encode as a 2-byte integer with bit 15 set, bit 14 clear (value held in bits 13 through 0).
 1152        ///
 1153        /// Otherwise, encode as a 4-byte integer, with bit 31 set, bit 30 set, bit 29 clear (value held in bits 28 thro
 1154        /// </remarks>
 1155        /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> can't be represented as a compressed 
 1156        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 1157        public void WriteCompressedInteger(int value)
 01158        {
 01159            BlobWriterImpl.WriteCompressedInteger(this, unchecked((uint)value));
 01160        }
 1161
 1162        /// <summary>
 1163        /// Writes a constant value (see ECMA-335 Partition II section 22.9) at the current position.
 1164        /// </summary>
 1165        /// <exception cref="ArgumentException"><paramref name="value"/> is not of a constant type.</exception>
 1166        /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</e
 1167        public void WriteConstant(object? value)
 01168        {
 01169            BlobWriterImpl.WriteConstant(this, value);
 01170        }
 1171
 1172        internal string GetDebuggerDisplay()
 01173        {
 01174            return IsHead ?
 01175                string.Join("->", GetChunks().Select(chunk => $"[{Display(chunk._buffer, chunk.Length)}]")) :
 01176                $"<{Display(_buffer, Length)}>";
 01177        }
 1178
 1179        private static string Display(byte[] bytes, int length)
 01180        {
 1181            const int MaxDisplaySize = 64;
 1182
 01183            return (length <= MaxDisplaySize) ?
 01184                BitConverter.ToString(bytes, 0, length) :
 01185                BitConverter.ToString(bytes, 0, MaxDisplaySize / 2) + "-...-" + BitConverter.ToString(bytes, length - Ma
 01186        }
 1187
 1188        private void ClearAndFreeChunk()
 01189        {
 01190            ClearChunk();
 01191            FreeChunk();
 01192        }
 1193    }
 1194}
 1195

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobBuilder.Enumerators.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;
 5using System.Collections;
 6using System.Collections.Generic;
 7using System.Diagnostics;
 8
 9namespace System.Reflection.Metadata
 10{
 11    public partial class BlobBuilder
 12    {
 13        // internal for testing
 14        internal struct Chunks : IEnumerable<BlobBuilder>, IEnumerator<BlobBuilder>, IEnumerator
 15        {
 16            private readonly BlobBuilder _head;
 17            private BlobBuilder _next;
 18            private BlobBuilder? _currentOpt;
 19
 20            internal Chunks(BlobBuilder builder)
 021            {
 022                Debug.Assert(builder.IsHead);
 23
 024                _head = builder;
 025                _next = builder.FirstChunk;
 026                _currentOpt = null;
 027            }
 28
 029            object IEnumerator.Current => Current;
 030            public BlobBuilder Current => _currentOpt!;
 31
 32            public bool MoveNext()
 033            {
 034                if (_currentOpt == _head)
 035                {
 036                    return false;
 37                }
 38
 039                if (_currentOpt == _head._nextOrPrevious)
 040                {
 041                    _currentOpt = _head;
 042                    return true;
 43                }
 44
 045                _currentOpt = _next;
 046                _next = _next._nextOrPrevious;
 047                return true;
 048            }
 49
 50            public void Reset()
 051            {
 052                _currentOpt = null;
 053                _next = _head.FirstChunk;
 054            }
 55
 056            void IDisposable.Dispose() { }
 57
 58            // IEnumerable:
 059            public Chunks GetEnumerator() => this;
 060            IEnumerator<BlobBuilder> IEnumerable<BlobBuilder>.GetEnumerator() => GetEnumerator();
 061            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 62        }
 63
 64        public struct Blobs : IEnumerable<Blob>, IEnumerator<Blob>, IEnumerator
 65        {
 66            private Chunks _chunks;
 67
 68            internal Blobs(BlobBuilder builder)
 069            {
 070                _chunks = new Chunks(builder);
 071            }
 72
 073            object IEnumerator.Current => Current;
 74
 75            public Blob Current
 76            {
 77                get
 078                {
 079                    var current = _chunks.Current;
 080                    if (current != null)
 081                    {
 082                        return new Blob(current._buffer, 0, current.Length);
 83                    }
 84                    else
 085                    {
 086                        return default(Blob);
 87                    }
 088                }
 89            }
 90
 091            public bool MoveNext() => _chunks.MoveNext();
 092            public void Reset() => _chunks.Reset();
 93
 094            void IDisposable.Dispose() { }
 95
 96            // IEnumerable:
 097            public Blobs GetEnumerator() => this;
 098            IEnumerator<Blob> IEnumerable<Blob>.GetEnumerator() => GetEnumerator();
 099            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 100        }
 101    }
 102}
 103

Methods/Properties

FirstChunk()
IsHead()
Length()
FrozenLength()
Span()
.ctor(System.Int32)
AllocateChunk(System.Int32)
FreeChunk()
Clear()
Free()
ClearChunk()
CheckInvariants()
Count()
PreviousLength()
PreviousLength(System.Int32)
FreeBytes()
ChunkCapacity()
GetChunks()
GetBlobs()
ContentEquals(System.Reflection.Metadata.BlobBuilder)
ToArray()
ToArray(System.Int32,System.Int32)
ToImmutableArray()
ToImmutableArray(System.Int32,System.Int32)
TryGetSpan(System.ReadOnlySpan`1<System.Byte>&)
WriteContentTo(System.IO.Stream)
WriteContentTo(System.Reflection.Metadata.BlobWriter&)
WriteContentTo(System.Reflection.Metadata.BlobBuilder)
LinkPrefix(System.Reflection.Metadata.BlobBuilder)
LinkSuffix(System.Reflection.Metadata.BlobBuilder)
AddLength(System.Int32)
Expand(System.Int32)
ReserveBytes(System.Int32)
ReserveBytesImpl(System.Int32)
ReserveBytesPrimitive(System.Int32)
WriteBytes(System.Byte,System.Int32)
WriteBytes(System.Byte*,System.Int32)
WriteBytesUnchecked(System.ReadOnlySpan`1<System.Byte>)
TryWriteBytes(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)
WriteBytes(System.ReadOnlySpan`1<System.Byte>)
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)
GetDebuggerDisplay()
Display(System.Byte[],System.Int32)
ClearAndFreeChunk()
.ctor(System.Reflection.Metadata.BlobBuilder)
em.Collections.IEnumerator.get_Current()
Current()
MoveNext()
Reset()
System.IDisposable.Dispose()
GetEnumerator()
System.Collections.Generic.IEnumerable<System.Reflection.Metadata.BlobBuilder>.GetEnumerator()
System.Collections.IEnumerable.GetEnumerator()
.ctor(System.Reflection.Metadata.BlobBuilder)
em.Collections.IEnumerator.get_Current()
Current()
MoveNext()
Reset()
System.IDisposable.Dispose()
GetEnumerator()
System.Collections.Generic.IEnumerable<System.Reflection.Metadata.Blob>.GetEnumerator()
System.Collections.IEnumerable.GetEnumerator()