< Summary - Combined Code Coverage

Information
Class: NLightning.Common.Utils.BitReader
Assembly: NLightning.Common
File(s): /home/runner/work/nlightning/nlightning/src/NLightning.Common/Utils/BitReader.cs
Tag: 30_15166811759
Line coverage
96%
Covered lines: 60
Uncovered lines: 2
Coverable lines: 62
Total lines: 162
Line coverage: 96.7%
Branch coverage
74%
Covered branches: 40
Total branches: 54
Branch coverage: 74%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%22100%
ReadBits(...)100%11100%
ReadBits(...)78.57%14.031494.74%
ReadBit()100%22100%
ReadByteFromBits(...)100%11100%
ReadInt16FromBits(...)100%88100%
ReadUInt16FromBits(...)62.5%88100%
ReadInt32FromBits(...)70%1010100%
ReadInt64FromBits(...)62.5%88100%
HasMoreBits(...)100%11100%
SkipBits(...)50%2.06275%

File(s)

/home/runner/work/nlightning/nlightning/src/NLightning.Common/Utils/BitReader.cs

#LineLine coverage
 1using System.Buffers.Binary;
 2using System.Runtime.CompilerServices;
 3
 4namespace NLightning.Common.Utils;
 5
 6using Domain.Serialization;
 7
 2448public class BitReader(byte[] buffer) : IBitReader
 9{
 24410    private readonly byte[] _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer));
 24411    private readonly int _totalBits = buffer.Length * 8;
 12
 13    private int _bitOffset;
 14
 15    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 16    public int ReadBits(Span<byte> value, int bitLength)
 17    {
 226818        return ReadBits(value, 0, bitLength);
 19    }
 20
 21    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 22    private int ReadBits(Span<byte> value, int valueOffset, int bitLength)
 23    {
 226824        if (_bitOffset + bitLength > _totalBits)
 25        {
 026            throw new InvalidOperationException($"Not enough bits left to read. Requested {bitLength}, but only {_totalB
 27        }
 28
 226829        var byteOffset = _bitOffset / 8;
 226830        var shift = (int)((uint)_bitOffset % 8);
 31
 226832        if (shift == 0)
 33        {
 34            // copy bytes to value
 52435            _buffer.AsSpan(byteOffset, bitLength / 8 + (bitLength % 8 == 0 ? 0 : 1)).CopyTo(value[valueOffset..]);
 36
 37            // mask extra bits
 52438            if (bitLength % 8 != 0)
 39            {
 46040                value[valueOffset + bitLength / 8] &= (byte)(0xFF << (8 - bitLength % 8));
 41            }
 42        }
 43        else
 44        {
 174445            var bytesToRead = bitLength / 8 + (shift > 0 ? 0 : -1);
 174446            var maxBytesToRead = value.Length - 1 - valueOffset;
 174447            if (bytesToRead > maxBytesToRead)
 48            {
 5249                bytesToRead = maxBytesToRead;
 50            }
 51
 1965652            for (var i = 0; i <= bytesToRead; i++)
 53            {
 808454                var left = (byte)(_buffer[byteOffset + i] << shift);
 808455                var right = (byte)((_buffer[byteOffset + i + 1] &
 808456                                    (i == bytesToRead + 1 ? 0xFF << (8 - bitLength % 8) : 0xFF)) >> (8 - shift));
 57
 808458                value[valueOffset + i] = (byte)(left | right);
 59            }
 60        }
 61
 226862        _bitOffset += bitLength;
 226863        return bitLength;
 64    }
 65
 66    public bool ReadBit()
 67    {
 284868        if (_bitOffset >= _totalBits)
 69        {
 470            throw new InvalidOperationException("No more bits to read.");
 71        }
 72
 284473        var byteIndex = _bitOffset / 8;
 284474        var bitIndex = _bitOffset % 8;
 75
 76        // Extract the bit at the current BitOffset
 284477        var bit = (_buffer[byteIndex] >> (7 - bitIndex)) & 1;
 78
 79        // Increment the BitOffset
 284480        _bitOffset++;
 81
 284482        return bit == 1;
 83    }
 84
 85    public byte ReadByteFromBits(int bits)
 86    {
 92087        Span<byte> bytes = stackalloc byte[sizeof(byte)];
 92088        ReadBits(bytes, bits);
 92089        var mask = (1 << bits) - 1;
 92090        return (byte)((bytes[0] >> (sizeof(byte) * 8 - bits)) & mask);
 91    }
 92
 93    public short ReadInt16FromBits(int bits, bool bigEndian = true)
 94    {
 87695        Span<byte> bytes = stackalloc byte[sizeof(short)];
 87696        ReadBits(bytes, bits);
 97
 87698        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 99        {
 872100            bytes.Reverse();
 101        }
 102
 876103        var mask = (1 << bits) - 1;
 876104        return (short)((BinaryPrimitives.ReadInt16LittleEndian(bytes) >> (sizeof(short) * 8 - bits)) & mask);
 105    }
 106
 107    public ushort ReadUInt16FromBits(int bits, bool bigEndian = true)
 108    {
 8109        Span<byte> bytes = stackalloc byte[sizeof(ushort)];
 8110        ReadBits(bytes, bits);
 111
 8112        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 113        {
 8114            bytes.Reverse();
 115        }
 116
 8117        var mask = (1 << bits) - 1;
 8118        return (ushort)((BinaryPrimitives.ReadUInt16LittleEndian(bytes) >> (sizeof(ushort) * 8 - bits)) & mask);
 119    }
 120
 121    public int ReadInt32FromBits(int bits, bool bigEndian = true)
 122    {
 64123        Span<byte> bytes = stackalloc byte[sizeof(int)];
 64124        ReadBits(bytes, bits);
 125
 64126        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 127        {
 64128            bytes.Reverse();
 129        }
 130
 64131        var mask = bits != 32 ? (1 << bits) - 1 : -1;
 64132        return (BinaryPrimitives.ReadInt32LittleEndian(bytes) >> (sizeof(int) * 8 - bits)) & mask;
 133    }
 134
 135    public long ReadInt64FromBits(int bits, bool bigEndian = true)
 136    {
 68137        Span<byte> bytes = stackalloc byte[sizeof(long)];
 68138        ReadBits(bytes, bits);
 139
 68140        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 141        {
 68142            bytes.Reverse();
 143        }
 144
 68145        return BinaryPrimitives.ReadInt64LittleEndian(bytes) >> (sizeof(long) * 8 - bits);
 146    }
 147
 148    public bool HasMoreBits(int requiredBits)
 149    {
 1288150        return _bitOffset + requiredBits <= _totalBits;
 151    }
 152
 153    public void SkipBits(int v)
 154    {
 32155        if (_bitOffset + v > _totalBits)
 156        {
 0157            throw new InvalidOperationException($"Cannot skip {v} bits, only {_totalBits - _bitOffset} remain.");
 158        }
 159
 32160        _bitOffset += v;
 32161    }
 162}