< Summary - Combined Code Coverage

Information
Class: NLightning.Domain.Utils.BitWriter
Assembly: NLightning.Domain
File(s): /home/runner/work/nlightning/nlightning/src/NLightning.Domain/Utils/BitWriter.cs
Tag: 36_15743069263
Line coverage
88%
Covered lines: 97
Uncovered lines: 13
Coverable lines: 110
Total lines: 219
Line coverage: 88.1%
Branch coverage
65%
Covered branches: 56
Total branches: 86
Branch coverage: 65.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_TotalBits()100%11100%
.ctor(...)50%2.01287.5%
GrowByBits(...)50%2.01287.5%
WriteBits(...)100%11100%
WriteBits(...)92.86%14.111491.67%
WriteBit(...)100%44100%
WriteByteAsBits(...)50%6.07687.5%
WriteInt16AsBits(...)57.14%14.21490%
WriteUInt16AsBits(...)57.14%14.21490%
WriteInt32AsBits(...)57.14%14.21490%
WriteInt64AsBits(...)57.14%14.21490%
HasMoreBits(...)100%11100%
SkipBits(...)100%210%
ToArray()100%22100%
Dispose()100%210%

File(s)

/home/runner/work/nlightning/nlightning/src/NLightning.Domain/Utils/BitWriter.cs

