using System; using System.Diagnostics; namespace RTCSync.Utils; public readonly struct BitIndex { private readonly ushort _byteIdx; private readonly byte _startBitIdx0; private readonly byte _endBitIdx0; // порядок указывания байт в структуре // сначала старший, потом младше // 1<-------------------------------41 // порядок указывания бит в структуре // сначала младший, потом старше // 7<------0 // таким образом все биты вписываются по порядку справа налево // BitIndex[new(3, 0..7), new(2, 0..7), new(1, 0..6)] public BitIndex(int byteIdx1, Range bits) { _byteIdx = (ushort)(byteIdx1 - 1); Debug.Assert(!bits.Start.IsFromEnd); Debug.Assert(!bits.End.IsFromEnd); _startBitIdx0 = (byte)bits.Start.Value; _endBitIdx0 = (byte)bits.End.Value; } public int Length => _endBitIdx0 - _startBitIdx0 + 1; public int Take(ReadOnlySpan bytes) { // example: Len=3, 0b00001000 - 1 = 0b00000111 var mask = (1U << (_endBitIdx0 - _startBitIdx0 + 1)) - 1U; return (int)((bytes[_byteIdx] >> _startBitIdx0) & mask); } public static int Take(BitIndex[] bis, ReadOnlySpan bytes) { var shift = 0; var result = 0; foreach (var bi in bis) { result |= (bi.Take(bytes) << shift); shift += bi.Length; } return result; } } public static class BitIndexV2Extensions { public static int Take(this BitIndex[] bis, ReadOnlySpan bytes) { var shift = 0; var result = 0; foreach (var bi in bis) { result |= (bi.Take(bytes) << shift); shift += bi.Length; } return result; } }