< Summary - Combined Code Coverage

Information
Class: NLightning.Common.Utils.BitWriter
Assembly: NLightning.Common
File(s): /home/runner/work/nlightning/nlightning/src/NLightning.Common/Utils/BitWriter.cs
Tag: 30_15166811759
Line coverage
90%
Covered lines: 96
Uncovered lines: 10
Coverable lines: 106
Total lines: 231
Line coverage: 90.5%
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.01285.71%
GrowByBits(...)50%2.01287.5%
WriteBits(...)100%11100%
WriteBits(...)92.86%14.021495.65%
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%

File(s)

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

#LineLine coverage
 1using System.Buffers.Binary;
 2using System.Runtime.CompilerServices;
 3
 4namespace NLightning.Common.Utils;
 5
 6using Domain.Serialization;
 7
 8public class BitWriter : IBitWriter
 9{
 10    private int _bitOffset;
 11    private byte[] _buffer;
 12
 642813    public int TotalBits { get; private set; }
 14
 22815    public BitWriter(int totalBits)
 16    {
 22817        if (totalBits < 0)
 018            throw new ArgumentOutOfRangeException(nameof(totalBits), "Must be >= 0.");
 19
 22820        TotalBits = totalBits;
 22821        var totalBytes = (totalBits + 7) / 8;
 22822        _buffer = new byte[totalBytes];
 22823    }
 24
 25    public void GrowByBits(int additionalBits)
 26    {
 427        var requiredBits = _bitOffset + additionalBits;
 428        if (requiredBits <= TotalBits)
 29        {
 030            return;
 31        }
 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)
 50        {
 051            throw new InvalidOperationException($"Not enough bits to write {bitLength}. Offset={_bitOffset}, capacity={T
 52        }
 53
 120054        var byteOffset = _bitOffset / 8;
 120055        var shift = _bitOffset % 8;
 56
 120057        var bytesNeeded = (bitLength + 7) / 8;
 58
 120059        if (shift == 0)
 60        {
 730461            for (var i = 0; i < bytesNeeded; i++)
 62            {
 324463                byte b = 0;
 324464                if (valueOffset + i < value.Length)
 318465                    b = value[valueOffset + i];
 66
 324467                _buffer[byteOffset + i] = b;
 68            }
 69        }
 70        else
 71        {
 1520072            for (var i = 0; i < bytesNeeded; i++)
 73            {
 680874                byte current = 0;
 680875                if (valueOffset + i < value.Length)
 669676                    current = value[valueOffset + i];
 77
 680878                var left = (byte)(current >> shift);
 680879                _buffer[byteOffset + i] |= left;
 80
 680881                var nextIndex = byteOffset + i + 1;
 82
 680883                if (nextIndex >= _buffer.Length) continue;
 84
 680885                var right = (byte)(current << (8 - shift));
 680886                _buffer[nextIndex] |= right;
 87            }
 88        }
 89
 120090        _bitOffset += bitLength;
 120091    }
 92
 93    public void WriteBit(bool bit)
 94    {
 235295        if (_bitOffset >= TotalBits)
 96        {
 497            throw new InvalidOperationException("No more bits to write.");
 98        }
 99
 2348100        var byteIndex = _bitOffset / 8;
 2348101        var bitIndex = _bitOffset % 8;
 2348102        var shift = 7 - bitIndex;
 103
 2348104        if (bit)
 105        {
 164106            _buffer[byteIndex] |= (byte)(1 << shift);
 107        }
 108        else
 109        {
 2184110            _buffer[byteIndex] &= (byte)~(1 << shift);
 111        }
 112
 2348113        _bitOffset++;
 2348114    }
 115
 116    public void WriteByteAsBits(byte value, int bits)
 117    {
 118        const byte BYTE_BIT_SIZE = sizeof(byte) * 8;
 340119        if (bits is < 1 or > BYTE_BIT_SIZE)
 0120            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {BYTE_BIT_SIZE}.");
 121
 340122        var masked = value & (byte)((1 << bits) - 1);
 340123        var shifted = (byte)(masked << (BYTE_BIT_SIZE - bits));
 124
 340125        Span<byte> bytes = stackalloc byte[sizeof(byte)];
 340126        bytes[0] = shifted;
 127
 340128        WriteBits(bytes, bits);
 340129    }
 130
 131    public void WriteInt16AsBits(short value, int bits, bool bigEndian = true)
 132    {
 133        const byte SHORT_BIT_SIZE = sizeof(short) * 8;
 324134        if (bits is < 1 or > SHORT_BIT_SIZE)
 0135            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {SHORT_BIT_SIZE}.");
 136
 324137        var masked = value & (short)((1 >> bits) - 1);
 324138        var shifted = (short)(masked << (SHORT_BIT_SIZE - bits));
 139
 324140        Span<byte> bytes = stackalloc byte[sizeof(short)];
 324141        BinaryPrimitives.WriteInt16LittleEndian(bytes, shifted);
 142
 324143        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 144        {
 324145            bytes.Reverse();
 146        }
 147
 324148        WriteBits(bytes, bits);
 324149    }
 150
 151    public void WriteUInt16AsBits(ushort value, int bits, bool bigEndian = true)
 152    {
 153        const byte USHORT_BIT_SIZE = sizeof(ushort) * 8;
 24154        if (bits is < 1 or > USHORT_BIT_SIZE)
 0155            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {USHORT_BIT_SIZE}.");
 156
 24157        var masked = value & (ushort)((1 << bits) - 1);
 24158        var shifted = (ushort)(masked << (USHORT_BIT_SIZE - bits));
 159
 24160        Span<byte> bytes = stackalloc byte[sizeof(ushort)];
 24161        BinaryPrimitives.WriteUInt16LittleEndian(bytes, shifted);
 162
 24163        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 164        {
 24165            bytes.Reverse();
 166        }
 167
 24168        WriteBits(bytes, bits);
 24169    }
 170
 171    public void WriteInt32AsBits(int value, int bits, bool bigEndian = true)
 172    {
 173        const byte INT_BIT_SIZE = sizeof(int) * 8;
 92174        if (bits is < 1 or > INT_BIT_SIZE)
 0175            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {INT_BIT_SIZE}.");
 176
 92177        var masked = value & (int)((1L << bits) - 1);
 92178        var shifted = masked << (INT_BIT_SIZE - bits);
 179
 92180        Span<byte> bytes = stackalloc byte[sizeof(int)];
 92181        BinaryPrimitives.WriteInt32LittleEndian(bytes, shifted);
 182
 92183        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 184        {
 92185            bytes.Reverse();
 186        }
 187
 92188        WriteBits(bytes, bits);
 92189    }
 190
 191    public void WriteInt64AsBits(long value, int bits, bool bigEndian = true)
 192    {
 193        const byte LONG_BIT_SIZE = sizeof(long) * 8;
 72194        if (bits is < 1 or > LONG_BIT_SIZE)
 0195            throw new ArgumentOutOfRangeException(nameof(bits), $"must be between 1 and {LONG_BIT_SIZE}.");
 196
 72197        var masked = value & (long)((1UL << bits) - 1);
 72198        var shifted = masked << (LONG_BIT_SIZE - bits);
 199
 72200        Span<byte> bytes = stackalloc byte[sizeof(long)];
 72201        BinaryPrimitives.WriteInt64LittleEndian(bytes, shifted);
 202
 72203        if ((bigEndian && BitConverter.IsLittleEndian) || (!bigEndian && !BitConverter.IsLittleEndian))
 204        {
 72205            bytes.Reverse();
 206        }
 207
 72208        WriteBits(bytes, bits);
 72209    }
 210
 211    public bool HasMoreBits(int requiredBits)
 212    {
 2272213        return _bitOffset + requiredBits <= TotalBits;
 214    }
 215
 216    public void SkipBits(int v)
 217    {
 0218        _bitOffset += v;
 0219    }
 220
 221    public byte[] ToArray()
 222    {
 284223        var bytes = new byte[(TotalBits + 7) / 8];
 37648224        for (var i = 0; i < bytes.Length; i++)
 225        {
 18540226            bytes[i] = _buffer[i];
 227        }
 228
 284229        return bytes;
 230    }
 231}