< 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: 44_19672976073
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
 2608public class BitReader(byte[] buffer) : IBitReader
 9{
 26010    private readonly byte[] _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer));
 26011    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    {
 239218        return ReadBits(value, 0, bitLength);
 19    }
 20
 21    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 22    private int ReadBits(Span<byte> value, int valueOffset, int bitLength)
 23    {
 239224        ArgumentOutOfRangeException.ThrowIfNegative(bitLength);
 25
 239226        if (_bitOffset + bitLength > _totalBits)
 027            throw new InvalidOperationException(
 028                $"Not enough bits left to read. Requested {bitLength}, but only {_totalBits - _bitOffset} remain.");
 29
 239230        if (bitLength == 0)
 031            return 0;
 32
 239233        var byteOffset = _bitOffset / 8;
 239234        var shift = (int)((uint)_bitOffset % 8);
 35
 239236        if (shift == 0)
 37        {
 38            // copy bytes to value
 54439            _buffer.AsSpan(byteOffset, bitLength / 8 + (bitLength % 8 == 0 ? 0 : 1)).CopyTo(value[valueOffset..]);
 40
 41            // mask extra bits
 54442            if (bitLength % 8 != 0)
 48043                value[valueOffset + bitLength / 8] &= (byte)(0xFF << (8 - bitLength % 8));
 44        }
 45        else
 46        {
 184847            var bytesToRead = bitLength / 8 + (shift > 0 ? 0 : -1);
 184848            var maxBytesToRead = value.Length - 1 - valueOffset;
 184849            if (bytesToRead > maxBytesToRead)
 5250                bytesToRead = maxBytesToRead;
 51
 2068852            for (var i = 0; i <= bytesToRead; i++)
 53            {
 849654                var left = (byte)(_buffer[byteOffset + i] << shift);
 849655                var right = 0;
 56
 849657                if (byteOffset + i + 1 < _buffer.Length)
 848858                    right = (byte)((_buffer[byteOffset + i + 1]
 848859                                  & (i == bytesToRead + 1
 848860                                         ? 0xFF << (8 - bitLength % 8)
 848861                                         : 0xFF)
 848862                                   ) >> (8 - shift));
 63
 849664                value[valueOffset + i] = (byte)(left | right);
 65            }
 66        }
 67
 239268        _bitOffset += bitLength;
 239269        return bitLength;
 70    }
 71
 72    public bool ReadBit()
 73    {
 296874        if (_bitOffset >= _totalBits)
 475            throw new InvalidOperationException("No more bits to read.");
 76
 296477        var byteIndex = _bitOffset / 8;
 296478        var bitIndex = _bitOffset % 8;
 79
 80        // Extract the bit at the current BitOffset
 296481        var bit = (_buffer[byteIndex] >> (7 - bitIndex)) & 1;
 82
 83        // Increment the BitOffset
 296484        _bitOffset++;
 85
 296486        return bit == 1;
 87    }
 88
 89    public byte ReadByteFromBits(int bits)
 90    {
 96891        Span<byte> bytes = stackalloc byte[sizeof(byte)];
 96892        ReadBits(bytes, bits);
 96893        var mask = (1 << bits) - 1;
 96894        return (byte)((bytes[0] >> (sizeof(byte) * 8 - bits)) & mask);
 95    }
 96
 97    public short ReadInt16FromBits(int bits, bool bigEndian = true)
 98    {
 92899        Span<byte> bytes = stackalloc byte[sizeof(short)];
 928100        ReadBits(bytes, bits);
 101
 928102        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 924103            bytes.Reverse();
 104
 928105        var mask = (1 << bits) - 1;
 928106        return (short)((BinaryPrimitives.ReadInt16LittleEndian(bytes) >> (sizeof(short) * 8 - bits)) & mask);
 107    }
 108
 109    public ushort ReadUInt16FromBits(int bits, bool bigEndian = true)
 110    {
 12111        Span<byte> bytes = stackalloc byte[sizeof(ushort)];
 12112        ReadBits(bytes, bits);
 113
 12114        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 12115            bytes.Reverse();
 116
 12117        var mask = (1 << bits) - 1;
 12118        return (ushort)((BinaryPrimitives.ReadUInt16LittleEndian(bytes) >> (sizeof(ushort) * 8 - bits)) & mask);
 119    }
 120
 121    public int ReadInt32FromBits(int bits, bool bigEndian = true)
 122    {
 68123        Span<byte> bytes = stackalloc byte[sizeof(int)];
 68124        ReadBits(bytes, bits);
 125
 68126        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 68127            bytes.Reverse();
 128
 68129        var mask = bits != 32 ? (1 << bits) - 1 : -1;
 68130        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    {
 564153        if (_bitOffset + v > _totalBits)
 0154            throw new InvalidOperationException($"Cannot skip {v} bits, only {_totalBits - _bitOffset} remain.");
 155
 564156        _bitOffset += v;
 564157    }
 158}