< Summary

Information
Class: System.Net.ArrayBuffer
Assembly: System.Net.Security
File(s): D:\runner\runtime\src\libraries\Common\src\System\Net\ArrayBuffer.cs
Line coverage
70%
Covered lines: 67
Uncovered lines: 28
Coverable lines: 95
Total lines: 195
Line coverage: 70.5%
Branch coverage
43%
Covered branches: 13
Total branches: 30
Branch coverage: 43.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)25%44100%
Dispose()0%220%
ClearAndReturnBuffer()100%11100%
Discard(...)0%440%
DiscardAll()100%11100%
Commit(...)100%11100%
EnsureAvailableSpace(...)100%22100%
EnsureAvailableSpaceCore(...)57.14%141476.47%
ReturnBufferIfPooled(...)50%44100%

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)
 3833        {
 3834            Debug.Assert(initialSize > 0 || usePool);
 35
 3836            _usePool = usePool;
 3837            _bytes = initialSize == 0
 3838                ? Array.Empty<byte>()
 3839                : usePool ? ArrayPool<byte>.Shared.Rent(initialSize) : new byte[initialSize];
 3840            _activeStart = 0;
 3841            _availableStart = 0;
 3842        }
 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()
 3869        {
 3870            Debug.Assert(_usePool);
 3871            Debug.Assert(_bytes is not null);
 72
 3873            DiscardAll();
 74
 3875            byte[] bufferToReturn = _bytes;
 3876            _bytes = Array.Empty<byte>();
 3877            ReturnBufferIfPooled(bufferToReturn);
 3878        }
 79
 25180        public int ActiveLength => _availableStart - _activeStart;
 8281        public Span<byte> ActiveSpan => new Span<byte>(_bytes, _activeStart, _availableStart - _activeStart);
 082        public ReadOnlySpan<byte> ActiveReadOnlySpan => new ReadOnlySpan<byte>(_bytes, _activeStart, _availableStart - _
 83        public Memory<byte> ActiveMemory => new Memory<byte>(_bytes, _activeStart, _availableStart - _activeStart);
 84
 28285        public int AvailableLength => _bytes.Length - _availableStart;
 086        public Span<byte> AvailableSpan => _bytes.AsSpan(_availableStart);
 6487        public Memory<byte> AvailableMemory => _bytes.AsMemory(_availableStart);
 88        public Memory<byte> AvailableMemorySliced(int length) => new Memory<byte>(_bytes, _availableStart, length);
 89
 90        public int Capacity => _bytes.Length;
 91        public int ActiveStartOffset => _activeStart;
 92
 93        public byte[] DangerousGetUnderlyingBuffer() => _bytes;
 94
 95        public void Discard(int byteCount)
 096        {
 097            Debug.Assert(byteCount <= ActiveLength, $"Expected {byteCount} <= {ActiveLength}");
 098            _activeStart += byteCount;
 99
 0100            if (_activeStart == _availableStart)
 0101            {
 0102                _activeStart = 0;
 0103                _availableStart = 0;
 0104            }
 0105        }
 106
 107        public void DiscardAll()
 38108        {
 38109            _activeStart = 0;
 38110            _availableStart = 0;
 38111        }
 112
 113        public void Commit(int byteCount)
 38114        {
 38115            Debug.Assert(byteCount <= AvailableLength);
 38116            _availableStart += byteCount;
 38117        }
 118
 119        // Ensure at least [byteCount] bytes to write to.
 120        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 121        public void EnsureAvailableSpace(int byteCount)
 109122        {
 109123            if (byteCount > AvailableLength)
 49124            {
 49125                EnsureAvailableSpaceCore(byteCount);
 49126            }
 109127        }
 128
 129        private void EnsureAvailableSpaceCore(int byteCount)
 49130        {
 49131            Debug.Assert(AvailableLength < byteCount);
 132
 49133            if (_bytes.Length == 0)
 38134            {
 38135                Debug.Assert(_usePool && _activeStart == 0 && _availableStart == 0);
 38136                _bytes = ArrayPool<byte>.Shared.Rent(byteCount);
 38137                return;
 138            }
 139
 11140            int totalFree = _activeStart + AvailableLength;
 11141            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
 11151            int desiredSize = ActiveLength + byteCount;
 152
 11153            if ((uint)desiredSize > Array.MaxLength)
 0154            {
 0155                throw new OutOfMemoryException();
 156            }
 157
 158            // Double the existing buffer size (capped at Array.MaxLength).
 11159            int newSize = Math.Max(desiredSize, (int)Math.Min(Array.MaxLength, 2 * (uint)_bytes.Length));
 160
 11161            byte[] newBytes = _usePool ?
 11162                ArrayPool<byte>.Shared.Rent(newSize) :
 11163                new byte[newSize];
 11164            byte[] oldBytes = _bytes;
 165
 11166            if (ActiveLength != 0)
 11167            {
 11168                Buffer.BlockCopy(oldBytes, _activeStart, newBytes, 0, ActiveLength);
 11169            }
 170
 11171            _availableStart = ActiveLength;
 11172            _activeStart = 0;
 173
 11174            _bytes = newBytes;
 11175            ReturnBufferIfPooled(oldBytes);
 176
 11177            Debug.Assert(byteCount <= AvailableLength);
 49178        }
 179
 180        public void Grow()
 181        {
 182            EnsureAvailableSpaceCore(AvailableLength + 1);
 183        }
 184
 185        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 186        private void ReturnBufferIfPooled(byte[] buffer)
 49187        {
 188            // The buffer may be Array.Empty<byte>()
 49189            if (_usePool && buffer.Length > 0)
 49190            {
 49191                ArrayPool<byte>.Shared.Return(buffer);
 49192            }
 49193        }
 194    }
 195}