using Godot; using System.Collections.Generic; using System.Text; namespace Rokojori { public partial class BitView { byte[] _data; int _internalViewOffset = 0; public int bitOffset => _internalViewOffset; int _internalViewSize = 0; public int numBits => _internalViewSize; public int numBytes => Mathf.CeilToInt( _internalViewSize / 8f ); int _externalPointer = 0; public int pointerPosition => _externalPointer; public void SetPointer( int bitPosition ) { _externalPointer = bitPosition; } public byte[] GetBytes() { var numBytes = this.numBytes; var data = new byte[ numBytes ]; System.Array.Fill( data, (byte)0 ); for ( int i = 0; i < _internalViewSize; i++ ) { var isSet = GetValueInternal( i + _internalViewOffset ); if ( ! isSet ) { continue; } var shift = i % 8; var index = i / 8; var value = 1 << shift; data[ index ] = (byte) ( data[ index ] | value ); } return data; } public BitView Clone() { var clone = new BitView(); clone._data = _data; clone._internalViewOffset = _internalViewOffset; clone._internalViewSize = _internalViewSize; clone._externalPointer = _externalPointer; return clone; } public string GetBitString() { var output = new StringBuilder(); for ( int i = 0; i < _internalViewSize; i++ ) { var mod = i % 8; if ( i != 0 && mod == 0 ) { output.Append( " " ); } var reverseIndex = 7 - mod * 2; var value = GetValueInternal( reverseIndex + i + _internalViewOffset ) ? "1" : "0"; output.Append( value ); } return output.ToString(); } void SetValueInternal( int position, bool value ) { var byteIndex = position / 8; var bitOffset = position - byteIndex * 8; if ( value ) { _data[ byteIndex ] = Bytes.SetBit( _data[ byteIndex ], bitOffset ); } else { _data[ byteIndex ] = Bytes.UnsetBit( _data[ byteIndex ], bitOffset ); } } bool GetValueInternal( int bitPosition ) { var byteIndex = bitPosition / 8; var bitOffset = bitPosition - byteIndex * 8; return Bytes.IsBitSet( _data[ byteIndex ], bitOffset ); } int GetShiftedValueInternal( int bitPosition, int shift ) { if ( ! GetValueInternal( bitPosition ) ) { return 0; } return 1 << shift; } void EnsureInternalSize( int bitPosition ) { var byteIndex = bitPosition / 8; if ( byteIndex >= _data.Length ) { var newData = new byte[ _data.Length + 1 ]; System.Array.Fill( newData, (byte) 0 ); System.Array.Copy( _data, 0, newData, 0, _data.Length ); _data = newData; } } public void Clear() { _externalPointer = 0; for ( int i = 0; i < _internalViewSize; i++ ) { SetValueInternal( i + _internalViewOffset, false ); } } public void ResetPointerAndSize() { _externalPointer = 0; _internalViewSize = 0; } public void WriteBit( bool value ) { var position = _externalPointer + _internalViewOffset; EnsureInternalSize( position ); SetValueInternal( position, value ); _externalPointer ++; _internalViewSize = Mathf.Max( _externalPointer, _internalViewSize ); } public bool GetBitAt( int bitPosition ) { var internalBitPosition = bitPosition + _internalViewOffset; return GetValueInternal( bitPosition ); } public bool ReadBit() { var bitPosition = _externalPointer + _internalViewOffset; if ( bitPosition < 0 || bitPosition >= numBits ) { RJLog.Log( "Can't read: >> ", bitPosition, "in", numBits, "p:", _externalPointer, "o:", _internalViewOffset ); return false; } var value = GetValueInternal( bitPosition ); _externalPointer ++; return value; } public void SetPointerPosition( int position ) { _externalPointer = Mathf.Clamp( position, 0, _internalViewSize ); } public static BitView CreateEmpty() { var view = new BitView(); view._data = new byte[]{}; view._internalViewOffset = 0; view._internalViewSize = 0; view._externalPointer = 0; return view; } public static BitView From( byte[] data) { var view = new BitView(); view._data = data; view._internalViewOffset = 0; view._internalViewSize = data.Length * 8; view._externalPointer = 0; return view; } public static BitView From( byte[] data, int bitSize = -1 ) { var view = new BitView(); view._data = data; view._internalViewOffset = 0; view._internalViewSize = bitSize == -1 ? data.Length * 8 : bitSize; view._externalPointer = 0; return view; } public static BitView PrependBytes( byte[] before, BitView after ) { var data = new byte[ before.Length + after.numBytes ]; System.Array.Copy( before, 0, data, 0, before.Length ); var view = BitView.From( data, before.Length * 8 + after.numBits ); if ( after.bitOffset == 0 ) { System.Array.Copy( after._data, 0, data, 0, after.numBytes ); view.SetPointer( after.pointerPosition + before.Length * 8 ); return view; } view.SetPointer( before.Length * 8 ); for ( int i = 0; i < after.numBits; i++ ) { view.WriteBit( after.GetBitAt( i ) ); } view.SetPointer( after.pointerPosition + before.Length * 8 ); return view; } public static BitView PrependID( int id, BitView after ) { var idView = BitView.CreateEmpty(); idView.WriteUIntVL8( id ); return BitView.PrependBytes( idView.GetBytes(), after ); } } }