< 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: 36_15743069263
Line coverage
95%
Covered lines: 60
Uncovered lines: 3
Coverable lines: 63
Total lines: 163
Line coverage: 95.2%
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.21490%
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
 2488public class BitReader(byte[] buffer) : IBitReader
 9{
 24810    private readonly byte[] _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer));
 24811    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    {
 231618        return ReadBits(value, 0, bitLength);
 19    }
 20
 21    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 22    private int ReadBits(Span<byte> value, int valueOffset, int bitLength)
 23    {
 231624        if (_bitOffset + bitLength > _totalBits)
 25        {
 026            throw new InvalidOperationException(
 027                $"Not enough bits left to read. Requested {bitLength}, but only {_totalBits - _bitOffset} remain.");
 28        }
 29
 231630        var byteOffset = _bitOffset / 8;
 231631        var shift = (int)((uint)_bitOffset % 8);
 32
 231633        if (shift == 0)
 34        {
 35            // copy bytes to value
 53636            _buffer.AsSpan(byteOffset, bitLength / 8 + (bitLength % 8 == 0 ? 0 : 1)).CopyTo(value[valueOffset..]);
 37
 38            // mask extra bits
 53639            if (bitLength % 8 != 0)
 40            {
 47241                value[valueOffset + bitLength / 8] &= (byte)(0xFF << (8 - bitLength % 8));
 42            }
 43        }
 44        else
 45        {
 178046            var bytesToRead = bitLength / 8 + (shift > 0 ? 0 : -1);
 178047            var maxBytesToRead = value.Length - 1 - valueOffset;
 178048            if (bytesToRead > maxBytesToRead)
 49            {
 5250                bytesToRead = maxBytesToRead;
 51            }
 52
 2008053            for (var i = 0; i <= bytesToRead; i++)
 54            {
 826055                var left = (byte)(_buffer[byteOffset + i] << shift);
 826056                var right = (byte)((_buffer[byteOffset + i + 1] &
 826057                                    (i == bytesToRead + 1 ? 0xFF << (8 - bitLength % 8) : 0xFF)) >> (8 - shift));
 58
 826059                value[valueOffset + i] = (byte)(left | right);
 60            }
 61        }
 62
 231663        _bitOffset += bitLength;
 231664        return bitLength;
 65    }
 66
 67    public bool ReadBit()
 68    {
 284869        if (_bitOffset >= _totalBits)
 70        {
 471            throw new InvalidOperationException("No more bits to read.");
 72        }
 73
 284474        var byteIndex = _bitOffset / 8;
 284475        var bitIndex = _bitOffset % 8;
 76
 77        // Extract the bit at the current BitOffset
 284478        var bit = (_buffer[byteIndex] >> (7 - bitIndex)) & 1;
 79
 80        // Increment the BitOffset
 284481        _bitOffset++;
 82
 284483        return bit == 1;
 84    }
 85
 86    public byte ReadByteFromBits(int bits)
 87    {
 94088        Span<byte> bytes = stackalloc byte[sizeof(byte)];
 94089        ReadBits(bytes, bits);
 94090        var mask = (1 << bits) - 1;
 94091        return (byte)((bytes[0] >> (sizeof(byte) * 8 - bits)) & mask);
 92    }
 93
 94    public short ReadInt16FromBits(int bits, bool bigEndian = true)
 95    {
 89696        Span<byte> bytes = stackalloc byte[sizeof(short)];
 89697        ReadBits(bytes, bits);
 98
 89699        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 100        {
 892101            bytes.Reverse();
 102        }
 103
 896104        var mask = (1 << bits) - 1;
 896105        return (short)((BinaryPrimitives.ReadInt16LittleEndian(bytes) >> (sizeof(short) * 8 - bits)) & mask);
 106    }
 107
 108    public ushort ReadUInt16FromBits(int bits, bool bigEndian = true)
 109    {
 8110        Span<byte> bytes = stackalloc byte[sizeof(ushort)];
 8111        ReadBits(bytes, bits);
 112
 8113        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 114        {
 8115            bytes.Reverse();
 116        }
 117
 8118        var mask = (1 << bits) - 1;
 8119        return (ushort)((BinaryPrimitives.ReadUInt16LittleEndian(bytes) >> (sizeof(ushort) * 8 - bits)) & mask);
 120    }
 121
 122    public int ReadInt32FromBits(int bits, bool bigEndian = true)
 123    {
 64124        Span<byte> bytes = stackalloc byte[sizeof(int)];
 64125        ReadBits(bytes, bits);
 126
 64127        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 128        {
 64129            bytes.Reverse();
 130        }
 131
 64132        var mask = bits != 32 ? (1 << bits) - 1 : -1;
 64133        return (BinaryPrimitives.ReadInt32LittleEndian(bytes) >> (sizeof(int) * 8 - bits)) & mask;
 134    }
 135
 136    public long ReadInt64FromBits(int bits, bool bigEndian = true)
 137    {
 72138        Span<byte> bytes = stackalloc byte[sizeof(long)];
 72139        ReadBits(bytes, bits);
 140
 72141        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 142        {
 72143            bytes.Reverse();
 144        }
 145
 72146        return BinaryPrimitives.ReadInt64LittleEndian(bytes) >> (sizeof(long) * 8 - bits);
 147    }
 148
 149    public bool HasMoreBits(int requiredBits)
 150    {
 1316151        return _bitOffset + requiredBits <= _totalBits;
 152    }
 153
 154    public void SkipBits(int v)
 155    {
 32156        if (_bitOffset + v > _totalBits)
 157        {
 0158            throw new InvalidOperationException($"Cannot skip {v} bits, only {_totalBits - _bitOffset} remain.");
 159        }
 160
 32161        _bitOffset += v;
 32162    }
 163}