< 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: 49_19945309242
Line coverage
91%
Covered lines: 101
Uncovered lines: 9
Coverable lines: 110
Total lines: 219
Line coverage: 91.8%
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%11100%
ToArray()100%22100%
Dispose()100%11100%

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
 980414    public int TotalBits { get; private set; }
 15
 60016    public BitWriter(int totalBits)
 17    {
 60018        if (totalBits < 0)
 019            throw new ArgumentOutOfRangeException(nameof(totalBits), "Must be >= 0.");
 20
 60021        TotalBits = totalBits;
 60022        var totalBytes = (totalBits + 7) / 8;
 60023        _buffer = ArrayPool<byte>.Shared.Rent(totalBytes);
 60024        Array.Clear(_buffer, 0, totalBytes);
 60025    }
 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    {
 182443        WriteBits(value, 0, bitLength);
 182444    }
 45
 46    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 47    public void WriteBits(ReadOnlySpan<byte> value, int valueOffset, int bitLength)
 48    {
 182449        if (_bitOffset + bitLength > TotalBits)
 050            throw new InvalidOperationException(
 051                $"Not enough bits to write {bitLength}. Offset={_bitOffset}, capacity={TotalBits}.");
 52
 182453        var byteOffset = _bitOffset / 8;
 182454        var shift = _bitOffset % 8;
 55
 182456        var bytesNeeded = (bitLength + 7) / 8;
 57
 182458        if (shift == 0)
 59        {
 1689660            for (var i = 0; i < bytesNeeded; i++)
 61            {
 775662                byte b = 0;
 775663                if (valueOffset + i < value.Length)
 761664                    b = value[valueOffset + i];
 65
 775666                _buffer[byteOffset + i] = b;
 67            }
 68        }
 69        else
 70        {
 2761671            for (var i = 0; i < bytesNeeded; i++)
 72            {
 1267673                byte current = 0;
 1267674                if (valueOffset + i < value.Length)
 1248075                    current = value[valueOffset + i];
 76
 1267677                var left = (byte)(current >> shift);
 1267678                _buffer[byteOffset + i] |= left;
 79
 1267680                var nextIndex = byteOffset + i + 1;
 81
 1267682                if (nextIndex >= _buffer.Length) continue;
 83
 1267684                var right = (byte)(current << (8 - shift));
 1267685                _buffer[nextIndex] |= right;
 86            }
 87        }
 88
 182489        _bitOffset += bitLength;
 182490    }
 91
 92    public void WriteBit(bool bit)
 93    {
 340094        if (_bitOffset >= TotalBits)
 495            throw new InvalidOperationException("No more bits to write.");
 96
 339697        var byteIndex = _bitOffset / 8;
 339698        var bitIndex = _bitOffset % 8;
 339699        var shift = 7 - bitIndex;
 100
 3396101        if (bit)
 284102            _buffer[byteIndex] |= (byte)(1 << shift);
 103        else
 3112104            _buffer[byteIndex] &= (byte)~(1 << shift);
 105
 3396106        _bitOffset++;
 3396107    }
 108
 109    public void WriteByteAsBits(byte value, int bits)
 110    {
 111        const byte byteBitSize = sizeof(byte) * 8;
 436112        if (bits is < 1 or > byteBitSize)
 0113            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {byteBitSize}.");
 114
 436115        var masked = value & (byte)((1 << bits) - 1);
 436116        var shifted = (byte)(masked << (byteBitSize - bits));
 117
 436118        Span<byte> bytes = stackalloc byte[sizeof(byte)];
 436119        bytes[0] = shifted;
 120
 436121        WriteBits(bytes, bits);
 436122    }
 123
 124    public void WriteInt16AsBits(short value, int bits, bool bigEndian = true)
 125    {
 126        const byte shortBitSize = sizeof(short) * 8;
 368127        if (bits is < 1 or > shortBitSize)
 0128            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {shortBitSize}.");
 129
 368130        var masked = value & (short)((1 >> bits) - 1);
 368131        var shifted = (short)(masked << (shortBitSize - bits));
 132
 368133        Span<byte> bytes = stackalloc byte[sizeof(short)];
 368134        BinaryPrimitives.WriteInt16LittleEndian(bytes, shifted);
 135
 368136        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 368137            bytes.Reverse();
 138
 368139        WriteBits(bytes, bits);
 368140    }
 141
 142    public void WriteUInt16AsBits(ushort value, int bits, bool bigEndian = true)
 143    {
 144        const byte ushortBitSize = sizeof(ushort) * 8;
 48145        if (bits is < 1 or > ushortBitSize)
 0146            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {ushortBitSize}.");
 147
 48148        var masked = value & (ushort)((1 << bits) - 1);
 48149        var shifted = (ushort)(masked << (ushortBitSize - bits));
 150
 48151        Span<byte> bytes = stackalloc byte[sizeof(ushort)];
 48152        BinaryPrimitives.WriteUInt16LittleEndian(bytes, shifted);
 153
 48154        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 48155            bytes.Reverse();
 156
 48157        WriteBits(bytes, bits);
 48158    }
 159
 160    public void WriteInt32AsBits(int value, int bits, bool bigEndian = true)
 161    {
 162        const byte intBitSize = sizeof(int) * 8;
 204163        if (bits is < 1 or > intBitSize)
 0164            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {intBitSize}.");
 165
 204166        var masked = value & (int)((1L << bits) - 1);
 204167        var shifted = masked << (intBitSize - bits);
 168
 204169        Span<byte> bytes = stackalloc byte[sizeof(int)];
 204170        BinaryPrimitives.WriteInt32LittleEndian(bytes, shifted);
 171
 204172        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 204173            bytes.Reverse();
 174
 204175        WriteBits(bytes, bits);
 204176    }
 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    {
 3232198        return _bitOffset + requiredBits <= TotalBits;
 199    }
 200
 201    public void SkipBits(int v)
 202    {
 576203        _bitOffset += v;
 576204    }
 205
 206    public byte[] ToArray()
 207    {
 656208        var bytes = new byte[(TotalBits + 7) / 8];
 60616209        for (var i = 0; i < bytes.Length; i++)
 29652210            bytes[i] = _buffer[i];
 211
 656212        return bytes;
 213    }
 214
 215    public void Dispose()
 216    {
 352217        ArrayPool<byte>.Shared.Return(_buffer);
 352218    }
 219}