< Summary

Information
Class: System.Net.ArrayBuffer
Assembly: System.Net.Http
File(s): D:\runner\runtime\src\libraries\Common\src\System\Net\ArrayBuffer.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 109
Coverable lines: 109
Total lines: 197
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 30
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%
.ctor(...)100%110%
Dispose()0%220%
ClearAndReturnBuffer()100%110%
AvailableMemorySliced(...)100%110%
DangerousGetUnderlyingBuffer()100%110%
Discard(...)0%440%
Commit(...)100%110%
EnsureAvailableSpace(...)0%220%
EnsureAvailableSpaceCore(...)0%14140%
Grow()100%110%
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#if NET
 025        private static int ArrayMaxLength => Array.MaxLength;
 26#else
 27        private const int ArrayMaxLength = 0X7FFFFFC7;
 28#endif
 29
 30        private readonly bool _usePool;
 31        private byte[] _bytes;
 32        private int _activeStart;
 33        private int _availableStart;
 34
 35        // Invariants:
 36        // 0 <= _activeStart <= _availableStart <= bytes.Length
 37
 38        public ArrayBuffer(int initialSize, bool usePool = false)
 039        {
 040            Debug.Assert(initialSize > 0 || usePool);
 41
 042            _usePool = usePool;
 043            _bytes = initialSize == 0
 044                ? Array.Empty<byte>()
 045                : usePool ? ArrayPool<byte>.Shared.Rent(initialSize) : new byte[initialSize];
 046            _activeStart = 0;
 047            _availableStart = 0;
 048        }
 49
 50        public ArrayBuffer(byte[] buffer)
 051        {
 052            Debug.Assert(buffer.Length > 0);
 53
 054            _usePool = false;
 055            _bytes = buffer;
 056            _activeStart = 0;
 057            _availableStart = 0;
 058        }
 59
 60        public void Dispose()
 061        {
 062            _activeStart = 0;
 063            _availableStart = 0;
 64
 065            byte[] array = _bytes;
 066            _bytes = null!;
 67
 068            if (array is not null)
 069            {
 070                ReturnBufferIfPooled(array);
 071            }
 072        }
 73
 74        // This is different from Dispose as the instance remains usable afterwards (_bytes will not be null).
 75        public void ClearAndReturnBuffer()
 076        {
 077            Debug.Assert(_usePool);
 078            Debug.Assert(_bytes is not null);
 79
 080            _activeStart = 0;
 081            _availableStart = 0;
 82
 083            byte[] bufferToReturn = _bytes;
 084            _bytes = Array.Empty<byte>();
 085            ReturnBufferIfPooled(bufferToReturn);
 086        }
 87
 088        public int ActiveLength => _availableStart - _activeStart;
 089        public Span<byte> ActiveSpan => new Span<byte>(_bytes, _activeStart, _availableStart - _activeStart);
 090        public ReadOnlySpan<byte> ActiveReadOnlySpan => new ReadOnlySpan<byte>(_bytes, _activeStart, _availableStart - _
 091        public Memory<byte> ActiveMemory => new Memory<byte>(_bytes, _activeStart, _availableStart - _activeStart);
 92
 093        public int AvailableLength => _bytes.Length - _availableStart;
 094        public Span<byte> AvailableSpan => _bytes.AsSpan(_availableStart);
 095        public Memory<byte> AvailableMemory => _bytes.AsMemory(_availableStart);
 096        public Memory<byte> AvailableMemorySliced(int length) => new Memory<byte>(_bytes, _availableStart, length);
 97
 098        public int Capacity => _bytes.Length;
 099        public int ActiveStartOffset => _activeStart;
 100
 0101        public byte[] DangerousGetUnderlyingBuffer() => _bytes;
 102
 103        public void Discard(int byteCount)
 0104        {
 0105            Debug.Assert(byteCount <= ActiveLength, $"Expected {byteCount} <= {ActiveLength}");
 0106            _activeStart += byteCount;
 107
 0108            if (_activeStart == _availableStart)
 0109            {
 0110                _activeStart = 0;
 0111                _availableStart = 0;
 0112            }
 0113        }
 114
 115        public void Commit(int byteCount)
 0116        {
 0117            Debug.Assert(byteCount <= AvailableLength);
 0118            _availableStart += byteCount;
 0119        }
 120
 121        // Ensure at least [byteCount] bytes to write to.
 122        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 123        public void EnsureAvailableSpace(int byteCount)
 0124        {
 0125            if (byteCount > AvailableLength)
 0126            {
 0127                EnsureAvailableSpaceCore(byteCount);
 0128            }
 0129        }
 130
 131        private void EnsureAvailableSpaceCore(int byteCount)
 0132        {
 0133            Debug.Assert(AvailableLength < byteCount);
 134
 0135            if (_bytes.Length == 0)
 0136            {
 0137                Debug.Assert(_usePool && _activeStart == 0 && _availableStart == 0);
 0138                _bytes = ArrayPool<byte>.Shared.Rent(byteCount);
 0139                return;
 140            }
 141
 0142            int totalFree = _activeStart + AvailableLength;
 0143            if (byteCount <= totalFree)
 0144            {
 145                // We can free up enough space by just shifting the bytes down, so do so.
 0146                Buffer.BlockCopy(_bytes, _activeStart, _bytes, 0, ActiveLength);
 0147                _availableStart = ActiveLength;
 0148                _activeStart = 0;
 0149                Debug.Assert(byteCount <= AvailableLength);
 0150                return;
 151            }
 152
 0153            int desiredSize = ActiveLength + byteCount;
 154
 0155            if ((uint)desiredSize > ArrayMaxLength)
 0156            {
 0157                throw new OutOfMemoryException();
 158            }
 159
 160            // Double the existing buffer size (capped at Array.MaxLength).
 0161            int newSize = Math.Max(desiredSize, (int)Math.Min(ArrayMaxLength, 2 * (uint)_bytes.Length));
 162
 0163            byte[] newBytes = _usePool ?
 0164                ArrayPool<byte>.Shared.Rent(newSize) :
 0165                new byte[newSize];
 0166            byte[] oldBytes = _bytes;
 167
 0168            if (ActiveLength != 0)
 0169            {
 0170                Buffer.BlockCopy(oldBytes, _activeStart, newBytes, 0, ActiveLength);
 0171            }
 172
 0173            _availableStart = ActiveLength;
 0174            _activeStart = 0;
 175
 0176            _bytes = newBytes;
 0177            ReturnBufferIfPooled(oldBytes);
 178
 0179            Debug.Assert(byteCount <= AvailableLength);
 0180        }
 181
 182        public void Grow()
 0183        {
 0184            EnsureAvailableSpaceCore(AvailableLength + 1);
 0185        }
 186
 187        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 188        private void ReturnBufferIfPooled(byte[] buffer)
 0189        {
 190            // The buffer may be Array.Empty<byte>()
 0191            if (_usePool && buffer.Length > 0)
 0192            {
 0193                ArrayPool<byte>.Shared.Return(buffer);
 0194            }
 0195        }
 196    }
 197}