< Summary

Line coverage
70%
Covered lines: 68
Uncovered lines: 28
Coverable lines: 96
Total lines: 202
Line coverage: 70.8%
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)

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
 1625        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)
 3139        {
 3140            Debug.Assert(initialSize > 0 || usePool);
 41
 3142            _usePool = usePool;
 3143            _bytes = initialSize == 0
 3144                ? Array.Empty<byte>()
 3145                : usePool ? ArrayPool<byte>.Shared.Rent(initialSize) : new byte[initialSize];
 3146            _activeStart = 0;
 3147            _availableStart = 0;
 3148        }
 49
 50        public ArrayBuffer(byte[] buffer)
 51        {
 52            Debug.Assert(buffer.Length > 0);
 53
 54            _usePool = false;
 55            _bytes = buffer;
 56            _activeStart = 0;
 57            _availableStart = 0;
 58        }
 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()
 3175        {
 3176            Debug.Assert(_usePool);
 3177            Debug.Assert(_bytes is not null);
 78
 3179            DiscardAll();
 80
 3181            byte[] bufferToReturn = _bytes;
 3182            _bytes = Array.Empty<byte>();
 3183            ReturnBufferIfPooled(bufferToReturn);
 3184        }
 85
 18586        public int ActiveLength => _availableStart - _activeStart;
 6887        public Span<byte> ActiveSpan => new Span<byte>(_bytes, _activeStart, _availableStart - _activeStart);
 088        public ReadOnlySpan<byte> ActiveReadOnlySpan => new ReadOnlySpan<byte>(_bytes, _activeStart, _availableStart - _
 89        public Memory<byte> ActiveMemory => new Memory<byte>(_bytes, _activeStart, _availableStart - _activeStart);
 90
 21691        public int AvailableLength => _bytes.Length - _availableStart;
 092        public Span<byte> AvailableSpan => _bytes.AsSpan(_availableStart);
 4593        public Memory<byte> AvailableMemory => _bytes.AsMemory(_availableStart);
 94        public Memory<byte> AvailableMemorySliced(int length) => new Memory<byte>(_bytes, _availableStart, length);
 95
 96        public int Capacity => _bytes.Length;
 97        public int ActiveStartOffset => _activeStart;
 98
 99        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()
 31114        {
 31115            _activeStart = 0;
 31116            _availableStart = 0;
 31117        }
 118
 119        public void Commit(int byteCount)
 31120        {
 31121            Debug.Assert(byteCount <= AvailableLength);
 31122            _availableStart += byteCount;
 31123        }
 124
 125        // Ensure at least [byteCount] bytes to write to.
 126        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 127        public void EnsureAvailableSpace(int byteCount)
 85128        {
 85129            if (byteCount > AvailableLength)
 39130            {
 39131                EnsureAvailableSpaceCore(byteCount);
 39132            }
 85133        }
 134
 135        private void EnsureAvailableSpaceCore(int byteCount)
 39136        {
 39137            Debug.Assert(AvailableLength < byteCount);
 138
 39139            if (_bytes.Length == 0)
 31140            {
 31141                Debug.Assert(_usePool && _activeStart == 0 && _availableStart == 0);
 31142                _bytes = ArrayPool<byte>.Shared.Rent(byteCount);
 31143                return;
 144            }
 145
 8146            int totalFree = _activeStart + AvailableLength;
 8147            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
 8157            int desiredSize = ActiveLength + byteCount;
 158
 8159            if ((uint)desiredSize > ArrayMaxLength)
 0160            {
 0161                throw new OutOfMemoryException();
 162            }
 163
 164            // Double the existing buffer size (capped at Array.MaxLength).
 8165            int newSize = Math.Max(desiredSize, (int)Math.Min(ArrayMaxLength, 2 * (uint)_bytes.Length));
 166
 8167            byte[] newBytes = _usePool ?
 8168                ArrayPool<byte>.Shared.Rent(newSize) :
 8169                new byte[newSize];
 8170            byte[] oldBytes = _bytes;
 171
 8172            if (ActiveLength != 0)
 8173            {
 8174                Buffer.BlockCopy(oldBytes, _activeStart, newBytes, 0, ActiveLength);
 8175            }
 176
 8177            _availableStart = ActiveLength;
 8178            _activeStart = 0;
 179
 8180            _bytes = newBytes;
 8181            ReturnBufferIfPooled(oldBytes);
 182
 8183            Debug.Assert(byteCount <= AvailableLength);
 39184        }
 185
 186        public void Grow()
 187        {
 188            EnsureAvailableSpaceCore(AvailableLength + 1);
 189        }
 190
 191        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 192        private void ReturnBufferIfPooled(byte[] buffer)
 39193        {
 194            // The buffer may be Array.Empty<byte>()
 39195            if (_usePool && buffer.Length > 0)
 39196            {
 39197                ArrayPool<byte>.Shared.Return(buffer);
 39198            }
 39199        }
 200    }
 201}
 202