#LineLine coverage
 1using System.Buffers;
 2using System.Buffers.Binary;
 3using System.Runtime.CompilerServices;
 4
 5namespace NLightning.Domain.Utils;
 6
 7using Interfaces;
 8
 9public class BitWriter : IBitWriter
 10{
 11    private int _bitOffset;
 12    private byte[] _buffer;
 13
 642814    public int TotalBits { get; private set; }
 15
 22816    public BitWriter(int totalBits)
 17    {
 22818        if (totalBits < 0)
 019            throw new ArgumentOutOfRangeException(nameof(totalBits), "Must be >= 0.");
 20
 22821        TotalBits = totalBits;
 22822        var totalBytes = (totalBits + 7) / 8;
 22823        _buffer = ArrayPool<byte>.Shared.Rent(totalBytes);
 22824        Array.Clear(_buffer, 0, totalBytes);
 22825    }
 26
 27    public void GrowByBits(int additionalBits)
 28    {
 429        var requiredBits = _bitOffset + additionalBits;
 430        if (requiredBits <= TotalBits)
 031            return;
 32
 433        var newTotalBits = Math.Max(TotalBits * 2, requiredBits);
 434        var newByteCount = (newTotalBits + 7) / 8;
 35
 436        Array.Resize(ref _buffer, newByteCount);
 437        TotalBits = requiredBits;
 438    }
 39
 40    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 41    public void WriteBits(ReadOnlySpan<byte> value, int bitLength)
 42    {
 120043        WriteBits(value, 0, bitLength);
 120044    }
 45
 46    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 47    public void WriteBits(ReadOnlySpan<byte> value, int valueOffset, int bitLength)
 48    {
 120049        if (_bitOffset + bitLength > TotalBits)
 050            throw new InvalidOperationException(
 051                $"Not enough bits to write {bitLength}. Offset={_bitOffset}, capacity={TotalBits}.");
 52
 120053        var byteOffset = _bitOffset / 8;
 120054        var shift = _bitOffset % 8;
 55
 120056        var bytesNeeded = (bitLength + 7) / 8;
 57
 120058        if (shift == 0)
 59        {
 730460            for (var i = 0; i < bytesNeeded; i++)
 61            {
 324462                byte b = 0;
 324463                if (valueOffset + i < value.Length)
 318464                    b = value[valueOffset + i];
 65
 324466                _buffer[byteOffset + i] = b;
 67            }
 68        }
 69        else
 70        {
 1520071            for (var i = 0; i < bytesNeeded; i++)
 72            {
 680873                byte current = 0;
 680874                if (valueOffset + i < value.Length)
 669675                    current = value[valueOffset + i];
 76
 680877                var left = (byte)(current >> shift);
 680878                _buffer[byteOffset + i] |= left;
 79
 680880                var nextIndex = byteOffset + i + 1;
 81
 680882                if (nextIndex >= _buffer.Length) continue;
 83
 680884                var right = (byte)(current << (8 - shift));
 680885                _buffer[nextIndex] |= right;
 86            }
 87        }
 88
 120089        _bitOffset += bitLength;
 120090    }
 91
 92    public void WriteBit(bool bit)
 93    {
 235294        if (_bitOffset >= TotalBits)
 495            throw new InvalidOperationException("No more bits to write.");
 96
 234897        var byteIndex = _bitOffset / 8;
 234898        var bitIndex = _bitOffset % 8;
 234899        var shift = 7 - bitIndex;
 100
 2348101        if (bit)
 164102            _buffer[byteIndex] |= (byte)(1 << shift);
 103        else
 2184104            _buffer[byteIndex] &= (byte)~(1 << shift);
 105
 2348106        _bitOffset++;
 2348107    }
 108
 109    public void WriteByteAsBits(byte value, int bits)
 110    {
 111        const byte byteBitSize = sizeof(byte) * 8;
 340112        if (bits is < 1 or > byteBitSize)
 0113            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {byteBitSize}.");
 114
 340115        var masked = value & (byte)((1 << bits) - 1);
 340116        var shifted = (byte)(masked << (byteBitSize - bits));
 117
 340118        Span<byte> bytes = stackalloc byte[sizeof(byte)];
 340119        bytes[0] = shifted;
 120
 340121        WriteBits(bytes, bits);
 340122    }
 123
 124    public void WriteInt16AsBits(short value, int bits, bool bigEndian = true)
 125    {
 126        const byte shortBitSize = sizeof(short) * 8;
 324127        if (bits is < 1 or > shortBitSize)
 0128            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {shortBitSize}.");
 129
 324130        var masked = value & (short)((1 >> bits) - 1);
 324131        var shifted = (short)(masked << (shortBitSize - bits));
 132
 324133        Span<byte> bytes = stackalloc byte[sizeof(short)];
 324134        BinaryPrimitives.WriteInt16LittleEndian(bytes, shifted);
 135
 324136        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 324137            bytes.Reverse();
 138
 324139        WriteBits(bytes, bits);
 324140    }
 141
 142    public void WriteUInt16AsBits(ushort value, int bits, bool bigEndian = true)
 143    {
 144        const byte ushortBitSize = sizeof(ushort) * 8;
 24145        if (bits is < 1 or > ushortBitSize)
 0146            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {ushortBitSize}.");
 147
 24148        var masked = value & (ushort)((1 << bits) - 1);
 24149        var shifted = (ushort)(masked << (ushortBitSize - bits));
 150
 24151        Span<byte> bytes = stackalloc byte[sizeof(ushort)];
 24152        BinaryPrimitives.WriteUInt16LittleEndian(bytes, shifted);
 153
 24154        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 24155            bytes.Reverse();
 156
 24157        WriteBits(bytes, bits);
 24158    }
 159
 160    public void WriteInt32AsBits(int value, int bits, bool bigEndian = true)
 161    {
 162        const byte intBitSize = sizeof(int) * 8;
 92163        if (bits is < 1 or > intBitSize)
 0164            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {intBitSize}.");
 165
 92166        var masked = value & (int)((1L << bits) - 1);
 92167        var shifted = masked << (intBitSize - bits);
 168
 92169        Span<byte> bytes = stackalloc byte[sizeof(int)];
 92170        BinaryPrimitives.WriteInt32LittleEndian(bytes, shifted);
 171
 92172        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 92173            bytes.Reverse();
 174
 92175        WriteBits(bytes, bits);
 92176    }
 177
 178    public void WriteInt64AsBits(long value, int bits, bool bigEndian = true)
 179    {
 180        const byte longBitSize = sizeof(long) * 8;
 72181        if (bits is < 1 or > longBitSize)
 0182            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {longBitSize}.");
 183
 72184        var masked = value & (long)((1UL << bits) - 1);
 72185        var shifted = masked << (longBitSize - bits);
 186
 72187        Span<byte> bytes = stackalloc byte[sizeof(long)];
 72188        BinaryPrimitives.WriteInt64LittleEndian(bytes, shifted);
 189
 72190        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 72191            bytes.Reverse();
 192
 72193        WriteBits(bytes, bits);
 72194    }
 195
 196    public bool HasMoreBits(int requiredBits)
 197    {
 2272198        return _bitOffset + requiredBits <= TotalBits;
 199    }
 200
 201    public void SkipBits(int v)
 202    {
 0203        _bitOffset += v;
 0204    }
 205
 206    public byte[] ToArray()
 207    {
 284208        var bytes = new byte[(TotalBits + 7) / 8];
 37648209        for (var i = 0; i < bytes.Length; i++)
 18540210            bytes[i] = _buffer[i];
 211
 284212        return bytes;
 213    }
 214
 215    public void Dispose()
 216    {
 0217        ArrayPool<byte>.Shared.Return(_buffer);
 0218    }
 219}