C#版BitStream 1.0

时间:2023-03-10 04:10:22
C#版BitStream 1.0

根据C++版的改编,刚刚改完,估计使用会有问题,对于uint8处理的不好

关于使用:

 BitStream bs = new BitStream( );
bs.WriteInt32( ); int a = bs.ReadInt32( );

非常简单

BitStream.cs

 public class BitStream
{
#if __BITSTREAM_BIG_END
// Set up the read/write routines to produce Big-End network streams.
private const int B16_1 = ;
private const int B16_0 = ; private const int B32_3 = ;
private const int B32_2 = ;
private const int B32_1 = ;
private const int B32_0 = ; private const int B64_7 = ;
private const int B64_6 = ;
private const int B64_5 = ;
private const int B64_4 = ;
private const int B64_3 = ;
private const int B64_2 = ;
private const int B64_1 = ;
private const int B64_0 = ; #else
// Default to producing Little-End network streams.
private const int B16_1 = ;
private const int B16_0 = ; private const int B32_3 = ;
private const int B32_2 = ;
private const int B32_1 = ;
private const int B32_0 = ; private const int B64_7 = ;
private const int B64_6 = ;
private const int B64_5 = ;
private const int B64_4 = ;
private const int B64_3 = ;
private const int B64_2 = ;
private const int B64_1 = ;
private const int B64_0 = ;
#endif
public const int BITSTREAM_STACK_ALLOCATION_SIZE = ; /// Default Constructor
public static int BITS_TO_BYTES(int x)
{
return (((x) + ) >> );
} public static int BYTES_TO_BITS(int x)
{
return (x << );
} /**
* @brief Packets encoding and decoding facilities
*
* Helper class to encode and decode packets.
*
*/ /**
* Default Constructor
*/ public BitStream()
{
numberOfBitsUsed = ;
//numberOfBitsAllocated = 32 * 8;
numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE*;
readOffset = ;
//data = ( unsigned char* ) malloc( 32 );
data = stackData;
copyData = true;
} /**
* Preallocate some memory for the construction of the packet
* @param initialBytesToAllocate the amount of byte to pre-allocate.
*/ public BitStream(int initialBytesToAllocate)
{
numberOfBitsUsed = ;
readOffset = ;
if (initialBytesToAllocate <= BITSTREAM_STACK_ALLOCATION_SIZE)
{
data = stackData;
numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE*;
}
else
{
data = new Byte[initialBytesToAllocate];
numberOfBitsAllocated = initialBytesToAllocate << ;
}
copyData = true;
} /**
* Initialize the BitStream object using data from the network.
* Set _copyData to true if you want to make an internal copy of
* the data you are passing. You can then Write and do all other
* operations Set it to false if you want to just use a pointer to
* the data you are passing, in order to save memory and speed.
* You should only then do read operations.
* @param _data An array of bytes.
* @param lengthInBytes Size of the @em _data.
* @param _copyData Does a copy of the input data.
*/ public BitStream(Byte[] _data, int lengthInBytes, bool _copyData)
{
numberOfBitsUsed = lengthInBytes << ;
readOffset = ;
copyData = _copyData;
numberOfBitsAllocated = lengthInBytes << ; if (copyData)
{
if (lengthInBytes > )
{
if (lengthInBytes < BITSTREAM_STACK_ALLOCATION_SIZE)
{
data = stackData;
numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE << ;
}
else
{
data = new Byte[lengthInBytes];
}
_data.CopyTo(data, );
}
else
data = null;
}
else
{
data = _data;
numberOfBitsUsed = ;
}
} //
public BitStream(Byte[] _data, int lengthInBytes, int datasize)
{
numberOfBitsUsed = datasize << ;
readOffset = ;
numberOfBitsAllocated = lengthInBytes << ;
data = _data;
copyData = false;
} /**
* Destructor
*/
//~BitStream(){}
/**
* Reset the bitstream for reuse
*/ private void Reset()
{
if (numberOfBitsUsed > )
{
// memset(data, 0, BITS_TO_BYTES(numberOfBitsUsed));
}
// Don't free memory here for speed efficiency
//free(data); // Use realloc and free so we are more efficient than delete and new for resizing
numberOfBitsUsed = ;
//numberOfBitsAllocated=8;
readOffset = ;
} public void SetBuffer(Byte[] _data, int lengthInBytes, int datasize)
{
numberOfBitsUsed = datasize << ;
readOffset = ;
numberOfBitsAllocated = lengthInBytes << ;
data = _data;
copyData = false;
} public void ClearBuffer()
{
numberOfBitsUsed = ;
readOffset = ;
numberOfBitsAllocated = ;
data = null;
} /**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/ public void WriteBool(bool input)
{
if (input)
WriteInt8();
else
WriteInt8();
} /**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/ public void WriteUInt8(Byte input)
{
WriteBits(BitConverter.GetBytes(input), sizeof (Byte)*, true);
} // /**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/ public void WriteInt8(SByte input)
{
WriteBits(BitConverter.GetBytes(input), sizeof (SByte)*, true);
} /**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/ public void WriteUInt16(UInt16 input)
{
var uint16w = new Byte[];
uint16w[B16_1] = (Byte) ((Byte) (input >> ) & (0xff));
uint16w[B16_0] = (Byte) (input & (0xff)); WriteBits(uint16w, sizeof (UInt16)*, true);
} /**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/ public void WriteInt16(Int16 input)
{
var int16w = new Byte[];
int16w[B16_1] = (Byte) ((Byte) (input >> ) & (0xff));
int16w[B16_0] = (Byte) (input & (0xff)); WriteBits(int16w, sizeof (Int16)*, true);
} /**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/ public void WriteUInt32(UInt32 input)
{
var uint32w = new Byte[];
uint32w[B32_3] = (Byte) ((Byte) (input >> ) & (0x000000ff));
uint32w[B32_2] = (Byte) ((Byte) (input >> ) & (0x000000ff));
uint32w[B32_1] = (Byte) ((Byte) (input >> ) & (0x000000ff));
uint32w[B32_0] = (Byte) ((input) & (0x000000ff)); WriteBits(uint32w, sizeof (UInt32)*, true);
} /**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/ public void WriteInt32(int input)
{
var int32w = new Byte[];
int32w[B32_3] = (Byte) ((Byte) (input >> ) & (0x000000ff));
int32w[B32_2] = (Byte) ((Byte) (input >> ) & (0x000000ff));
int32w[B32_1] = (Byte) ((Byte) (input >> ) & (0x000000ff));
int32w[B32_0] = (Byte) ((input) & (0x000000ff)); WriteBits(int32w, sizeof (int)*, true);
} //#if HAS_INT64
/**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/ public void WriteUInt64(UInt64 input)
{
var uint64w = new Byte[];
uint64w[B64_7] = (Byte) ((input >> ) & 0xff);
uint64w[B64_6] = (Byte) ((input >> ) & 0xff);
uint64w[B64_5] = (Byte) ((input >> ) & 0xff);
uint64w[B64_4] = (Byte) ((input >> ) & 0xff);
uint64w[B64_3] = (Byte) ((input >> ) & 0xff);
uint64w[B64_2] = (Byte) ((input >> ) & 0xff);
uint64w[B64_1] = (Byte) ((input >> ) & 0xff);
uint64w[B64_0] = (Byte) (input & 0xff); WriteBits(uint64w, sizeof (UInt64)*, true);
} /**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/ public void WriteInt64(Int64 input)
{
var int64w = new Byte[];
int64w[B64_7] = (Byte) ((input >> ) & 0xff);
int64w[B64_6] = (Byte) ((input >> ) & 0xff);
int64w[B64_5] = (Byte) ((input >> ) & 0xff);
int64w[B64_4] = (Byte) ((input >> ) & 0xff);
int64w[B64_3] = (Byte) ((input >> ) & 0xff);
int64w[B64_2] = (Byte) ((input >> ) & 0xff);
int64w[B64_1] = (Byte) ((input >> ) & 0xff);
int64w[B64_0] = (Byte) (input & 0xff); WriteBits(int64w, sizeof (Int64)*, true);
} //#endif /**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/ public void WriteFloat(float input)
{
WriteBits(BitConverter.GetBytes(input), sizeof (float)*, true);
} /**
* Write the native types to the end of the buffer
* without any compression mechanism.
* @param input The data
*/ public void WriteDouble(double input)
{
WriteBits(BitConverter.GetBytes(input), sizeof (double)*, true);
} /**
* Write an array or casted stream. It is supposed to
* be raw data. It is also not possible to deal with endian problem
* @param input a byte buffer
* @param numberOfBytes the size of the byte buffer
*/ public void WriteBytes(Byte[] input, int numberOfBytes)
{
WriteBits(input, numberOfBytes*, true);
} /**
* write multi bytes string
* @param input
*/ public void WriteStr(string input)
{
var len = (short) input.Length;
WriteUInt16((ushort) len);
if (len > )
{
WriteBytes(Encoding.Default.GetBytes(input), len);
}
} /// **
// * write standard string
// * @param input
// */
// public void WriteStr(
// const std::
// string
//&
// input
//){}
/**
* Copy from another bitstream
* @bitStream the bitstream to copy from
*/
public void WriteBS(BitStream bitStream)
{
WriteBits(bitStream.GetData(), bitStream.GetWriteOffset(), false);
} /**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/ public void WriteCompUInt8(Byte input)
{
WriteCompressed(BitConverter.GetBytes(input), sizeof (Byte)*, true);
} /**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/ public void WriteCompInt8(SByte input)
{
WriteCompressed(BitConverter.GetBytes(input), sizeof (SByte)*, false);
} /**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/ public void WriteCompUInt16(UInt16 input)
{
var uint16wc = new Byte[];
uint16wc[B16_1] = (byte) ((Byte) (input >> ) & (0xff));
uint16wc[B16_0] = (byte) (input & (0xff)); WriteCompressed(uint16wc, sizeof (UInt16)*, true);
} /**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/ public void WriteCompInt16(Int16 input)
{
var int16wc = new Byte[];
int16wc[B16_1] = (Byte) ((input >> ) & (0xff));
int16wc[B16_0] = (Byte) (input & (0xff)); WriteCompressed(int16wc, sizeof (Int16)*, false);
} /**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/ public void WriteCompUInt32(UInt32 input)
{
var uint32wc = new Byte[];
uint32wc[B32_3] = (Byte) ((input >> ) & (0x000000ff));
uint32wc[B32_2] = (Byte) ((input >> ) & (0x000000ff));
uint32wc[B32_1] = (Byte) ((input >> ) & (0x000000ff));
uint32wc[B32_0] = (Byte) ((input) & (0x000000ff)); WriteCompressed(uint32wc, sizeof (UInt32)*, true);
} /**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/ public void WriteCompInt32(int input)
{
var int32wc = new Byte[];
int32wc[B32_3] = (Byte) ((input >> ) & (0x000000ff));
int32wc[B32_2] = (Byte) ((input >> ) & (0x000000ff));
int32wc[B32_1] = (Byte) ((input >> ) & (0x000000ff));
int32wc[B32_0] = (Byte) ((input) & (0x000000ff)); WriteCompressed(int32wc, sizeof (int)*, false);
} //#ifdef HAS_INT64
/**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/ public void WriteCompUInt64(UInt64 input)
{
var uint64wc = new Byte[];
uint64wc[B64_7] = (Byte) ((input >> ) & 0xff);
uint64wc[B64_6] = (Byte) ((input >> ) & 0xff);
uint64wc[B64_5] = (Byte) ((input >> ) & 0xff);
uint64wc[B64_4] = (Byte) ((input >> ) & 0xff);
uint64wc[B64_3] = (Byte) ((input >> ) & 0xff);
uint64wc[B64_2] = (Byte) ((input >> ) & 0xff);
uint64wc[B64_1] = (Byte) ((input >> ) & 0xff);
uint64wc[B64_0] = (Byte) (input & 0xff); WriteCompressed(uint64wc, sizeof (UInt64)*, true);
} /**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/ public void WriteCompInt64(Int64 input)
{
var int64wc = new Byte[];
int64wc[B64_7] = (Byte) ((input >> ) & 0xff);
int64wc[B64_6] = (Byte) ((input >> ) & 0xff);
int64wc[B64_5] = (Byte) ((input >> ) & 0xff);
int64wc[B64_4] = (Byte) ((input >> ) & 0xff);
int64wc[B64_3] = (Byte) ((input >> ) & 0xff);
int64wc[B64_2] = (Byte) ((input >> ) & 0xff);
int64wc[B64_1] = (Byte) ((input >> ) & 0xff);
int64wc[B64_0] = (Byte) (input & 0xff); WriteCompressed(int64wc, sizeof (Int64)*, false);
} //#endif
/**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/ public void WriteCompFloat(float input)
{
WriteFloat(input);
} /**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/ public void WriteCompDouble(double input)
{
WriteDouble(input);
} /**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/ public bool ReadBool()
{
if (readOffset + > numberOfBitsUsed)
return false; //if (ReadBit()) // Check that bit
if ((data[readOffset >> ] & (0x80 >> (readOffset++%))) != ) // Is it faster to just write it out here?
return true; return false;
} /**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/ public SByte ReadUInt8()
{
var x = new Byte[sizeof (SByte)];
if (ReadBits(ref x, sizeof (SByte)*))
{
return (SByte) BitConverter.ToChar(x, );
}
return ;
} /**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/ public Byte ReadInt8()
{
var x = new Byte[sizeof (Byte)];
if (ReadBits(ref x, sizeof (Byte)*))
{
return (Byte) BitConverter.ToChar(x, );
;
}
return ;
} /**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/ public UInt16 ReadUInt16()
{
var uint16r = new Byte[];
if (ReadBits(ref uint16r, sizeof (UInt16)*) != true)
return ;
return (ushort) ((uint16r[B16_1] << ) | uint16r[B16_0]);
} /**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/ public short ReadInt16()
{
var int16r = new Byte[];
if (ReadBits(ref int16r, sizeof (short)*) != true)
return ; return (short) ((int16r[B16_1] << ) | int16r[B16_0]);
} /**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/ public UInt32 ReadUInt32()
{
var uint32r = new Byte[];
if (ReadBits(ref uint32r, sizeof (UInt32)*) != true)
return ;
return (((UInt32) uint32r[B32_3]) << ) |
(((UInt32) uint32r[B32_2]) << ) |
(((UInt32) uint32r[B32_1]) << ) |
uint32r[B32_0];
} /**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/ public int ReadInt32()
{
var int32r = new Byte[];
if (ReadBits(ref int32r, sizeof (int)*) != true)
return ;
return (int32r[B32_3] << ) |
(int32r[B32_2] << ) |
(int32r[B32_1] << ) |
int32r[B32_0];
} //#ifdef HAS_INT64
/**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/ public UInt64 ReadUInt64()
{
var uint64r = new Byte[];
if (ReadBits(ref uint64r, sizeof (UInt64)*) != true)
return ;
return (((UInt64) uint64r[B64_7]) << ) | (((UInt64) uint64r[B64_6]) << ) |
(((UInt64) uint64r[B64_5]) << ) | (((UInt64) uint64r[B64_4]) << ) |
(((UInt64) uint64r[B64_3]) << ) | (((UInt64) uint64r[B64_2]) << ) |
(((UInt64) uint64r[B64_1]) << ) | uint64r[B64_0];
} /**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/ public Int64 ReadInt64()
{
var int64r = new Byte[];
if (ReadBits(ref int64r, sizeof (Int64)*) != true)
return ;
return (Int64) ((((UInt64) int64r[B64_7]) << ) | (((UInt64) int64r[B64_6]) << ) |
(((UInt64) int64r[B64_5]) << ) | (((UInt64) int64r[B64_4]) << ) |
(((UInt64) int64r[B64_3]) << ) | (((UInt64) int64r[B64_2]) << ) |
(((UInt64) int64r[B64_1]) << ) | int64r[B64_0]);
} //#endif
/**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/ public float ReadFloat()
{
uint val = ReadUInt32();
return BitConverter.ToSingle(BitConverter.GetBytes(val), );
} /**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/ public double ReadDouble()
{
UInt64 val = ReadUInt64();
return BitConverter.ToDouble(BitConverter.GetBytes(val), );
} /**
* Read an array or casted stream of byte. The array
* is raw data. There is no automatic conversion on
* big endian arch
* @param output The result byte array. It should be larger than @em numberOfBytes.
* @param numberOfBytes The number of byte to read
* @return true on success false if there is some missing bytes.
*/ public bool ReadBytes(ref Byte[] output, int numberOfBytes)
{
return ReadBits(ref output, numberOfBytes*);
} /**
* Read standard string
* @return
*/ public string ReadStr()
{
string strs;
ushort len = ReadUInt16();
if (len > )
{
var str = new Byte[len + ];
if (ReadBytes(ref str, len))
{
str[len] = ;
strs = Encoding.Default.GetString(str);
return strs;
}
}
return string.Empty;
} /**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/ public SByte ReadCompUInt8()
{
var uint8rc = new Byte[sizeof (Byte)]; if (ReadCompressed(ref uint8rc, sizeof (Byte)*, true))
{
return (SByte) uint8rc[];
}
return ;
} /**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/ public Byte ReadCompInt8()
{
var uint8rc = new Byte[sizeof (Byte)]; if (ReadCompressed(ref uint8rc, sizeof (Byte)*, true))
{
return uint8rc[];
}
return ;
} /**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/ public UInt16 ReadCompUInt16()
{
var uint16rc = new Byte[];
if (ReadCompressed(ref uint16rc, sizeof (UInt16)*, true) != true)
return ;
return (ushort) ((uint16rc[B16_1] << ) |
uint16rc[B16_0]);
} /**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/ public short ReadCompInt16()
{
var int16rc = new byte[];
if (ReadCompressed(ref int16rc, sizeof (short)*, false) != true) return ;
return (short) ((int16rc[B16_1] << ) | int16rc[B16_0]);
} /**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/ public UInt32 ReadCompUInt32()
{
var uint32rc = new Byte[];
if (ReadCompressed(ref uint32rc, sizeof (UInt32)*, true) != true)
return ;
return (((UInt32) uint32rc[B32_3]) << ) |
(((UInt32) uint32rc[B32_2]) << ) |
(((UInt32) uint32rc[B32_1]) << ) |
uint32rc[B32_0];
} /**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/ public int ReadCompInt32()
{
var int32rc = new Byte[];
if (ReadCompressed(ref int32rc, sizeof (int)*, false) != true)
return ;
return (int) ((((UInt32) int32rc[B32_3]) << ) |
(((UInt32) int32rc[B32_2]) << ) |
(((UInt32) int32rc[B32_1]) << ) |
int32rc[B32_0]);
} //#ifdef HAS_INT64
/**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/ public UInt64 ReadCompUInt64()
{
var uint64rc = new Byte[];
if (ReadCompressed(ref uint64rc, sizeof (UInt64)*, true) != true)
return ;
return (((UInt64) uint64rc[B64_7]) << ) | (((UInt64) uint64rc[B64_6]) << ) |
(((UInt64) uint64rc[B64_5]) << ) | (((UInt64) uint64rc[B64_4]) << ) |
(((UInt64) uint64rc[B64_3]) << ) | (((UInt64) uint64rc[B64_2]) << ) |
(((UInt64) uint64rc[B64_1]) << ) | uint64rc[B64_0];
} /**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/ public Int64 ReadCompInt64()
{
var int64rc = new Byte[];
if (ReadCompressed(ref int64rc, sizeof (Int64)*, false) != true)
return ;
return (Int64) ((((UInt64) int64rc[B64_7]) << ) | (((UInt64) int64rc[B64_6]) << ) |
(((UInt64) int64rc[B64_5]) << ) | (((UInt64) int64rc[B64_4]) << ) |
(((UInt64) int64rc[B64_3]) << ) | (((UInt64) int64rc[B64_2]) << ) |
(((UInt64) int64rc[B64_1]) << ) | int64rc[B64_0]);
} //#endif
/**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/ public float ReadCompFloat()
{
return ReadFloat();
} /**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/ public double ReadCompDouble()
{
return ReadDouble();
} /**
* Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12 bytes. Will further compress y or z axis aligned vectors.
* Accurate to 1/32767.5.
* @param x x
* @param y y
* @param z z
*/ /**
* This is good to call when you are done with the stream to make
* sure you didn't leave any data left over void
*/ public void AssertStreamEmpty()
{
if (readOffset == numberOfBitsUsed)
throw new Exception();
} // * print to the standard output the state of the stream bit by bit
// */ //public void PrintBits()
//{ //}
/// **
/// **
// * Ignore data we don't intend to read
// * @param numberOfBits The number of bits to ignore
// */
public void IgnoreBits(int numberOfBits)
{
readOffset += numberOfBits;
} /**
* Move the write pointer to a position on the array.
* @param offset the offset from the start of the array.
* @attention
* Dangerous if you don't know what you are doing!
*
*/ public void SetWriteOffset(int offset)
{
numberOfBitsUsed = offset;
} /**
* Returns the length in bits of the stream
*/ public int GetWriteOffset()
{
return numberOfBitsUsed;
} /**
* Returns the length in bytes of the stream
*/ public int GetNumberOfBytesUsed()
{
return BITS_TO_BYTES(numberOfBitsUsed);
} public int GetNumberOfBytesRead()
{
return BITS_TO_BYTES(readOffset);
} /**
* Move the read pointer to a position on the array.
* @param offset
*/ public void SetReadOffset(int offset)
{
readOffset = offset;
} public void SetByteReadOffSet(int offset)
{
readOffset = BYTES_TO_BITS(offset);
} /**
* Returns the number of bits into the stream that we have read
*/ public int GetReadOffset()
{
return readOffset;
} /**
* Returns the number of bits left in the stream that haven't been read
*/ public int GetNumberOfUnreadBits()
{
return numberOfBitsUsed - readOffset;
} /**
* Makes a copy of the internal data for you Data will point to
* the stream. Returns the length in bits of the stream. Partial
* bytes are left aligned
* @param _data the resulting byte copy of the internal state.
*/ public int CopyData(Byte[] _data)
{
_data = new Byte[BITS_TO_BYTES(numberOfBitsUsed)];
data.CopyTo(_data, );
return numberOfBitsUsed;
} /**
* Set the stream to some initial data. For internal use
* Partial bytes are left aligned
* @param input The data
* @param numberOfBits the number of bits set in the data buffer
*/ public void SetData(Byte[] input, int numberOfBits)
{
if (numberOfBits <= )
return;
AddBitsAndReallocate(numberOfBits);
input.CopyTo(data, );
numberOfBitsUsed = numberOfBits;
} /**
* Exposes the internal data.
* Partial bytes are left aligned.
* @return A pointer to the internal state
*/ public Byte[] GetData()
{
return data;
} /**
* Write numberToWrite bits from the input source Right aligned
* data means in the case of a partial byte, the bits are aligned
* from the right (bit 0) rather than the left (as in the normal
* internal representation) You would set this to true when
* writing user data, and false when copying bitstream data, such
* as writing one bitstream to another
* @param input The data
* @param numberOfBitsToWrite The number of bits to write
* @param rightAlignedBits if true data will be right aligned
*/ public void WriteBits(Byte[] input, int numberOfBitsToWrite, bool rightAlignedBits = true)
{
AddBitsAndReallocate(numberOfBitsToWrite);
int offset = ;
Byte dataByte;
int numberOfBitsUsedMod8; numberOfBitsUsedMod8 = numberOfBitsUsed%; // Faster to put the while at the top surprisingly enough
while (numberOfBitsToWrite > )
//do
{
dataByte = input[offset]; //*( input + offset ); if (numberOfBitsToWrite < && rightAlignedBits)
// rightAlignedBits means in the case of a partial byte, the bits are aligned from the right (bit 0) rather than the left (as in the normal internal representation)
dataByte <<= - numberOfBitsToWrite;
// shift left to get the bits on the left, as in our internal representation // Writing to a new byte each time
if (numberOfBitsUsedMod8 == )
data[numberOfBitsUsed >> ] = dataByte; //*( data + ( numberOfBitsUsed >> 3 ) ) = dataByte;
else
{
// Copy over the new data.
data[numberOfBitsUsed >> ] |= (Byte) (dataByte >> (numberOfBitsUsedMod8));
//*( data + ( numberOfBitsUsed >> 3 ) ) |= dataByte >> ( numberOfBitsUsedMod8 ); // First half if ( - (numberOfBitsUsedMod8) < && - (numberOfBitsUsedMod8) < numberOfBitsToWrite)
// If we didn't write it all out in the first half (8 - (numberOfBitsUsed%8) is the number we wrote in the first half)
{
//*( data + ( numberOfBitsUsed >> 3 ) + 1 ) = (unsigned char) ( dataByte << ( 8 - ( numberOfBitsUsedMod8 ) ) ); // Second half (overlaps byte boundary)
data[(numberOfBitsUsed >> ) + ] = (Byte) (dataByte << ( - (numberOfBitsUsedMod8)));
}
} if (numberOfBitsToWrite >= )
numberOfBitsUsed += ;
else
numberOfBitsUsed += numberOfBitsToWrite; numberOfBitsToWrite -= ; offset++;
}
} /**
* Align the bitstream to the byte boundary and then write the
* specified number of bits. This is faster than WriteBits but
* wastes the bits to do the alignment and requires you to call
* ReadAlignedBits at the corresponding read position.
* @param input The data
* @param numberOfBytesToWrite The size of data.
*/ public void WriteAlignedBytes(Byte[] input, int numberOfBytesToWrite)
{
AlignWriteToByteBoundary();
// Allocate enough memory to hold everything
AddBitsAndReallocate(numberOfBytesToWrite << ); // Write the data
//memcpy( data + ( numberOfBitsUsed >> 3 ), input, numberOfBytesToWrite );
input.CopyTo(data, (numberOfBitsUsed >> ));
numberOfBitsUsed += numberOfBytesToWrite << ;
} /**
* Read bits, starting at the next aligned bits. Note that the
* modulus 8 starting offset of the sequence must be the same as
* was used with WriteBits. This will be a problem with packet
* coalescence unless you byte align the coalesced packets.
* @param output The byte array larger than @em numberOfBytesToRead
* @param numberOfBytesToRead The number of byte to read from the internal state
* @return true if there is enough byte.
*/ public bool ReadAlignedBytes(out Byte[] output, int numberOfBytesToRead)
{
if (numberOfBytesToRead <= )
{
output = null;
return false;
}
// Byte align
AlignReadToByteBoundary();
if (readOffset + (numberOfBytesToRead << ) > numberOfBitsUsed)
{
output = null;
return false;
} // Write the data
//memcpy( output, data + ( readOffset >> 3 ), numberOfBytesToRead );
output = new byte[] {};
Array.Copy(data, readOffset >> , output, , numberOfBytesToRead);
readOffset += numberOfBytesToRead << ;
return true;
} /**
* Align the next write and/or read to a byte boundary. This can
* be used to 'waste' bits to byte align for efficiency reasons It
* can also be used to force coalesced bitstreams to start on byte
* boundaries so so WriteAlignedBits and ReadAlignedBits both
* calculate the same offset when aligning.
*/ public void AlignWriteToByteBoundary()
{
if (numberOfBitsUsed > )
numberOfBitsUsed += - ((numberOfBitsUsed - )% + );
} /**
* Align the next write and/or read to a byte boundary. This can
* be used to 'waste' bits to byte align for efficiency reasons It
* can also be used to force coalesced bitstreams to start on byte
* boundaries so so WriteAlignedBits and ReadAlignedBits both
* calculate the same offset when aligning.
*/ public void AlignReadToByteBoundary()
{
if (readOffset > )
readOffset += - ((readOffset - )% + );
} /**
* Read numberOfBitsToRead bits to the output source
* alignBitsToRight should be set to true to convert internal
* bitstream data to userdata It should be false if you used
* WriteBits with rightAlignedBits false
* @param output The resulting bits array
* @param numberOfBitsToRead The number of bits to read
* @param alignsBitsToRight if true bits will be right aligned.
* @return true if there is enough bits to read
*/ public bool ReadBits(ref Byte[] output, int numberOfBitsToRead, bool alignBitsToRight = true)
{
if (readOffset + numberOfBitsToRead > numberOfBitsUsed)
{
output = null;
return false;
} int readOffsetMod8;
int offset = ;
//memset( output, 0, BITS_TO_BYTES( numberOfBitsToRead ) );
readOffsetMod8 = readOffset%; // do
// Faster to put the while at the top surprisingly enough
while (numberOfBitsToRead > )
{
//*( output + offset ) |= *( data + ( readOffset >> 3 ) ) << ( readOffsetMod8 ); // First half
output[offset] |= (Byte) (data[readOffset >> ] << (readOffsetMod8));
if (readOffsetMod8 > && numberOfBitsToRead > - (readOffsetMod8))
// If we have a second half, we didn't read enough bytes in the first half
//*(output + offset) |= *(data + (readOffset >> 3) + 1) >> (8 - (readOffsetMod8));
output[offset] |= (Byte) (data[(readOffset >> ) + ] >> ( - (readOffsetMod8)));
// Second half (overlaps byte boundary) numberOfBitsToRead -= ; if (numberOfBitsToRead < )
// Reading a partial byte for the last byte, shift right so the data is aligned on the right
{
if (alignBitsToRight)
output[offset] >>= -numberOfBitsToRead;
//*(output + offset) >>= -numberOfBitsToRead; readOffset += + numberOfBitsToRead;
}
else
readOffset += ; offset++;
}
return true;
} /**
* --- Low level functions ---
* These are for when you want to deal
* with bits and don't care about type checking
* Write a 0
*/ public void Write0()
{
AddBitsAndReallocate(); // New bytes need to be zeroed if ((numberOfBitsUsed%) == )
data[numberOfBitsUsed >> ] = ; numberOfBitsUsed++;
} /**
* --- Low level functions ---
* These are for when you want to deal
* with bits and don't care about type checking
* Write a 1
*/ public void Write1()
{
AddBitsAndReallocate(); int numberOfBitsMod8 = numberOfBitsUsed%; if (numberOfBitsMod8 == )
data[numberOfBitsUsed >> ] = 0x80;
else
data[numberOfBitsUsed >> ] |= (Byte) (0x80 >> (numberOfBitsMod8));
//data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 numberOfBitsUsed++;
} /**
* --- Low level functions ---
* These are for when you want to deal
* with bits and don't care about type checking
* Reads 1 bit and returns true if that bit is 1 and false if it is 0
*/ public bool ReadBit()
{
return (data[readOffset >> ] & (0x80 >> (readOffset++%))) == ;
} /**
* If we used the constructor version with copy data off, this
* makes sure it is set to on and the data pointed to is copied.
*/ public void AssertCopyData()
{
if (copyData == false)
{
copyData = true; if (numberOfBitsAllocated > )
{
var newdata = new Byte[BITS_TO_BYTES(numberOfBitsAllocated)];
data.CopyTo(newdata, );
data = newdata;
}
else
data = null;
}
} /**
* Use this if you pass a pointer copy to the constructor
* (_copyData==false) and want to overallocate to prevent
* reallocation
*/ public void SetNumberOfBitsAllocated(int lengthInBits)
{
numberOfBitsAllocated = lengthInBits;
} /**
* Assume the input source points to a native type, compress and write it.
*/ public void WriteCompressed(Byte[] input, int size, bool unsignedData)
{
int currentByte = (size >> ) - ; // PCs Byte byteMatch; if (unsignedData)
{
byteMatch = ;
} else
{
byteMatch = 0xFF;
} // Write upper bytes with a single 1
// From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes
while (currentByte > )
{
if (input[currentByte] == byteMatch)
// If high byte is byteMatch (0 of 0xff) then it would have the same value shifted
{
bool b = true;
WriteBool(b);
}
else
{
// Write the remainder of the data after writing 0
bool b = false;
WriteBool(b); WriteBits(input, (currentByte + ) << , true);
// currentByte--; return;
} currentByte--;
} // If the upper half of the last byte is a 0 (positive) or 16 (negative) then write a 1 and the remaining 4 bits. Otherwise write a 0 and the 8 bites.
if ((unsignedData && (((input[currentByte])) & 0xF0) == 0x00) ||
(unsignedData == false && (((input[currentByte])) & 0xF0) == 0xF0))
{
bool b = true;
WriteBool(b);
var bs = new byte[];
Array.Copy(input, currentByte, bs, , );
WriteBits(bs, , true);
} else
{
bool b = false;
WriteBool(b);
var bs = new byte[];
Array.Copy(input, currentByte, bs, , );
WriteBits(bs, , true);
}
} /**
* Assume the input source points to a compressed native type.
* Decompress and read it.
*/ public bool ReadCompressed(ref Byte[] output, int size, bool unsignedData)
{
int currentByte = (size >> ) - ; Byte byteMatch, halfByteMatch; if (unsignedData)
{
byteMatch = ;
halfByteMatch = ;
} else
{
byteMatch = 0xFF;
halfByteMatch = 0xF0;
} // Upper bytes are specified with a single 1 if they match byteMatch
// From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes
while (currentByte > )
{
// If we read a 1 then the data is byteMatch. bool b = ReadBool(); if (b) // Check that bit
{
output[currentByte] = byteMatch;
currentByte--;
}
else
{
// Read the rest of the bytes if (ReadBits(ref output, (currentByte + ) << ) == false)
return false; return true;
}
}
return false;
} /**
* Reallocates (if necessary) in preparation of writing
* numberOfBitsToWrite
*/ public void AddBitsAndReallocate(int numberOfBitsToWrite)
{
if (numberOfBitsToWrite <= )
return; int newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed; if (numberOfBitsToWrite + numberOfBitsUsed > &&
((numberOfBitsAllocated - ) >> ) < ((newNumberOfBitsAllocated - ) >> ))
// If we need to allocate 1 or more new bytes
{
// Less memory efficient but saves on news and deletes
newNumberOfBitsAllocated = (numberOfBitsToWrite + numberOfBitsUsed)*;
// int newByteOffset = BITS_TO_BYTES( numberOfBitsAllocated );
// Use realloc and free so we are more efficient than delete and new for resizing
int amountToAllocate = BITS_TO_BYTES(newNumberOfBitsAllocated);
if (data == stackData)
{
if (amountToAllocate > BITSTREAM_STACK_ALLOCATION_SIZE)
{
data = new byte[amountToAllocate]; // need to copy the stack data over to our new memory area too
stackData.CopyTo(data, );
}
}
else
{
data = data.Concat(new Byte[amountToAllocate - data.Length]).ToArray();
//data = ( unsigned char* ) realloc( data, amountToAllocate );
}
// memset(data+newByteOffset, 0, ((newNumberOfBitsAllocated-1)>>3) - ((numberOfBitsAllocated-1)>>3)); // Set the new data block to 0
} if (newNumberOfBitsAllocated > numberOfBitsAllocated)
numberOfBitsAllocated = newNumberOfBitsAllocated;
} /**
* Number of bits currently used
*/
private int numberOfBitsUsed;
/**
* Number of bits currently allocated
*/ private
int numberOfBitsAllocated; /**
* Current readOffset
*/ private
int readOffset; /**
* array of byte storing the data. Points to stackData or if is bigger than that then is allocated
*/ private
Byte[] data; /**
* true if the internal buffer is copy of the data passed to the
* constructor
*/ private
bool copyData; private Byte[] stackData = new Byte[BITSTREAM_STACK_ALLOCATION_SIZE];
}
const