<pre class="brush: csharp; gutter: false;">using System;
using System.Collections.Generic;
using System.Text;
namespace BitIntegerTest
{
public class Packet
{
private List<byte> buffer;
private int bitIndex = 0;
private int maxBitIndex = 0;
private int previousBitIndex = -1;
/// <summary>
/// Expands the buffer size appending bytes so that the write functions don't overflow.
/// Records the furthest write in the maxBitIndex
/// </summary>
/// <param name="bits">The number of bits to allocate and record.</param>
private void ExpandBuffer(int bits)
{
if (bits < 1) throw new ArgumentOutOfRangeException("bits must be greater than 0");
while ((bitIndex + bits + 7) / 8 > buffer.Count) buffer.Add(new byte());
maxBitIndex = Math.Max(maxBitIndex, bitIndex + bits);
}
/// <summary>
/// Constructor.
/// </summary>
public Packet()
{
buffer = new List<byte>();
}
/// <summary>
/// Fills the buffer with the requested bytes to work with.
/// </summary>
/// <param name="buffer">The bytes to load into the buffer</param>
public Packet(byte[] buffer)
{
this.buffer = new List<byte>(buffer);
}
/// <summary>
/// Fills the buffer with the requested bytes to work with.
/// </summary>
/// <param name="buffer">The bytes to load into the buffer</param>
/// <param name="maxBitIndex">The maximum bit index</param>
public Packet(byte[] buffer, int maxBitIndex)
{
this.buffer = new List<byte>(buffer);
this.maxBitIndex = maxBitIndex;
}
/// <summary>
/// Gets or sets the bit index.
/// </summary>
public int BitIndex
{
get
{
return bitIndex;
}
set
{
if (value < 0 || (value + 7) / 8 > buffer.Count) throw new ArgumentOutOfRangeException("Unable to set the bit index outside of the buffer size.");
bitIndex = value;
}
}
/// <summary>
/// Sets the bit index to the start of the data section.
/// </summary>
public void ResetBitIndex()
{
if (maxBitIndex < 32) throw new InvalidOperationException("Unable to set the bit index over the max bit index.");
bitIndex = 32;
}
/// <summary>
/// Gets or Sets the max bit index.
/// </summary>
public int MaxBitIndex
{
set
{
if (value < 0 || (value + 7) / 8 > buffer.Count) throw new ArgumentOutOfRangeException("Unable to set the max bit index outside of the buffer size.");
maxBitIndex = value;
}
}
public int BufferLength
{
get
{
return buffer.Count;
}
}
/// <summary>
/// Returns the buffer as an array of bytes.
/// </summary>
public byte[] Buffer
{
get
{
return buffer.ToArray();
}
}
public int HeaderSize
{
get
{
return 4;
}
}
/// <summary>
/// Returns a string of 0s and 1s representing the bits in the buffer. Good for debugging.
/// Places a space between nibbles and two spaces between bytes.
/// </summary>
/// <returns></returns>
public string Trace()
{
string s = string.Empty;
for (int copyBits = 0; copyBits < buffer.Count * 8; ++copyBits)
{
s += ((buffer[copyBits / 8] >> (7 - copyBits % 8)) & 0x1) == 0 ? "0" : "1";
if ((copyBits + 1) % 4 == 0 && copyBits != 0)
{
s += " ";
if ((copyBits + 1) % 8 == 0)
{
s += " ";
}
}
}
return s;
}
/// <summary>
/// Rounds the bitIndex up to a byte.
/// </summary>
public void RoundUpToByte()
{
bitIndex = (bitIndex + 7) / 8 * 8;
}
/// <summary>
///
/// </summary>
/// <returns>true if 32 bits exist in the buffer</returns>
public bool HasHeader()
{
return buffer.Count >= 4;
}
/// <summary>
/// Reads the length in bits stored in the header.
/// </summary>
public void ReadHeader()
{
int oldBitIndex = bitIndex;
bitIndex = 0;
maxBitIndex = 32;
uint length;
if (ReadUInt32(out length))
{
if (length > int.MaxValue)
{
maxBitIndex = int.MaxValue;
}
else
{
maxBitIndex = (int)length;
}
}
bitIndex = oldBitIndex;
}
/// <summary>
/// Writes a begin packet header of 32 bits.
/// </summary>
internal void BeginPacket()
{
WriteUInt32(0);
}
/// <summary>
/// Writes maxBitIndex to the packet header created with BeginPacket.
/// </summary>
internal void EndPacket()
{
int oldBitIndex = bitIndex;
bitIndex = 0;
WriteUInt32((uint)maxBitIndex);
bitIndex = oldBitIndex;
}
/// <summary>
/// Appends a byte directly to the end of the buffer.
/// </summary>
/// <param name="value"></param>
public void AppendByte(byte value)
{
buffer.Add(value);
bitIndex += 8;
}
/// <summary>
/// Determines if the packet has read in all the data defined by the maxBitIndex.
/// </summary>
/// <returns></returns>
public bool HasData()
{
return bitIndex >= maxBitIndex;
}
/// <summary>
/// Rollback as if the last read didn't occur.
/// </summary>
public void RollbackRead()
{
if (previousBitIndex == -1) throw new InvalidOperationException("A read method must be called successfully first");
bitIndex = previousBitIndex;
previousBitIndex = -1;
}
public void ClearBits(int bits)
{
if (bitIndex % 8 + bits - 1 < 8)
{
byte mask = 0xFF;
for (int bitItr = bitIndex; bitItr < bitIndex + bits; ++bitItr)
{
mask ^= (byte)(1 << 7 - bitItr % 8);
}
buffer[bitIndex / 8] &= mask;
}
else if (bitIndex % 8 + bits - 1 < 16)
{
int offset = 8 - bitIndex % 8;
buffer[bitIndex / 8] >>= offset;
buffer[bitIndex / 8] <<= offset;
offset = bits - (8 - bitIndex % 8);
buffer[bitIndex / 8 + 1] <<= offset;
buffer[bitIndex / 8 + 1] >>= offset;
}
else
{
int offset = 8 - bitIndex % 8;
buffer[bitIndex / 8] >>= offset;
buffer[bitIndex / 8] <<= offset;
for (int bitItr = bitIndex + 8 - bitIndex % 8; bitItr < (bitIndex + bits - 1) / 8 * 8; bitItr += 8)
{
buffer[bitItr / 8] = 0;
}
offset = bitIndex + bits - (bitIndex + bits - 1) / 8 * 8;
buffer[(bitIndex + bits - 1) / 8] <<= offset;
buffer[(bitIndex + bits - 1) / 8] >>= offset;
}
}
// Write Methods
/// <summary>
/// Writes an Event ID.
/// </summary>
/// <param name="value">The event ID normally stored in an enumeration.</param>
public void WriteEventID(uint value)
{
WriteVariableWidthUInt(value, 6);
}
/// <summary>
/// Writes a single bit either 0 or 1 into the buffer.
/// </summary>
/// <param name="value">The boolean value to write.</param>
public void WriteBool(bool value)
{
ExpandBuffer(1);
if (value) buffer[bitIndex / 8] |= (byte)(1 << (7 - bitIndex % 8));
++bitIndex;
}
/// <summary>
/// Writes an 8 bit unsigned byte into the buffer.
/// </summary>
/// <param name="value">The unsigned byte value to write.</param>
public void WriteByte(byte value)
{
ExpandBuffer(8);
int offset = bitIndex % 8;
buffer[bitIndex / 8] |= (byte)(value >> offset);
if (offset != 0)
{
buffer[bitIndex / 8 + 1] |= (byte)(value << 8 - offset);
}
bitIndex += 8;
}
/// <summary>
/// Writes an 8 bit signed byte into the buffer.
/// </summary>
/// <param name="value">The signed byte value to write.</param>
public void WriteSByte(sbyte value)
{
WriteByte((byte)value);
}
/// <summary>
/// Writes a 16 bit unsigned short into the buffer.
/// </summary>
/// <param name="value">The unsigned short value to write.</param>
public void WriteUInt16(ushort value)
{
ExpandBuffer(16);
int offset = bitIndex % 8;
buffer[bitIndex / 8] |= (byte)(value >> 8 + offset);
buffer[bitIndex / 8 + 1] |= (byte)(value >> offset);
if (offset != 0)
{
buffer[bitIndex / 8 + 2] |= (byte)(value << 8 - offset);
}
bitIndex += 16;
}
/// <summary>
/// Writes a 16 bit signed short into the buffer.
/// </summary>
/// <param name="value">The signed short value to write.</param>
public void WriteInt16(short value)
{
WriteUInt16((ushort)value);
}
/// <summary>
/// Writes a 32 bit unsigned integer into the buffer.
/// </summary>
/// <param name="value">The unsigned integer value to write.</param>
public void WriteUInt32(uint value)
{
ExpandBuffer(32);
int offset = bitIndex % 8;
buffer[bitIndex / 8] |= (byte)(value >> 24 + offset);
buffer[bitIndex / 8 + 1] |= (byte)(value >> 16 + offset);
buffer[bitIndex / 8 + 2] |= (byte)(value >> 8 + offset);
buffer[bitIndex / 8 + 3] |= (byte)(value >> offset);
if (offset != 0)
{
buffer[bitIndex / 8 + 4] |= (byte)(value << 8 - offset);
}
bitIndex += 32;
}
/// <summary>
/// Writes a 32 bit signed integer into the buffer.
/// </summary>
/// <param name="value">The signed integer value to write.</param>
public void WriteInt32(int value)
{
WriteUInt32((uint)value);
}
/// <summary>
/// Writes a 64 bit unsigned integer into the buffer.
/// </summary>
/// <param name="value">The unsigned integer value to write.</param>
public void WriteUInt64(ulong value)
{
ExpandBuffer(64);
int offset = bitIndex % 8;
buffer[bitIndex / 8] |= (byte)(value >> 56 + offset);
buffer[bitIndex / 8 + 1] |= (byte)(value >> 48 + offset);
buffer[bitIndex / 8 + 2] |= (byte)(value >> 32 + offset);
buffer[bitIndex / 8 + 3] |= (byte)(value >> 24 + offset);
buffer[bitIndex / 8 + 4] |= (byte)(value >> 16 + offset);
buffer[bitIndex / 8 + 5] |= (byte)(value >> 8 + offset);
buffer[bitIndex / 8 + 6] |= (byte)(value >> offset);
if (offset != 0)
{
buffer[bitIndex / 8 + 7] |= (byte)(value << 8 - offset);
}
bitIndex += 64;
}
/// <summary>
/// Writes a 64 bit signed integer into the buffer.
/// </summary>
/// <param name="value">The signed integer value to write.</param>
public void WriteInt64(long value)
{
WriteUInt64((ulong)value);
}
/// <summary>
/// Writes an n bit unsigned integer into the buffer.
/// </summary>
/// <param name="value">The unsigned integer value to write.</param>
/// <param name="bits">The number of bits to use.</param>
public void WriteUInt(uint value, int bits)
{
if (bits < 1 || bits > 32) throw new ArgumentOutOfRangeException("bits must be in the range (0, 32].");
if (bits != 32 && value > (0x1 << bits) - 1) throw new ArgumentOutOfRangeException("Value does not fit into " + bits.ToString() + " bits.");
ExpandBuffer(bits);
value <<= 32 - bits;
int offset = bitIndex % 8;
buffer[bitIndex / 8] |= (byte)(value >> 24 + offset);
if (offset + bits > 8)
{
buffer[bitIndex / 8 + 1] |= (byte)(value >> 16 + offset);
if (offset + bits > 16)
{
buffer[bitIndex / 8 + 2] |= (byte)(value >> 8 + offset);
if (offset + bits > 24)
{
buffer[bitIndex / 8 + 3] |= (byte)(value >> offset);
if (offset + bits > 32)
{
buffer[bitIndex / 8 + 4] |= (byte)(value << 8 - offset);
}
}
}
}
bitIndex += bits;
}
/// <summary>
/// Writes an n bit unsigned integer into the buffer.
/// </summary>
/// <param name="value">The unsigned integer value to write.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
public void WriteUInt(uint value, uint min, uint max)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (value < min || value > max) throw new ArgumentOutOfRangeException("The value must be on the interval [min, max]");
uint valueBase = max - min + 1;
// Log2
var bits = 32;
if (valueBase != 0)
{
int left = 0;
int right = 32;
for (int i = 0; i < 5; ++i)
{
if (valueBase >> (32 - (left + right) / 2) != 0)
{
// Left
right = (left + right) / 2;
}
else
{
// Right
left = (left + right) / 2;
}
}
bits = 32 - left;
}
WriteUInt(value - min, bits);
}
/// <summary>
/// Writes an n bit unsigned integer into the buffer.
/// </summary>
/// <param name="value">The unsigned integer value to write.</param>
/// <param name="bits">The number of bits to use.</param>
public void WriteUInt(ulong value, int bits)
{
if (bits < 1 || bits > 64) throw new ArgumentOutOfRangeException("bits must be in the range (0, 64].");
if (bits != 64 && value > ((ulong)0x1 << bits) - 1) throw new ArgumentOutOfRangeException("Value does not fit into " + bits.ToString() + " bits.");
ExpandBuffer(bits);
value <<= 64 - bits;
int offset = bitIndex % 8;
buffer[bitIndex / 8] |= (byte)(value >> 56 + offset);
if (offset + bits > 8)
{
buffer[bitIndex / 8 + 1] |= (byte)(value >> 48 + offset);
if (offset + bits > 16)
{
buffer[bitIndex / 8 + 2] |= (byte)(value >> 40 + offset);
if (offset + bits > 24)
{
buffer[bitIndex / 8 + 3] |= (byte)(value >> 32 + offset);
if (offset + bits > 32)
{
buffer[bitIndex / 8 + 4] |= (byte)(value >> 24 + offset);
if (offset + bits > 40)
{
buffer[bitIndex / 8 + 5] |= (byte)(value >> 16 + offset);
if (offset + bits > 48)
{
buffer[bitIndex / 8 + 6] |= (byte)(value >> 8 + offset);
if (offset + bits > 56)
{
buffer[bitIndex / 8 + 7] |= (byte)(value >> offset);
if (offset + bits > 64)
{
buffer[bitIndex / 8 + 8] |= (byte)(value << 8 - offset);
}
}
}
}
}
}
}
}
bitIndex += bits;
}
/// <summary>
/// Writes an n bit unsigned integer into the buffer.
/// </summary>
/// <param name="value">The unsigned integer value to write.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
public void WriteUInt(ulong value, ulong min, ulong max)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (value < min || value > max) throw new ArgumentOutOfRangeException("The value must be on the interval [min, max]");
ulong valueBase = max - min + 1;
// Log2
var bits = 64;
if (valueBase != 0)
{
int left = 0;
int right = 64;
for (int i = 0; i < 6; ++i)
{
if (valueBase >> (64 - (left + right) / 2) != 0)
{
// Left
right = (left + right) / 2;
}
else
{
// Right
left = (left + right) / 2;
}
}
bits = 64 - left;
}
WriteUInt(value - min, bits);
}
/// <summary>
/// Writes an n bit signed integer into the buffer.
/// </summary>
/// <param name="value">The signed integer value to write.</param>
/// <param name="bits">The number of bits to use.</param>
public void WriteInt(int value, int bits)
{
if (bits < 1 || bits > 32) throw new ArgumentOutOfRangeException("bits must be in the range (0, 32].");
if (bits != 32 && (value < -(0x1 << (bits - 1)) || value >= 0x1 << (bits - 1))) throw new ArgumentOutOfRangeException("Value does not fit into " + bits.ToString() + " bits.");
ExpandBuffer(bits);
value <<= 32 - bits;
uint uvalue = (uint)value;
int offset = bitIndex % 8;
buffer[bitIndex / 8] |= (byte)(uvalue >> 24 + offset);
if (offset + bits > 8)
{
buffer[bitIndex / 8 + 1] |= (byte)(uvalue >> 16 + offset);
if (offset + bits > 16)
{
buffer[bitIndex / 8 + 2] |= (byte)(uvalue >> 8 + offset);
if (offset + bits > 24)
{
buffer[bitIndex / 8 + 3] |= (byte)(uvalue >> offset);
if (offset + bits > 32)
{
buffer[bitIndex / 8 + 4] |= (byte)(uvalue << 8 - offset);
}
}
}
}
bitIndex += bits;
}
/// <summary>
/// Writes an n bit signed integer into the buffer.
/// </summary>
/// <param name="value">The signed integer value to write.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
public void WriteInt(int value, int min, int max)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (value < min || value > max) throw new ArgumentOutOfRangeException("The value must be on the interval [min, max]");
uint valueBase = (uint)(max - min + 1);
// Log2
var bits = 32;
if (valueBase != 0)
{
int left = 0;
int right = 32;
for (int i = 0; i < 5; ++i)
{
if (valueBase >> (32 - (left + right) / 2) != 0)
{
// Left
right = (left + right) / 2;
}
else
{
// Right
left = (left + right) / 2;
}
}
bits = 32 - left;
}
WriteInt(value - min, bits);
}
/// <summary>
/// Writes an n bit signed integer into the buffer.
/// </summary>
/// <param name="value">The signed integer value to write.</param>
/// <param name="bits">The number of bits to use.</param>
public void WriteInt(long value, int bits)
{
if (bits < 1 || bits > 64) throw new ArgumentOutOfRangeException("bits must be in the range (0, 64].");
if (bits != 64 && (value < -((long)0x1 << (bits - 1)) || value >= (long)0x1 << (bits - 1))) throw new ArgumentOutOfRangeException("Value does not fit into " + bits.ToString() + " bits.");
ExpandBuffer(bits);
value <<= 64 - bits;
ulong uvalue = (ulong)value;
int offset = bitIndex % 8;
buffer[bitIndex / 8] |= (byte)(uvalue >> 56 + offset);
if (offset + bits > 8)
{
buffer[bitIndex / 8 + 1] |= (byte)(uvalue >> 48 + offset);
if (offset + bits > 16)
{
buffer[bitIndex / 8 + 2] |= (byte)(uvalue >> 40 + offset);
if (offset + bits > 24)
{
buffer[bitIndex / 8 + 3] |= (byte)(uvalue >> 32 + offset);
if (offset + bits > 32)
{
buffer[bitIndex / 8 + 4] |= (byte)(uvalue >> 24 + offset);
if (offset + bits > 40)
{
buffer[bitIndex / 8 + 5] |= (byte)(uvalue >> 16 + offset);
if (offset + bits > 48)
{
buffer[bitIndex / 8 + 6] |= (byte)(uvalue >> 8 + offset);
if (offset + bits > 56)
{
buffer[bitIndex / 8 + 7] |= (byte)(uvalue >> offset);
if (offset + bits > 64)
{
buffer[bitIndex / 8 + 8] |= (byte)(uvalue << 8 - offset);
}
}
}
}
}
}
}
}
bitIndex += bits;
}
/// <summary>
/// Writes an n bit signed integer into the buffer.
/// </summary>
/// <param name="value">The signed integer value to write.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
public void WriteInt(long value, long min, long max)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (value < min || value > max) throw new ArgumentOutOfRangeException("The value must be on the interval [min, max]");
ulong valueBase = (ulong)(max - min + 1);
// Log2
var bits = 64;
if (valueBase != 0)
{
int left = 0;
int right = 64;
for (int i = 0; i < 6; ++i)
{
if (valueBase >> (64 - (left + right) / 2) != 0)
{
// Left
right = (left + right) / 2;
}
else
{
// Right
left = (left + right) / 2;
}
}
bits = 64 - left;
}
WriteInt(value - min, bits);
}
/// <summary>
/// Writes a 32 bit single into the buffer.
/// </summary>
/// <param name="value">The single value to write.</param>
public void WriteSingle(float value)
{
WriteUInt32(BitConverter.ToUInt32(BitConverter.GetBytes(value), 0));
}
/// <summary>
/// Writes a 64 bit double into the buffer.
/// </summary>
/// <param name="value">The double value to write.</param>
public void WriteDouble(double value)
{
byte[] bytes = BitConverter.GetBytes(value);
WriteUInt32(BitConverter.ToUInt32(bytes, 0));
WriteUInt32(BitConverter.ToUInt32(bytes, 4));
}
/// <summary>
/// Writes an integer using a variable width encoding of bits. Choose a bits value that represents the number of bits to hold the average value.
/// </summary>
/// <param name="value">The unsigned integer value to write.</param>
/// <param name="bits">The number of bits to use for the sequence.</param>
public void WriteVariableWidthUInt(uint value, int bits = 4)
{
if (bits < 1 || bits > 32) throw new ArgumentOutOfRangeException("bits must be in the range (0, 32].");
int shift = bits;
// Stop when our value can fit inside
for (; shift < 32 && value >= (0x1 << shift); shift += bits)
{
WriteBool(true); // Write a 1 for a continuation bit signifying one more interval is needed
}
if (shift < 32)
{
WriteBool(false); // Write a 0 for a continuation bit signifying the end
}
WriteUInt(value, shift >= 32 ? 32 : shift);
}
/// <summary>
/// Writes an integer using a variable width encoding of bits. Choose a bits value that represents the number of bits to hold the average value.
/// </summary>
/// <param name="value">The unsigned integer value to write.</param>
/// <param name="bits">The number of bits to use for the sequence.</param>
public void WriteVariableWidthUInt(ulong value, int bits = 4)
{
if (bits < 1 || bits > 64) throw new ArgumentOutOfRangeException("bits must be in the range (0, 64].");
int shift = bits;
// Stop when our value can fit inside
for (; shift < 64 && value >= (ulong)(0x1 << shift); shift += bits)
{
WriteBool(true); // Write a 1 for a continuation bit signifying one more interval is needed
}
if (shift < 64)
{
WriteBool(false); // Write a 0 for a continuation bit signifying the end
}
WriteUInt(value, shift >= 64 ? 64 : shift);
}
/// <summary>
/// Writes an integer using a variable length of bits. Choose a bits value that represents the number of bits to hold the average value.
/// </summary>
/// <param name="value">The signed integer value to write.</param>
/// <param name="bits">The number of bits to use for the sequence.</param>
public void WriteVariableWidthInt(int value, int bits = 4)
{
if (bits < 1 || bits > 32) throw new ArgumentOutOfRangeException("bits must be in the range (0, 32].");
int shift = bits;
// Stop when our value can fit inside
for (; shift < 32 && (value < -(0x1 << (shift - 1)) || value >= 0x1 << (shift - 1)); shift += bits)
{
WriteBool(true); // Write a 1 for a continuation bit signifying one more interval is needed
}
if (shift < 32)
{
WriteBool(false); // Write a 0 for a continuation bit signifying the end
}
WriteInt(value, shift >= 32 ? 32 : shift);
}
/// <summary>
/// Writes an integer using a variable length of bits. Choose a bits value that represents the number of bits to hold the average value.
/// </summary>
/// <param name="value">The signed integer value to write.</param>
/// <param name="bits">The number of bits to use for the sequence.</param>
public void WriteVariableWidthInt(long value, int bits = 4)
{
if (bits < 1 || bits > 64) throw new ArgumentOutOfRangeException("bits must be in the range (0, 64].");
int shift = bits;
// Stop when our value can fit inside
for (; shift < 64 && (value < -(0x1 << (shift - 1)) || value >= 0x1 << (shift - 1)); shift += bits)
{
WriteBool(true); // Write a 1 for a continuation bit signifying one more interval is needed
}
if (shift < 64)
{
WriteBool(false); // Write a 0 for a continuation bit signifying the end
}
WriteInt(value, shift >= 64 ? 64 : shift);
}
/// <summary>
/// Writes an array of unsigned integers in a given range defining a base.
/// </summary>
/// <param name="value">The array of unsigned integers to write.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
public void WriteArrayUInt32(uint[] value, int min, int max)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
ulong valueBase = (ulong)(max - min + 1);
var sum = new List<ulong>();
var valueBasePow = new List<ulong>();
valueBasePow.Add(1);
for (var i = 0; i < value.Length; ++i)
{
// sum += arrayBasePow * array[i];
ulong multiplyCarry = 0;
ulong addCarry = 0;
for (var j = 0; j < valueBasePow.Count || multiplyCarry != 0 || j < sum.Count || addCarry != 0; ++j)
{
if (j >= sum.Count)
{
sum.Add(0);
}
ulong temp = 0;
ulong product = 0;
if (j < valueBasePow.Count)
{
product = (ulong)(value[i] - min) * valueBasePow[j];
}
temp += product + multiplyCarry;
multiplyCarry = temp >> 32;
temp &= 0xFFFFFFFF;
sum[j] += temp + addCarry;
addCarry = sum[j] >> 32;
sum[j] &= 0xFFFFFFFF;
}
// arrayBasePow *= arrayBase;
multiplyCarry = 0;
for (var j = 0; j < valueBasePow.Count || multiplyCarry != 0; ++j)
{
if (j >= valueBasePow.Count)
{
valueBasePow.Add(0);
}
ulong product = 0;
if (j < valueBasePow.Count)
{
product = valueBase * valueBasePow[j];
}
valueBasePow[j] = product + multiplyCarry;
multiplyCarry = valueBasePow[j] >> 32;
valueBasePow[j] &= 0xFFFFFFFF;
}
}
for (var i = sum.Count - 1; i > 0 && sum[i] == 0; --i)
{
sum.RemoveAt(sum.Count - 1);
}
// Log2
int numberOfBits = 0;
if (!(valueBasePow.Count == 1 && valueBasePow[0] == 0))
{
int left = 0;
int right = 32;
for (int i = 0; i < 5; ++i)
{
if (valueBasePow[valueBasePow.Count - 1] >> (32 - (left + right) / 2) != 0)
{
// Left
right = (left + right) / 2;
}
else
{
// Right
left = (left + right) / 2;
}
}
numberOfBits = (valueBasePow.Count - 1) * 32 + (32 - left);
}
for (int copyBits = 0; copyBits < numberOfBits; copyBits += 32)
{
WriteUInt((uint)sum[copyBits / 32], numberOfBits - copyBits > 32 ? 32 : numberOfBits - copyBits);
}
}
/// <summary>
/// Writes an array of signed integers in a given range defining a base.
/// </summary>
/// <param name="value">The array of signed integers to write.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
public void WriteArrayInt32(int[] value, int min, int max)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
var uValue = new uint[value.Length];
for (var i = 0; i < value.Length; ++i)
{
uValue[i] = (uint)(value[i] - min);
}
WriteArrayUInt32(uValue, 0, max - min);
}
/// <summary>
/// Writes an array of singles in a given range converted to a given integral base.
/// Creates a value for the ratio: value / (valueBase - 1) in relationship to value / (max - min).
/// </summary>
/// <param name="value">The array of singles to write.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="valueBase">The base to convert the singles to.</param>
public void WriteArraySingle(float[] value, float min, float max, int valueBase)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
var uValue = new uint[value.Length];
if (min < 0 && max > 0)
{
for (var i = 0; i < value.Length; ++i)
{
uValue[i] = value[i] == 0 ? 0 : (uint)Math.Round((value[i] - min) / (max - min) * (valueBase - 2)) + 1;
}
}
else
{
for (var i = 0; i < value.Length; ++i)
{
uValue[i] = (uint)Math.Round((value[i] - min) / (max - min) * (valueBase - 1));
}
}
WriteArrayUInt32(uValue, 0, valueBase - 1);
}
/// <summary>
/// Writes an array of doubles in a given range converted to a given integral base.
/// Creates a value for the ratio: value / (valueBase - 1) in relationship to value / (max - min).
/// </summary>
/// <param name="value">The array of doubles to write.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="valueBase">The base to convert the doubles to.</param>
public void WriteArrayDouble(double[] value, double min, double max, int valueBase)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
var uValue = new uint[value.Length];
if (min < 0 && max > 0)
{
for (var i = 0; i < value.Length; ++i)
{
uValue[i] = value[i] == 0 ? 0 : (uint)Math.Round((value[i] - min) / (max - min) * (valueBase - 2)) + 1;
}
}
else
{
for (var i = 0; i < value.Length; ++i)
{
uValue[i] = (uint)Math.Round((value[i] - min) / (max - min) * (valueBase - 1));
}
}
WriteArrayUInt32(uValue, 0, valueBase - 1);
}
/// <summary>
/// Creates a value for the ratio: value / (2 ^ bitResolution - 1) in relationship to value / (max - min).
/// This allows a floating point to have a resolution.
/// </summary>
/// <param name="value">The floating point value in the range.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="bitResolution">The number of bits to use for the ratio.</param>
public void WriteCustomResolutionSingle(float value, float min, float max, int bitResolution)
{
if (bitResolution < 1 || bitResolution > 31) throw new ArgumentOutOfRangeException("bitResolution must be in the range (0, 32).");
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (value < min || value > max) throw new ArgumentOutOfRangeException("The value must be on the interval [min, max]");
uint uValue;
if (min < 0 && max > 0)
{
uValue = value == 0 ? 0 : (uint)Math.Round((value - min) / (max - min) * (float)((0x1 << bitResolution) - 2)) + 1;
}
else
{
uValue = (uint)Math.Round((value - min) / (max - min) * (float)((0x1 << bitResolution) - 1));
}
WriteUInt(uValue, bitResolution);
}
/// <summary>
/// Creates a value for the ratio: value / (2 ^ bitResolution - 1) in relationship to value / (max - min).
/// This allows a floating point to have a resolution.
/// </summary>
/// <param name="value">The double value in the range.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="bitResolution">The number of bits to use for the ratio.</param>
public void WriteCustomResolutionDouble(double value, double min, double max, int bitResolution)
{
if (bitResolution < 1 || bitResolution > 31) throw new ArgumentOutOfRangeException("bitResolution must be in the range (0, 32).");
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (value < min || value > max) throw new ArgumentOutOfRangeException("The value must be on the interval [min, max]");
uint uValue;
if (min < 0 && max > 0)
{
uValue = value == 0 ? 0 : (uint)Math.Round((value - min) / (max - min) * (double)((0x1 << bitResolution) - 2)) + 1;
}
else
{
uValue = (uint)Math.Round((value - min) / (max - min) * (double)((0x1 << bitResolution) - 1));
}
WriteUInt(uValue, bitResolution);
}
/// <summary>
/// Writes the length of the string using WriteVariableWidthUInt and then writes a boolean, true for unicode, false for ascii.
/// ASCII uses a compression step using either 6 or 7 bits per character.
/// </summary>
/// <param name="value">The string value to write.</param>
/// <param name="bits">The number of bits for the length written using an unsigned variable-width integer.</param>
public void WriteString(string value, int bits = 4)
{
if (bits < 1 || bits > 31) throw new ArgumentOutOfRangeException("bits must be in the range (0, 32).");
var asciiBytes = Encoding.ASCII.GetBytes(value);
uint size = (uint)asciiBytes.Length;
WriteVariableWidthUInt(size, bits);
foreach (byte asciiByte in asciiBytes)
{
WriteByte(asciiByte);
}
}
/// <summary>
/// Appends a binary packet to the buffer.
/// </summary>
/// <param name="value">The binary packet to write.</param>
public void WriteBinaryPacket(Packet value)
{
int oldBitIndex = value.bitIndex;
value.bitIndex = 0;
int valueMaxBitIndex = value.maxBitIndex;
WriteVariableWidthUInt((uint)valueMaxBitIndex);
for (int copyBits = 0; copyBits < valueMaxBitIndex; copyBits += 32)
{
uint uValue;
value.ReadUInt(out uValue, valueMaxBitIndex - copyBits > 32 ? 32 : valueMaxBitIndex - copyBits);
WriteUInt(uValue, valueMaxBitIndex - copyBits > 32 ? 32 : valueMaxBitIndex - copyBits);
}
bitIndex += (int)valueMaxBitIndex;
value.bitIndex = oldBitIndex;
}
//READ METHODS
/// <summary>
/// Reads an Event ID.
/// </summary>
/// <param name="value">The event ID normally stored in an enumeration.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadEventID(out uint value, bool nestedRead = false)
{
return ReadVariableWidthUInt(out value, 6);
}
/// <summary>
/// Reads one bit from the buffer.
/// </summary>
/// <param name="value">Boolean.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadBool(out bool value, bool nestedRead = false)
{
if (!nestedRead) previousBitIndex = bitIndex;
value = false;
if ((bitIndex + 1 + 7) / 8 > buffer.Count)
{
return false;
}
value = ((buffer[bitIndex / 8] >> (7 - bitIndex % 8)) & 0x1) == 1;
++bitIndex;
return true;
}
/// <summary>
/// Reads an 8 bits unsigned byte from the buffer.
/// </summary>
/// <param name="value">Unsigned Byte.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadByte(out byte value, bool nestedRead = false)
{
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + 8 + 7) / 8 > buffer.Count)
{
return false;
}
int offset = bitIndex % 8;
value |= (byte)(buffer[bitIndex / 8] << offset);
if (offset != 0)
{
value |= (byte)(buffer[bitIndex / 8 + 1] >> 8 - offset);
}
bitIndex += 8;
return true;
}
/// <summary>
/// Reads an 8 bits signed byte from the buffer.
/// </summary>
/// <param name="value">Signed Byte.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadSByte(out sbyte value, bool nestedRead = false)
{
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + 8 + 7) / 8 > buffer.Count)
{
return false;
}
int offset = bitIndex % 8;
value |= (sbyte)(buffer[bitIndex / 8] << offset);
if (offset != 0)
{
value |= (sbyte)(buffer[bitIndex / 8 + 1] >> 8 - offset);
}
bitIndex += 8;
return true;
}
/// <summary>
/// Reads a 16 bit unsigned short from the buffer.
/// </summary>
/// <param name="value">Unsigned Short.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadUInt16(out ushort value, bool nestedRead = false)
{
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + 16 + 7) / 8 > buffer.Count)
{
return false;
}
int offset = bitIndex % 8;
value |= (ushort)(buffer[bitIndex / 8] << 8 + offset);
value |= (ushort)(buffer[bitIndex / 8 + 1] << offset);
if (offset != 0)
{
value |= (ushort)(buffer[bitIndex / 8 + 2] >> 8 - offset);
}
bitIndex += 16;
return true;
}
/// <summary>
/// Reads a 16 bit signed short from the buffer.
/// </summary>
/// <param name="value">Signed Short.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadInt16(out short value, bool nestedRead = false)
{
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + 16 + 7) / 8 > buffer.Count)
{
return false;
}
int offset = bitIndex % 8;
value |= (short)(buffer[bitIndex / 8] << 8 + offset);
value |= (short)(buffer[bitIndex / 8 + 1] << offset);
if (offset != 0)
{
value |= (short)(buffer[bitIndex / 8 + 2] >> 8 - offset);
}
bitIndex += 16;
return true;
}
/// <summary>
/// Reads a 32 bit unsigned integer from the buffer.
/// </summary>
/// <param name="value">Unsigned Integer.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadUInt32(out uint value, bool nestedRead = false)
{
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + 32 + 7) / 8 > buffer.Count)
{
return false;
}
int offset = bitIndex % 8;
value |= (uint)(buffer[bitIndex / 8] << 24 + offset);
value |= (uint)(buffer[bitIndex / 8 + 1] << 16 + offset);
value |= (uint)(buffer[bitIndex / 8 + 2] << 8 + offset);
value |= (uint)(buffer[bitIndex / 8 + 3] << offset);
if (offset != 0)
{
value |= (uint)(buffer[bitIndex / 8 + 4] >> 8 - offset);
}
bitIndex += 32;
return true;
}
/// <summary>
/// Reads a 32 bit signed integer from the buffer.
/// </summary>
/// <param name="value">Signed Integer.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadInt32(out int value, bool nestedRead = false)
{
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + 32 + 7) / 8 > buffer.Count)
{
return false;
}
int offset = bitIndex % 8;
value |= (int)(buffer[bitIndex / 8] << 24 + offset);
value |= (int)(buffer[bitIndex / 8 + 1] << 16 + offset);
value |= (int)(buffer[bitIndex / 8 + 2] << 8 + offset);
value |= (int)(buffer[bitIndex / 8 + 3] << offset);
if (offset != 0)
{
value |= (int)(buffer[bitIndex / 8 + 4] >> 8 - offset);
}
bitIndex += 32;
return true;
}
/// <summary>
/// Reads a 64 bit unsigned integer from the buffer.
/// </summary>
/// <param name="value">Unsigned Long.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadUInt64(out ulong value, bool nestedRead = false)
{
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + 64 + 7) / 8 > buffer.Count)
{
return false;
}
int offset = bitIndex % 8;
value |= (ulong)(buffer[bitIndex / 8]) << 56 + offset;
value |= (ulong)(buffer[bitIndex / 8 + 1]) << 48 + offset;
value |= (ulong)(buffer[bitIndex / 8 + 2]) << 40 + offset;
value |= (ulong)(buffer[bitIndex / 8 + 3]) << 32 + offset;
value |= (ulong)(buffer[bitIndex / 8 + 4]) << 24 + offset;
value |= (ulong)(buffer[bitIndex / 8 + 5]) << 16 + offset;
value |= (ulong)(buffer[bitIndex / 8 + 6]) << 8 + offset;
value |= (ulong)(buffer[bitIndex / 8 + 7]) << offset;
if (offset != 0)
{
value |= (ulong)(buffer[bitIndex / 8 + 8]) >> 8 - offset;
}
bitIndex += 64;
return true;
}
/// <summary>
/// Reads a 64 bit signed integer from the buffer.
/// </summary>
/// <param name="value">Signed Long.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadInt64(out long value, bool nestedRead = false)
{
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + 64 + 7) / 8 > buffer.Count)
{
return false;
}
int offset = bitIndex % 8;
value |= (long)(buffer[bitIndex / 8]) << 56 + offset;
value |= (long)(buffer[bitIndex / 8 + 1]) << 48 + offset;
value |= (long)(buffer[bitIndex / 8 + 2]) << 40 + offset;
value |= (long)(buffer[bitIndex / 8 + 3]) << 32 + offset;
value |= (long)(buffer[bitIndex / 8 + 4]) << 24 + offset;
value |= (long)(buffer[bitIndex / 8 + 5]) << 16 + offset;
value |= (long)(buffer[bitIndex / 8 + 6]) << 8 + offset;
value |= (long)(buffer[bitIndex / 8 + 7]) << offset;
if (offset != 0)
{
value |= (long)(buffer[bitIndex / 8 + 8]) >> 8 - offset;
}
bitIndex += 64;
return true;
}
/// <summary>
/// Reads an n bit unsigned integer from the buffer.
/// </summary>
/// <param name="value">Unsigned Integer.</param>
/// <param name="bits">The number of bits used to write.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadUInt(out uint value, int bits, bool nestedRead = false)
{
if (bits < 1 || bits > 32) throw new ArgumentOutOfRangeException("bits must be in the range (0, 32].");
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + bits + 7) / 8 > buffer.Count)
{
return false;
}
int offset = bitIndex % 8;
value = (uint)buffer[bitIndex / 8] << 24 + offset;
if (offset + bits > 8)
{
value |= (uint)buffer[bitIndex / 8 + 1] << 16 + offset;
if (offset + bits > 16)
{
value |= (uint)buffer[bitIndex / 8 + 2] << 8 + offset;
if (offset + bits > 24)
{
value |= (uint)buffer[bitIndex / 8 + 3] << offset;
if (offset + bits > 32)
{
value |= (uint)buffer[bitIndex / 8 + 4] >> 8 - offset;
}
}
}
}
value >>= 32 - bits;
bitIndex += bits;
return true;
}
/// <summary>
/// Reads an n bit unsigned integer from the buffer.
/// </summary>
/// <param name="value">Unsigned Integer.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadUInt(out uint value, uint min, uint max, bool nestedRead = false)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (!nestedRead) previousBitIndex = bitIndex;
uint valueBase = max - min + 1;
// Log2
var bits = 32;
if (valueBase != 0)
{
int left = 0;
int right = 32;
for (int i = 0; i < 5; ++i)
{
if (valueBase >> (32 - (left + right) / 2) != 0)
{
// Left
right = (left + right) / 2;
}
else
{
// Right
left = (left + right) / 2;
}
}
bits = 32 - left;
}
if (!ReadUInt(out value, bits, true)) return false;
value += min;
return true;
}
/// <summary>
/// Reads an n bit unsigned integer from the buffer.
/// </summary>
/// <param name="value">Unsigned Integer.</param>
/// <param name="bits">The number of bits used to write.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadUInt(out ulong value, int bits, bool nestedRead = false)
{
if (bits < 1 || bits > 64) throw new ArgumentOutOfRangeException("bits must be in the range (0, 64].");
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + bits + 7) / 8 > buffer.Count)
{
return false;
}
int offset = bitIndex % 8;
value = (ulong)buffer[bitIndex / 8] << 56 + offset;
if (offset + bits > 8)
{
value |= (ulong)buffer[bitIndex / 8 + 1] << 48 + offset;
if (offset + bits > 16)
{
value |= (ulong)buffer[bitIndex / 8 + 2] << 40 + offset;
if (offset + bits > 24)
{
value |= (ulong)buffer[bitIndex / 8 + 3] << 32 + offset;
if (offset + bits > 32)
{
value |= (ulong)buffer[bitIndex / 8 + 4] << 24 + offset;
if (offset + bits > 40)
{
value |= (ulong)buffer[bitIndex / 8 + 5] << 16 + offset;
if (offset + bits > 48)
{
value |= (ulong)buffer[bitIndex / 8 + 6] << 8 + offset;
if (offset + bits > 56)
{
value |= (ulong)buffer[bitIndex / 8 + 7] << offset;
if (offset + bits > 64)
{
value |= (ulong)buffer[bitIndex / 8 + 8] >> 8 - offset;
}
}
}
}
}
}
}
}
value >>= 64 - bits;
bitIndex += bits;
return true;
}
/// <summary>
/// Reads an n bit unsigned integer from the buffer.
/// </summary>
/// <param name="value">Unsigned Integer.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadUInt(out ulong value, ulong min, ulong max, bool nestedRead = false)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (!nestedRead) previousBitIndex = bitIndex;
ulong valueBase = max - min + 1;
// Log2
var bits = 64;
if (valueBase != 0)
{
int left = 0;
int right = 64;
for (int i = 0; i < 6; ++i)
{
if (valueBase >> (64 - (left + right) / 2) != 0)
{
// Left
right = (left + right) / 2;
}
else
{
// Right
left = (left + right) / 2;
}
}
bits = 64 - left;
}
if (!ReadUInt(out value, bits, true)) return false;
value += min;
return true;
}
/// <summary>
/// Reads an n bit custom signed integer from the buffer.
/// </summary>
/// <param name="value">Signed Integer.</param>
/// <param name="bits">The number of bits used to write.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadInt(out int value, int bits, bool nestedRead = false)
{
if (bits < 1 || bits > 32) throw new ArgumentOutOfRangeException("bits must be in the range (0, 32].");
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + bits + 7) / 8 > buffer.Count)
{
return false;
}
int offset = bitIndex % 8;
value = buffer[bitIndex / 8] << 24 + offset;
if (offset + bits > 8)
{
value |= buffer[bitIndex / 8 + 1] << 16 + offset;
if (offset + bits > 16)
{
value |= buffer[bitIndex / 8 + 2] << 8 + offset;
if (offset + bits > 24)
{
value |= buffer[bitIndex / 8 + 3] << offset;
if (offset + bits > 32)
{
value |= buffer[bitIndex / 8 + 4] >> 8 - offset;
}
}
}
}
value >>= 32 - bits;
bitIndex += bits;
return true;
}
/// <summary>
/// Reads an n bit custom signed integer from the buffer.
/// </summary>
/// <param name="value">Signed Integer.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadInt(out int value, int min, int max, bool nestedRead = false)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (!nestedRead) previousBitIndex = bitIndex;
uint valueBase = (uint)(max - min + 1);
// Log2
var bits = 32;
if (valueBase != 0)
{
int left = 0;
int right = 32;
for (int i = 0; i < 5; ++i)
{
if (valueBase >> (32 - (left + right) / 2) != 0)
{
// Left
right = (left + right) / 2;
}
else
{
// Right
left = (left + right) / 2;
}
}
bits = 32 - left;
}
if (!ReadInt(out value, bits, true)) return false;
value += min;
return true;
}
/// <summary>
/// Reads an n bit custom signed integer from the buffer.
/// </summary>
/// <param name="value">Signed Integer.</param>
/// <param name="bits">The number of bits used to write.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadInt(out long value, int bits, bool nestedRead = false)
{
if (bits < 1 || bits > 64) throw new ArgumentOutOfRangeException("bits must be in the range (0, 64].");
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + bits + 7) / 8 > buffer.Count)
{
return false;
}
int offset = bitIndex % 8;
value = buffer[bitIndex / 8] << 56 + offset;
if (offset + bits > 8)
{
value |= (long)buffer[bitIndex / 8 + 1] << 48 + offset;
if (offset + bits > 16)
{
value |= (long)buffer[bitIndex / 8 + 2] << 40 + offset;
if (offset + bits > 24)
{
value |= (long)buffer[bitIndex / 8 + 3] << 32 + offset;
if (offset + bits > 32)
{
value |= (long)buffer[bitIndex / 8 + 4] << 24 + offset;
if (offset + bits > 40)
{
value |= (long)buffer[bitIndex / 8 + 5] << 16 + offset;
if (offset + bits > 48)
{
value |= (long)buffer[bitIndex / 8 + 6] << 8 + offset;
if (offset + bits > 56)
{
value |= (long)buffer[bitIndex / 8 + 7] << offset;
if (offset + bits > 64)
{
value |= (long)buffer[bitIndex / 8 + 8] >> 8 - offset;
}
}
}
}
}
}
}
}
value >>= 64 - bits;
bitIndex += bits;
return true;
}
/// <summary>
/// Reads an n bit custom signed integer from the buffer.
/// </summary>
/// <param name="value">Signed Integer.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadInt(out long value, long min, long max, bool nestedRead = false)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (!nestedRead) previousBitIndex = bitIndex;
ulong valueBase = (ulong)(max - min + 1);
// Log2
var bits = 64;
if (valueBase != 0)
{
int left = 0;
int right = 64;
for (int i = 0; i < 6; ++i)
{
if (valueBase >> (64 - (left + right) / 2) != 0)
{
// Left
right = (left + right) / 2;
}
else
{
// Right
left = (left + right) / 2;
}
}
bits = 64 - left;
}
if (!ReadInt(out value, bits, true)) return false;
value += min;
return true;
}
/// <summary>
/// Reads a 32 bit single from the buffer.
/// </summary>
/// <param name="value">Single.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadSingle(out float value, bool nestedRead = false)
{
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + 32 + 7) / 8 > buffer.Count)
{
return false;
}
uint uValue;
if (!ReadUInt32(out uValue, true)) return false;
value = BitConverter.ToSingle(BitConverter.GetBytes(uValue), 0);
return true;
}
/// <summary>
/// Reads a 64 bit double from the buffer.
/// </summary>
/// <param name="value">Double.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadDouble(out double value, bool nestedRead = false)
{
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
if ((bitIndex + 64 + 7) / 8 > buffer.Count)
{
return false;
}
byte[] bytes = new byte[8];
uint uValue1;
if (!ReadUInt32(out uValue1, true)) return false;
byte[] first4bytes = BitConverter.GetBytes(uValue1);
uint uValue2;
if (!ReadUInt32(out uValue2, true)) return false;
byte[] last4bytes = BitConverter.GetBytes(uValue2);
for (uint copyBytes = 0; copyBytes < 4; ++copyBytes)
{
bytes[copyBytes] = first4bytes[copyBytes];
}
for (uint copyBytes = 4; copyBytes < 8; ++copyBytes)
{
bytes[copyBytes] = last4bytes[copyBytes - 4];
}
value = BitConverter.ToDouble(bytes, 0);
return true;
}
/// <summary>
/// Reads the variable-width unsigned integer.
/// </summary>
/// <param name="value">Unsigned Integer.</param>
/// <param name="bits">The bit size of the sequence used to write.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadVariableWidthUInt(out uint value, int bits = 4, bool nestedRead = false)
{
if (bits < 1 || bits > 32) throw new ArgumentOutOfRangeException("bits must be in the range (0, 32].");
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
int valueBitCount = bits;
bool continuationBitValue = true;
do
{
if (!ReadBool(out continuationBitValue, true)) return false;
if (continuationBitValue) valueBitCount += bits;
}
while (continuationBitValue && valueBitCount < 32);
return ReadUInt(out value, valueBitCount >= 32 ? 32 : valueBitCount, true);
}
/// <summary>
/// Reads the variable-width unsigned integer.
/// </summary>
/// <param name="value">Unsigned Integer.</param>
/// <param name="bits">The bit size of the sequence used to write.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadVariableWidthUInt(out ulong value, int bits = 4, bool nestedRead = false)
{
if (bits < 1 || bits > 64) throw new ArgumentOutOfRangeException("bits must be in the range (0, 64].");
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
int valueBitCount = bits;
bool continuationBitValue = true;
do
{
if (!ReadBool(out continuationBitValue, true)) return false;
if (continuationBitValue) valueBitCount += bits;
}
while (continuationBitValue && valueBitCount < 64);
return ReadUInt(out value, valueBitCount >= 64 ? 64 : valueBitCount, true);
}
/// <summary>
/// Reads the variable-width signed integer.
/// </summary>
/// <param name="value">Signed Integer.</param>
/// <param name="bits">The bit size of the sequence used to write.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadVariableWidthInt(out int value, int bits = 4, bool nestedRead = false)
{
if (bits < 1 || bits > 32) throw new ArgumentOutOfRangeException("bits must be in the range (0, 32].");
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
int valueBitCount = bits;
bool continuationBitValue = true;
do
{
if (!ReadBool(out continuationBitValue, true)) return false;
if (continuationBitValue) valueBitCount += bits;
}
while (continuationBitValue && valueBitCount < 32);
return ReadInt(out value, valueBitCount >= 32 ? 32 : valueBitCount, true);
}
/// <summary>
/// Reads the variable-width signed integer.
/// </summary>
/// <param name="value">Signed Integer.</param>
/// <param name="bits">The bit size of the sequence used to write.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadVariableWidthInt(out long value, int bits = 4, bool nestedRead = false)
{
if (bits < 1 || bits > 64) throw new ArgumentOutOfRangeException("bits must be in the range (0, 64].");
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
int valueBitCount = bits;
bool continuationBitValue = true;
do
{
if (!ReadBool(out continuationBitValue, true)) return false;
if (continuationBitValue) valueBitCount += bits;
}
while (continuationBitValue && valueBitCount < 64);
return ReadInt(out value, valueBitCount >= 64 ? 64 : valueBitCount, true);
}
/// <summary>
/// Reads an array of unsigned integers in the given range defining a base.
/// </summary>
/// <param name="value">Array of Unsigned Integers.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadArrayUInt32(uint[] value, int min, int max, bool nestedRead = false)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (!nestedRead) previousBitIndex = bitIndex;
ulong valueBase = (ulong)(max - min + 1);
var exponent = value.Length;
var powResult = new List<ulong>();
powResult.Add(1);
var newPowResult = new List<ulong>();
var valueBaseTemp = new List<ulong>();
valueBaseTemp.Add(valueBase);
var newValueBaseTemp = new List<ulong>();
// Pow(value.size, arrayBase) using exponentiation by squaring
while (exponent != 0)
{
if (exponent % 2 != 0)
{
// powResult *= valueBaseTemp;
newPowResult.Clear();
for (int i = 0; i < valueBaseTemp.Count; ++i)
{
ulong carry = 0;
for (var j = 0; j < powResult.Count || carry != 0; ++j)
{
if (i + j >= newPowResult.Count)
{
newPowResult.Add(0);
}
ulong product = 0;
if (j < powResult.Count)
{
product = valueBaseTemp[i] * powResult[j];
}
newPowResult[j + i] += product + carry;
carry = newPowResult[j + i] >> 32;
newPowResult[j + i] &= 0xFFFFFFFF;
}
}
powResult.Clear();
powResult.InsertRange(0, newPowResult);
exponent--;
}
// valueBaseTemp *= valueBaseTemp;
newValueBaseTemp.Clear();
for (int i = 0; i < valueBaseTemp.Count; ++i)
{
ulong carry = 0;
for (var j = 0; j < valueBaseTemp.Count || carry != 0; ++j)
{
if (i + j >= newValueBaseTemp.Count)
{
newValueBaseTemp.Add(0);
}
ulong product = 0;
if (j < valueBaseTemp.Count)
{
product = valueBaseTemp[i] * valueBaseTemp[j];
}
newValueBaseTemp[j + i] += product + carry;
carry = newValueBaseTemp[j + i] >> 32;
newValueBaseTemp[j + i] &= 0xFFFFFFFF;
}
}
valueBaseTemp.Clear();
valueBaseTemp.InsertRange(0, newValueBaseTemp);
exponent /= 2;
}
for (var i = powResult.Count - 1; i > 0 && powResult[i] == 0; --i)
{
powResult.RemoveAt(powResult.Count - 1);
}
// Log2
int numberOfBits = 0;
if (!(powResult.Count == 1 && powResult[0] == 0))
{
int left = 0;
int right = 32;
for (int i = 0; i < 5; ++i)
{
if (powResult[powResult.Count - 1] >> (32 - (left + right) / 2) != 0)
{
// Left
right = (left + right) / 2;
}
else
{
// Right
left = (left + right) / 2;
}
}
numberOfBits = (powResult.Count - 1) * 32 + (32 - left);
}
var sum = new List<ulong>((numberOfBits + 31) / 32);
for (int copyBits = 0; copyBits < numberOfBits; copyBits += 32)
{
uint uValue;
if (!ReadUInt(out uValue, (int)numberOfBits - copyBits > 32 ? 32 : (int)numberOfBits - copyBits, true)) return false;
sum.Add(uValue);
}
for (var i = 0; i < value.Length; ++i)
{
ulong remainder = 0;
for (var j = sum.Count - 1; j >= 0; --j)
{
sum[j] += remainder << 32;
remainder = sum[j] % valueBase;
sum[j] /= valueBase;
}
value[i] = (uint)remainder + (uint)min;
}
return true;
}
/// <summary>
/// Reads an array of signed integers in the given range defining a base.
/// </summary>
/// <param name="value">Array of Signed Integers.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadArrayInt32(int[] value, int min, int max, bool nestedRead = false)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (!nestedRead) previousBitIndex = bitIndex;
var uValue = new uint[value.Length];
if (!ReadArrayUInt32(uValue, min, max, true)) return false;
for (var i = 0; i < value.Length; ++i)
{
value[i] = (int)uValue[i] + min;
}
return true;
}
/// <summary>
/// Reads an array of singles in the given range converted to the given base.
/// </summary>
/// <param name="value">Array of Singles.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="valueBase">The base to convert the singles to.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadArraySingle(float[] value, float min, float max, int valueBase, bool nestedRead = false)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (!nestedRead) previousBitIndex = bitIndex;
var uValue = new uint[value.Length];
if (!ReadArrayUInt32(uValue, 0, valueBase - 1, true)) return false;
if (min < 0 && max > 0)
{
for (var i = 0; i < value.Length; ++i)
{
value[i] = uValue[i] == 0 ? 0 : (uValue[i] - 1) / (float)(valueBase - 2) * (max - min) + min;
}
}
else
{
for (var i = 0; i < value.Length; ++i)
{
value[i] = uValue[i] / (float)(valueBase - 1) * (max - min) + min;
}
}
return true;
}
/// <summary>
/// Reads an array of doubles in the given range converted to the given base.
/// </summary>
/// <param name="value">Array of Doubles.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="valueBase">The base to convert the doubles to.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadArrayDouble(double[] value, double min, double max, int valueBase, bool nestedRead = false)
{
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (!nestedRead) previousBitIndex = bitIndex;
var uValue = new uint[value.Length];
if (!ReadArrayUInt32(uValue, 0, valueBase - 1, true)) return false;
if (min < 0 && max > 0)
{
for (var i = 0; i < value.Length; ++i)
{
value[i] = uValue[i] == 0 ? 0 : (uValue[i] - 1) / (double)(valueBase - 2) * (max - min) + min;
}
}
else
{
for (var i = 0; i < value.Length; ++i)
{
value[i] = uValue[i] / (double)(valueBase - 1) * (max - min) + min;
}
}
return true;
}
/// <summary>
/// Reads a custom resolution single.
/// </summary>
/// <param name="value">Single.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="bitResolution">The number of bits written for the ratio.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadCustomResolutionSingle(out float value, float min, float max, int bitResolution, bool nestedRead = false)
{
if (bitResolution < 1 || bitResolution > 31) throw new ArgumentOutOfRangeException("bitResolution must be in the range (0, 32).");
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
uint uValue;
if (!ReadUInt(out uValue, bitResolution, true)) return false;
if (min < 0 && max > 0)
{
value = uValue == 0 ? 0 : (uValue - 1) / (float)((0x1 << bitResolution) - 2) * (max - min) + min;
}
else
{
value = uValue / (float)((0x1 << bitResolution) - 1) * (max - min) + min;
}
return true;
}
/// <summary>
/// Reads a custom resolution double.
/// </summary>
/// <param name="value">Double.</param>
/// <param name="min">The minimum value in the range.</param>
/// <param name="max">The maximum value in the range.</param>
/// <param name="bitResolution">The number of bits written for the ratio.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadCustomResolutionDouble(out double value, double min, double max, int bitResolution, bool nestedRead = false)
{
if (bitResolution < 1 || bitResolution > 31) throw new ArgumentOutOfRangeException("bitResolution must be in the range (0, 32).");
if (max <= min) throw new ArgumentOutOfRangeException("max must be greater than min.");
if (!nestedRead) previousBitIndex = bitIndex;
value = 0;
uint uValue;
if (!ReadUInt(out uValue, bitResolution, true)) return false;
if (min < 0 && max > 0)
{
value = uValue == 0 ? 0 : (uValue - 1) / (double)((0x1 << bitResolution) - 2) * (max - min) + min;
}
else
{
value = uValue / (double)((0x1 << bitResolution) - 1) * (max - min) + min;
}
return true;
}
/// <summary>
/// Reads a string.
/// </summary>
/// <param name="value">String.</param>
/// <param name="bits">The number of bits for the length written using an unsigned variable-width integer.</param>
/// <param name="limit">The maximum number of characters to read.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadString(out string value, int bits = 4, int limit = 255, bool nestedRead = false)
{
if (bits < 1 || bits > 31) throw new ArgumentOutOfRangeException("bits must be in the range (0, 32).");
if (!nestedRead) previousBitIndex = bitIndex;
value = string.Empty;
uint size;
if (!ReadVariableWidthUInt(out size, bits, true)) return false;
for (var asciiByteItr = 0; asciiByteItr < size; ++asciiByteItr)
{
byte asciiByte;
if (!ReadByte(out asciiByte, true)) return false;
value += asciiByte;
if (value.Length > limit) return false;
}
return true;
}
/// <summary>
/// Reads a binary packet that has been written to the buffer.
/// </summary>
/// <param name="value">A binary packet.</param>
/// <param name="nestedRead">If false then the previous bit index is stored so the operation can be rolled back.</param>
/// <returns>false on error.</returns>
public bool ReadBinaryPacket(out Packet value, bool nestedRead = false)
{
value = new Packet();
if (!nestedRead) previousBitIndex = bitIndex;
uint valueMaxBitIndex;
if (!ReadVariableWidthUInt(out valueMaxBitIndex, nestedRead: true)) return false;
for (int copyBits = 0; copyBits < valueMaxBitIndex; copyBits += 32)
{
uint uValue;
if (!ReadUInt(out uValue, (int)valueMaxBitIndex - copyBits > 32 ? 32 : (int)valueMaxBitIndex - copyBits, true)) return false;
value.WriteUInt(uValue, (int)valueMaxBitIndex - copyBits > 32 ? 32 : (int)valueMaxBitIndex - copyBits);
}
bitIndex += (int)valueMaxBitIndex;
value.bitIndex = 0;
return true;
}
}
}</pre>