< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 111
Coverable lines: 111
Total lines: 202
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%
DiscardAll()100%110%
Commit(...)100%110%
EnsureAvailableSpace(...)0%220%
EnsureAvailableSpaceCore(...)0%14140%
Grow()100%110%
ReturnBufferIfPooled(...)0%440%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/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            DiscardAll();
 63
 064            byte[] array = _bytes;
 065            _bytes = null!;
 66
 067            if (array is not null)
 068            {
 069                ReturnBufferIfPooled(array);
 070            }
 071        }
 72
 73        // This is different from Dispose as the instance remains usable afterwards (_bytes will not be null).
 74        public void ClearAndReturnBuffer()
 075        {
 076            Debug.Assert(_usePool);
 077            Debug.Assert(_bytes is not null);
 78
 079            DiscardAll();
 80
 081            byte[] bufferToReturn = _bytes;
 082            _bytes = Array.Empty<byte>();
 083            ReturnBufferIfPooled(bufferToReturn);
 084        }
 85
 086        public int ActiveLength => _availableStart - _activeStart;
 087        public Span<byte> ActiveSpan => new Span<byte>(_bytes, _activeStart, _availableStart - _activeStart);
 088        public ReadOnlySpan<byte> ActiveReadOnlySpan => new ReadOnlySpan<byte>(_bytes, _activeStart, _availableStart - _
 089        public Memory<byte> ActiveMemory => new Memory<byte>(_bytes, _activeStart, _availableStart - _activeStart);
 90
 091        public int AvailableLength => _bytes.Length - _availableStart;
 092        public Span<byte> AvailableSpan => _bytes.AsSpan(_availableStart);
 093        public Memory<byte> AvailableMemory => _bytes.AsMemory(_availableStart);
 094        public Memory<byte> AvailableMemorySliced(int length) => new Memory<byte>(_bytes, _availableStart, length);
 95
 096        public int Capacity => _bytes.Length;
 097        public int ActiveStartOffset => _activeStart;
 98
 099        public byte[] DangerousGetUnderlyingBuffer() => _bytes;
 100
 101        public void Discard(int byteCount)
 0102        {
 0103            Debug.Assert(byteCount <= ActiveLength, $"Expected {byteCount} <= {ActiveLength}");
 0104            _activeStart += byteCount;
 105
 0106            if (_activeStart == _availableStart)
 0107            {
 0108                _activeStart = 0;
 0109                _availableStart = 0;
 0110            }
 0111        }
 112
 113        public void DiscardAll()
 0114        {
 0115            _activeStart = 0;
 0116            _availableStart = 0;
 0117        }
 118
 119        public void Commit(int byteCount)
 0120        {
 0121            Debug.Assert(byteCount <= AvailableLength);
 0122            _availableStart += byteCount;
 0123        }
 124
 125        // Ensure at least [byteCount] bytes to write to.
 126        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 127        public void EnsureAvailableSpace(int byteCount)
 0128        {
 0129            if (byteCount > AvailableLength)
 0130            {
 0131                EnsureAvailableSpaceCore(byteCount);
 0132            }
 0133        }
 134
 135        private void EnsureAvailableSpaceCore(int byteCount)
 0136        {
 0137            Debug.Assert(AvailableLength < byteCount);
 138
 0139            if (_bytes.Length == 0)
 0140            {
 0141                Debug.Assert(_usePool && _activeStart == 0 && _availableStart == 0);
 0142                _bytes = ArrayPool<byte>.Shared.Rent(byteCount);
 0143                return;
 144            }
 145
 0146            int totalFree = _activeStart + AvailableLength;
 0147            if (byteCount <= totalFree)
 0148            {
 149                // We can free up enough space by just shifting the bytes down, so do so.
 0150                Buffer.BlockCopy(_bytes, _activeStart, _bytes, 0, ActiveLength);
 0151                _availableStart = ActiveLength;
 0152                _activeStart = 0;
 0153                Debug.Assert(byteCount <= AvailableLength);
 0154                return;
 155            }
 156
 0157            int desiredSize = ActiveLength + byteCount;
 158
 0159            if ((uint)desiredSize > ArrayMaxLength)
 0160            {
 0161                throw new OutOfMemoryException();
 162            }
 163
 164            // Double the existing buffer size (capped at Array.MaxLength).
 0165            int newSize = Math.Max(desiredSize, (int)Math.Min(ArrayMaxLength, 2 * (uint)_bytes.Length));
 166
 0167            byte[] newBytes = _usePool ?
 0168                ArrayPool<byte>.Shared.Rent(newSize) :
 0169                new byte[newSize];
 0170            byte[] oldBytes = _bytes;
 171
 0172            if (ActiveLength != 0)
 0173            {
 0174                Buffer.BlockCopy(oldBytes, _activeStart, newBytes, 0, ActiveLength);
 0175            }
 176
 0177            _availableStart = ActiveLength;
 0178            _activeStart = 0;
 179
 0180            _bytes = newBytes;
 0181            ReturnBufferIfPooled(oldBytes);
 182
 0183            Debug.Assert(byteCount <= AvailableLength);
 0184        }
 185
 186        public void Grow()
 0187        {
 0188            EnsureAvailableSpaceCore(AvailableLength + 1);
 0189        }
 190
 191        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 192        private void ReturnBufferIfPooled(byte[] buffer)
 0193        {
 194            // The buffer may be Array.Empty<byte>()
 0195            if (_usePool && buffer.Length > 0)
 0196            {
 0197                ArrayPool<byte>.Shared.Return(buffer);
 0198            }
 0199        }
 200    }
 201}
 202