< Summary - Combined Code Coverage

Information
Class: NLightning.Domain.Utils.BitReader
Assembly: NLightning.Domain
File(s): /home/runner/work/nlightning/nlightning/src/NLightning.Domain/Utils/BitReader.cs
Tag: 49_19945309242
Line coverage
94%
Covered lines: 68
Uncovered lines: 4
Coverable lines: 72
Total lines: 158
Line coverage: 94.4%
Branch coverage
74%
Covered branches: 43
Total branches: 58
Branch coverage: 74.1%
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(...)77.78%18.41889.29%
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.Domain/Utils/BitReader.cs

#LineLine coverage
 1using System.Buffers.Binary;
 2using System.Runtime.CompilerServices;
 3
 4namespace NLightning.Domain.Utils;
 5
 6using Interfaces;
 7
 6288public class BitReader(byte[] buffer) : IBitReader
 9{
 62810    private readonly byte[] _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer));
 62811    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    {
 300818        return ReadBits(value, 0, bitLength);
 19    }
 20
 21    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 22    private int ReadBits(Span<byte> value, int valueOffset, int bitLength)
 23    {
 300824        ArgumentOutOfRangeException.ThrowIfNegative(bitLength);
 25
 300826        if (_bitOffset + bitLength > _totalBits)
 027            throw new InvalidOperationException(
 028                $"Not enough bits left to read. Requested {bitLength}, but only {_totalBits - _bitOffset} remain.");
 29
 300830        if (bitLength == 0)
 031            return 0;
 32
 300833        var byteOffset = _bitOffset / 8;
 300834        var shift = (int)((uint)_bitOffset % 8);
 35
 300836        if (shift == 0)
 37        {
 38            // copy bytes to value
 82039            _buffer.AsSpan(byteOffset, bitLength / 8 + (bitLength % 8 == 0 ? 0 : 1)).CopyTo(value[valueOffset..]);
 40
 41            // mask extra bits
 82042            if (bitLength % 8 != 0)
 63643                value[valueOffset + bitLength / 8] &= (byte)(0xFF << (8 - bitLength % 8));
 44        }
 45        else
 46        {
 218847            var bytesToRead = bitLength / 8 + (shift > 0 ? 0 : -1);
 218848            var maxBytesToRead = value.Length - 1 - valueOffset;
 218849            if (bytesToRead > maxBytesToRead)
 18450                bytesToRead = maxBytesToRead;
 51
 3318452            for (var i = 0; i <= bytesToRead; i++)
 53            {
 1440454                var left = (byte)(_buffer[byteOffset + i] << shift);
 1440455                var right = 0;
 56
 1440457                if (byteOffset + i + 1 < _buffer.Length)
 1438858                    right = (byte)((_buffer[byteOffset + i + 1]
 1438859                                  & (i == bytesToRead + 1
 1438860                                         ? 0xFF << (8 - bitLength % 8)
 1438861                                         : 0xFF)
 1438862                                   ) >> (8 - shift));
 63
 1440464                value[valueOffset + i] = (byte)(left | right);
 65            }
 66        }
 67
 300868        _bitOffset += bitLength;
 300869        return bitLength;
 70    }
 71
 72    public bool ReadBit()
 73    {
 392874        if (_bitOffset >= _totalBits)
 475            throw new InvalidOperationException("No more bits to read.");
 76
 392477        var byteIndex = _bitOffset / 8;
 392478        var bitIndex = _bitOffset % 8;
 79
 80        // Extract the bit at the current BitOffset
 392481        var bit = (_buffer[byteIndex] >> (7 - bitIndex)) & 1;
 82
 83        // Increment the BitOffset
 392484        _bitOffset++;
 85
 392486        return bit == 1;
 87    }
 88
 89    public byte ReadByteFromBits(int bits)
 90    {
 106491        Span<byte> bytes = stackalloc byte[sizeof(byte)];
 106492        ReadBits(bytes, bits);
 106493        var mask = (1 << bits) - 1;
 106494        return (byte)((bytes[0] >> (sizeof(byte) * 8 - bits)) & mask);
 95    }
 96
 97    public short ReadInt16FromBits(int bits, bool bigEndian = true)
 98    {
 97299        Span<byte> bytes = stackalloc byte[sizeof(short)];
 972100        ReadBits(bytes, bits);
 101
 972102        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 968103            bytes.Reverse();
 104
 972105        var mask = (1 << bits) - 1;
 972106        return (short)((BinaryPrimitives.ReadInt16LittleEndian(bytes) >> (sizeof(short) * 8 - bits)) & mask);
 107    }
 108
 109    public ushort ReadUInt16FromBits(int bits, bool bigEndian = true)
 110    {
 36111        Span<byte> bytes = stackalloc byte[sizeof(ushort)];
 36112        ReadBits(bytes, bits);
 113
 36114        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 36115            bytes.Reverse();
 116
 36117        var mask = (1 << bits) - 1;
 36118        return (ushort)((BinaryPrimitives.ReadUInt16LittleEndian(bytes) >> (sizeof(ushort) * 8 - bits)) & mask);
 119    }
 120
 121    public int ReadInt32FromBits(int bits, bool bigEndian = true)
 122    {
 180123        Span<byte> bytes = stackalloc byte[sizeof(int)];
 180124        ReadBits(bytes, bits);
 125
 180126        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 180127            bytes.Reverse();
 128
 180129        var mask = bits != 32 ? (1 << bits) - 1 : -1;
 180130        return (BinaryPrimitives.ReadInt32LittleEndian(bytes) >> (sizeof(int) * 8 - bits)) & mask;
 131    }
 132
 133    public long ReadInt64FromBits(int bits, bool bigEndian = true)
 134    {
 72135        Span<byte> bytes = stackalloc byte[sizeof(long)];
 72136        ReadBits(bytes, bits);
 137
 72138        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 72139            bytes.Reverse();
 140
 72141        return BinaryPrimitives.ReadInt64LittleEndian(bytes) >> (sizeof(long) * 8 - bits);
 142    }
 143
 144    public bool HasMoreBits(int requiredBits)
 145    {
 1376146        ArgumentOutOfRangeException.ThrowIfNegative(requiredBits);
 147
 1376148        return _bitOffset + requiredBits <= _totalBits;
 149    }
 150
 151    public void SkipBits(int v)
 152    {
 892153        if (_bitOffset + v > _totalBits)
 0154            throw new InvalidOperationException($"Cannot skip {v} bits, only {_totalBits - _bitOffset} remain.");
 155
 892156        _bitOffset += v;
 892157    }
 158}