< Summary

Information
Class: System.Net.ArrayBuffer
Assembly: System.Text.Json
File(s): D:\runner\runtime\src\libraries\Common\src\System\Net\ArrayBuffer.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 87
Coverable lines: 87
Total lines: 195
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 26
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)0%440%
Dispose()0%220%
ClearAndReturnBuffer()100%110%
DiscardAll()100%110%
Commit(...)100%110%
EnsureAvailableSpace(...)0%220%
EnsureAvailableSpaceCore(...)0%14140%
ReturnBufferIfPooled(...)0%440%

File(s)

D:\runner\runtime\src\libraries\Common\src\System\Net\ArrayBuffer.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.Buffers;
 5using System.Diagnostics;
 6using System.Runtime.CompilerServices;
 7using System.Runtime.InteropServices;
 8
 9namespace System.Net
 10{
 11    // Warning: Mutable struct!
 12    // The purpose of this struct is to simplify buffer management.
 13    // It manages a sliding buffer where bytes can be added at the end and removed at the beginning.
 14    // [ActiveSpan/Memory] contains the current buffer contents; these bytes will be preserved
 15    // (copied, if necessary) on any call to EnsureAvailableBytes.
 16    // [AvailableSpan/Memory] contains the available bytes past the end of the current content,
 17    // and can be written to in order to add data to the end of the buffer.
 18    // Commit(byteCount) will extend the ActiveSpan by [byteCount] bytes into the AvailableSpan.
 19    // Discard(byteCount) will discard [byteCount] bytes as the beginning of the ActiveSpan.
 20
 21    [StructLayout(LayoutKind.Auto)]
 22    internal struct ArrayBuffer : IDisposable
 23    {
 24        private readonly bool _usePool;
 25        private byte[] _bytes;
 26        private int _activeStart;
 27        private int _availableStart;
 28
 29        // Invariants:
 30        // 0 <= _activeStart <= _availableStart <= bytes.Length
 31
 32        public ArrayBuffer(int initialSize, bool usePool = false)
 033        {
 034            Debug.Assert(initialSize > 0 || usePool);
 35
 036            _usePool = usePool;
 037            _bytes = initialSize == 0
 038                ? Array.Empty<byte>()
 039                : usePool ? ArrayPool<byte>.Shared.Rent(initialSize) : new byte[initialSize];
 040            _activeStart = 0;
 041            _availableStart = 0;
 042        }
 43
 44        public ArrayBuffer(byte[] buffer)
 45        {
 46            Debug.Assert(buffer.Length > 0);
 47
 48            _usePool = false;
 49            _bytes = buffer;
 50            _activeStart = 0;
 51            _availableStart = 0;
 52        }
 53
 54        public void Dispose()
 055        {
 056            DiscardAll();
 57
 058            byte[] array = _bytes;
 059            _bytes = null!;
 60
 061            if (array is not null)
 062            {
 063                ReturnBufferIfPooled(array);
 064            }
 065        }
 66
 67        // This is different from Dispose as the instance remains usable afterwards (_bytes will not be null).
 68        public void ClearAndReturnBuffer()
 069        {
 070            Debug.Assert(_usePool);
 071            Debug.Assert(_bytes is not null);
 72
 073            DiscardAll();
 74
 075            byte[] bufferToReturn = _bytes;
 076            _bytes = Array.Empty<byte>();
 077            ReturnBufferIfPooled(bufferToReturn);
 078        }
 79
 080        public int ActiveLength => _availableStart - _activeStart;
 081        public Span<byte> ActiveSpan => new Span<byte>(_bytes, _activeStart, _availableStart - _activeStart);
 82        public ReadOnlySpan<byte> ActiveReadOnlySpan => new ReadOnlySpan<byte>(_bytes, _activeStart, _availableStart - _
 083        public Memory<byte> ActiveMemory => new Memory<byte>(_bytes, _activeStart, _availableStart - _activeStart);
 84
 085        public int AvailableLength => _bytes.Length - _availableStart;
 086        public Span<byte> AvailableSpan => _bytes.AsSpan(_availableStart);
 087        public Memory<byte> AvailableMemory => _bytes.AsMemory(_availableStart);
 88        public Memory<byte> AvailableMemorySliced(int length) => new Memory<byte>(_bytes, _availableStart, length);
 89
 090        public int Capacity => _bytes.Length;
 91        public int ActiveStartOffset => _activeStart;
 92
 93        public byte[] DangerousGetUnderlyingBuffer() => _bytes;
 94
 95        public void Discard(int byteCount)
 96        {
 97            Debug.Assert(byteCount <= ActiveLength, $"Expected {byteCount} <= {ActiveLength}");
 98            _activeStart += byteCount;
 99
 100            if (_activeStart == _availableStart)
 101            {
 102                _activeStart = 0;
 103                _availableStart = 0;
 104            }
 105        }
 106
 107        public void DiscardAll()
 0108        {
 0109            _activeStart = 0;
 0110            _availableStart = 0;
 0111        }
 112
 113        public void Commit(int byteCount)
 0114        {
 0115            Debug.Assert(byteCount <= AvailableLength);
 0116            _availableStart += byteCount;
 0117        }
 118
 119        // Ensure at least [byteCount] bytes to write to.
 120        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 121        public void EnsureAvailableSpace(int byteCount)
 0122        {
 0123            if (byteCount > AvailableLength)
 0124            {
 0125                EnsureAvailableSpaceCore(byteCount);
 0126            }
 0127        }
 128
 129        private void EnsureAvailableSpaceCore(int byteCount)
 0130        {
 0131            Debug.Assert(AvailableLength < byteCount);
 132
 0133            if (_bytes.Length == 0)
 0134            {
 0135                Debug.Assert(_usePool && _activeStart == 0 && _availableStart == 0);
 0136                _bytes = ArrayPool<byte>.Shared.Rent(byteCount);
 0137                return;
 138            }
 139
 0140            int totalFree = _activeStart + AvailableLength;
 0141            if (byteCount <= totalFree)
 0142            {
 143                // We can free up enough space by just shifting the bytes down, so do so.
 0144                Buffer.BlockCopy(_bytes, _activeStart, _bytes, 0, ActiveLength);
 0145                _availableStart = ActiveLength;
 0146                _activeStart = 0;
 0147                Debug.Assert(byteCount <= AvailableLength);
 0148                return;
 149            }
 150
 0151            int desiredSize = ActiveLength + byteCount;
 152
 0153            if ((uint)desiredSize > Array.MaxLength)
 0154            {
 0155                throw new OutOfMemoryException();
 156            }
 157
 158            // Double the existing buffer size (capped at Array.MaxLength).
 0159            int newSize = Math.Max(desiredSize, (int)Math.Min(Array.MaxLength, 2 * (uint)_bytes.Length));
 160
 0161            byte[] newBytes = _usePool ?
 0162                ArrayPool<byte>.Shared.Rent(newSize) :
 0163                new byte[newSize];
 0164            byte[] oldBytes = _bytes;
 165
 0166            if (ActiveLength != 0)
 0167            {
 0168                Buffer.BlockCopy(oldBytes, _activeStart, newBytes, 0, ActiveLength);
 0169            }
 170
 0171            _availableStart = ActiveLength;
 0172            _activeStart = 0;
 173
 0174            _bytes = newBytes;
 0175            ReturnBufferIfPooled(oldBytes);
 176
 0177            Debug.Assert(byteCount <= AvailableLength);
 0178        }
 179
 180        public void Grow()
 181        {
 182            EnsureAvailableSpaceCore(AvailableLength + 1);
 183        }
 184
 185        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 186        private void ReturnBufferIfPooled(byte[] buffer)
 0187        {
 188            // The buffer may be Array.Empty<byte>()
 0189            if (_usePool && buffer.Length > 0)
 0190            {
 0191                ArrayPool<byte>.Shared.Return(buffer);
 0192            }
 0193        }
 194    }
 195}