Skip to main content
Code Review

Return to Question

replaced http://codereview.stackexchange.com/ with https://codereview.stackexchange.com/
Source Link

Part 1 here. here.

Part 1 here.

Part 1 here.

Tweeted twitter.com/StackCodeReview/status/775262174776332288
Bumped by Community user
Bumped by Community user
Source Link
Tim
  • 115
  • 1
  • 6

Packing and unpacking bits (2)

Part 1 here.

I improved my code from my previous question (linked above).

using System;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// Used to store data as bits. Acts as a queue - first data added is the first data removed.
/// Data should be read in the same order it is written. If read in a different order, it gives undefined results.
/// Reading from an empty BitStream returns 0.
/// </summary>
public class BitStream
{
 ulong scratchWrite = 0;
 int scratchWriteBits = 0;
 ulong scratchRead = 0;
 int scratchReadBits = 0;
 Queue<ulong> buffer = new Queue<ulong>();
 /// <summary>
 /// How many bits are currently in the BitStream
 /// </summary>
 public long StoredBits
 {
 get;
 private set;
 }
 /// <summary>
 /// Make a new BitStream
 /// </summary>
 public BitStream() { }
 /// <summary>
 /// Make a new BitStream
 /// </summary>
 /// <param name="bitCount">How many bits you expect this stream will hold. A closer value nets increased performance.</param>
 public BitStream(long bitCount)
 {
 buffer = new Queue<ulong>((int) IntDivideRoundUp(bitCount, 64));
 }
 /// <summary>
 /// Make a new BitStream containing bits from the byte array
 /// NOTE: StoredBits may return a higher count than there are actual bits to read if the byte array came from another BitStream.
 /// </summary>
 /// <param name="bits">contains bits to be stored in the bitstream</param>
 public BitStream(byte[] bits)
 {
 foreach (var bite in bits)
 {
 Write(bite, byte.MinValue, byte.MaxValue);
 }
 }
 /// <summary>
 /// Get the bits stored in a ulong array (left-endian)
 /// </summary>
 /// <returns>ulong array of bits</returns>
 public ulong[] GetUlongArray()
 {
 ResetBuffer();
 if (scratchWriteBits > 0)
 {
 ulong[] result = new ulong[buffer.Count + 1];
 Array.Copy(buffer.ToArray(), result, buffer.Count);
 result[buffer.Count] = scratchWrite;
 return result;
 }
 return buffer.ToArray();
 }
 /// <summary>
 /// Get the bits stored in a byte array (left-endian)
 /// </summary>
 /// <returns>byte array of bits</returns>
 public byte[] GetByteArray()
 {
 ResetBuffer();
 int extraBytes = (int) IntDivideRoundUp(scratchWriteBits, 8);
 byte[] result = new byte[buffer.Count * 8 + extraBytes];
 Buffer.BlockCopy(buffer.ToArray(), 0, result, 0, result.Length - extraBytes);
 int index = buffer.Count * 8;
 int bits = scratchWriteBits;
 ulong scratch = scratchWrite;
 while (bits > 0)
 {
 int bitsToStore = bits >= 8 ? 8 : bits;
 result[index] = (byte) (scratch >> (64 - bitsToStore));
 scratch <<= bitsToStore;
 bits -= bitsToStore;
 index++;
 }
 return result;
 }
 /// <summary>
 /// Get the bits stored in a BitArray
 /// </summary>
 /// <returns>all bits in the stream in a BitArray</returns>
 public BitArray GetBitArray()
 {
 ResetBuffer();
 BitArray ba = new BitArray(buffer.Count * 64 + scratchWriteBits);
 var tempBuf = buffer.ToArray();
 int counter = 0;
 for (int i = 0; i < ba.Count; i++)
 {
 for (int j = 0; j < 64; j++)
 {
 ba[counter] = (tempBuf[i] & ((ulong) 1 << (63 - j))) > 0;
 counter++;
 }
 }
 for (int i = 0; i < scratchWriteBits; i++)
 {
 ba[counter] = (scratchWrite & ((ulong) 1 << (63 - i))) > 0;
 counter++;
 }
 return ba;
 }
 #region Write
 /// <summary>
 /// Write bits to the stream
 /// </summary>
 /// <param name="data">bits to be written</param>
 /// <param name="bits">how many bits. Range(0, 64]</param>
 protected void Write(ulong data, int bits)
 {
 if (bits <= 0 || bits > 64)
 {
 return;
 }
 scratchWrite |= ((data << (64 - bits)) >> scratchWriteBits);
 scratchWriteBits += bits;
 if (scratchWriteBits >= 64)
 {
 var temp = scratchWrite;
 buffer.Enqueue(scratchWrite);
 scratchWrite = 0;
 scratchWriteBits -= 64;
 if (scratchWriteBits > 0)
 {
 scratchWrite |= (data << (64 - scratchWriteBits));
 }
 }
 StoredBits += bits;
 }
 /// <summary>
 /// Write bits to the stream
 /// </summary>
 /// <param name="data">bits to be written</param>
 /// <param name="min">the minimum number that can be written</param>
 /// <param name="max">the maximum number that can be written</param>
 public void Write(long data, long min, long max)
 {
 if (min > max)
 {
 min ^= max;
 max ^= min;
 min ^= max;
 }
 if (data < min || data > max)
 {
 throw new ArgumentOutOfRangeException("data", data, "must be between min and max");
 }
 ulong bdata = 0;
 ulong bmin = 0;
 ulong bmax = 0;
 if (max < 0)
 {
 max -= min;
 bmax = (ulong) max;
 }
 else
 {
 bmax = (ulong) max;
 if (min == long.MinValue)
 {
 bmax += (ulong) long.MaxValue + 1;
 }
 else if (min < 0)
 {
 bmin = (ulong) -min;
 bmax += bmin;
 }
 else
 {
 bmin = (ulong) min;
 bmax -= bmin;
 }
 }
 data -= min;
 bdata = (ulong) data;
 Write(bdata, BitsRequired(bmax));
 }
 /// <summary>
 /// Write bits to the stream
 /// </summary>
 /// <param name="data">bits to be written</param>
 /// <param name="min">the minimum number that can be written</param>
 /// <param name="max">the maximum number that can be written</param>
 public void Write(ulong data, ulong min, ulong max)
 {
 if (min > max)
 {
 min ^= max;
 max ^= min;
 min ^= max;
 }
 if (data < min || data > max)
 {
 throw new ArgumentOutOfRangeException("data", data, "must be between min and max");
 }
 if (min > 0)
 {
 max -= min;
 }
 Write(data, BitsRequired(max));
 }
 /// <summary>
 /// Write 1 bit to the stream
 /// </summary>
 /// <param name="data">bit to be written</param>
 public void Write(bool data)
 {
 Write(Convert.ToByte(data), 1);
 }
 /// <summary>
 /// Write bits to the stream
 /// </summary>
 /// <param name="data">bits to be written</param>
 /// <param name="min">the minimum number that can be written</param>
 /// <param name="max">the maximum number that can be written</param>
 public void Write(byte data, byte min, byte max)
 {
 Write((ulong) data, min, max);
 }
 /// <summary>
 /// Write bits to the stream
 /// </summary>
 /// <param name="data">bits to be written</param>
 /// <param name="min">the minimum number that can be written</param>
 /// <param name="max">the maximum number that can be written</param>
 public void Write(sbyte data, sbyte min, sbyte max)
 {
 Write((long) data, min, max);
 }
 /// <summary>
 /// Write bits to the stream
 /// </summary>
 /// <param name="data">bits to be written</param>
 /// <param name="min">the minimum number that can be written</param>
 /// <param name="max">the maximum number that can be written</param>
 public void Write(char data, char min, char max)
 {
 Write((ulong) data, min, max);
 }
 /// <summary>
 /// Write bits to the stream
 /// </summary>
 /// <param name="data">bits to be written</param>
 /// <param name="min">the minimum number that can be written</param>
 /// <param name="max">the maximum number that can be written</param>
 public void Write(short data, short min, short max)
 {
 Write((long) data, min, max);
 }
 /// <summary>
 /// Write bits to the stream
 /// </summary>
 /// <param name="data">bits to be written</param>
 /// <param name="min">the minimum number that can be written</param>
 /// <param name="max">the maximum number that can be written</param>
 public void Write(ushort data, ushort min, ushort max)
 {
 Write((ulong) data, min, max);
 }
 /// <summary>
 /// Write bits to the stream
 /// </summary>
 /// <param name="data">bits to be written</param>
 /// <param name="min">the minimum number that can be written</param>
 /// <param name="max">the maximum number that can be written</param>
 public void Write(int data, int min, int max)
 {
 Write((long) data, min, max);
 }
 /// <summary>
 /// Write bits to the stream
 /// </summary>
 /// <param name="data">bits to be written</param>
 /// <param name="min">the minimum number that can be written</param>
 /// <param name="max">the maximum number that can be written</param>
 public void Write(uint data, uint min, uint max)
 {
 Write((ulong) data, min, max);
 }
 /// <summary>
 /// Write bits to the stream
 /// </summary>
 /// <param name="data">bits to be written</param>
 /// <param name="min">the minimum number that can be written</param>
 /// <param name="max">the maximum number that can be written</param>
 /// <param name="precision">how many digits after the decimal</param>
 public void Write(float data, float min, float max, byte precision)
 {
 if (min > max)
 {
 var temp = min;
 min = max;
 max = temp;
 }
 if (data < min || data > max)
 {
 throw new ArgumentOutOfRangeException("data", data, "must be between min and max");
 }
 float newMax = max - min;
 int mult = IntPow(10, precision);
 if (float.IsInfinity(newMax))
 {
 if (precision > 0)
 {
 Write((ulong) BitConverter.DoubleToInt64Bits(data), 32);
 return;
 }
 double infoMax = Math.Round(max, MidpointRounding.AwayFromZero);
 double infoMin = Math.Round(min, MidpointRounding.AwayFromZero);
 if (infoMax > uint.MaxValue || -infoMin > uint.MaxValue)
 {
 Write((ulong) BitConverter.DoubleToInt64Bits(data), 32);
 return;
 }
 long info = (long) Math.Round(data * mult, MidpointRounding.AwayFromZero);
 Write(info, (long) infoMin, (long) infoMax);
 }
 else
 {
 double infoMax = Math.Round(newMax * mult, MidpointRounding.AwayFromZero);
 if (infoMax > uint.MaxValue)
 {
 Write((ulong) BitConverter.DoubleToInt64Bits(data), 32);
 return;
 }
 data -= min;
 ulong info = (ulong) Math.Round(data * mult, MidpointRounding.AwayFromZero);
 Write(info, BitsRequired((ulong) infoMax));
 }
 }
 /// <summary>
 /// Write bits to the stream
 /// </summary>
 /// <param name="data">bits to be written</param>
 /// <param name="min">the minimum number that can be written</param>
 /// <param name="max">the maximum number that can be written</param>
 /// <param name="precision">how many digits after the decimal</param>
 public void Write(double data, double min, double max, byte precision)
 {
 if (min > max)
 {
 var temp = min;
 min = max;
 max = temp;
 }
 if (data < min || data > max)
 {
 throw new ArgumentOutOfRangeException("data", data, "must be between min and max");
 }
 double newMax = max - min;
 int mult = IntPow(10, precision);
 if (double.IsInfinity(newMax))
 {
 if (precision > 0)
 {
 Write((ulong) BitConverter.DoubleToInt64Bits(data), 64);
 return;
 }
 double infoMax = Math.Round(max, MidpointRounding.AwayFromZero);
 double infoMin = Math.Round(min, MidpointRounding.AwayFromZero);
 if (infoMax > ulong.MaxValue || -infoMin > ulong.MaxValue)
 {
 Write((ulong) BitConverter.DoubleToInt64Bits(data), 64);
 return;
 }
 long info = (long) Math.Round(data * mult, MidpointRounding.AwayFromZero);
 Write(info, (long) infoMin, (long) infoMax);
 }
 else
 {
 double infoMax = Math.Round(newMax * mult, MidpointRounding.AwayFromZero);
 if (infoMax > ulong.MaxValue)
 {
 Write((ulong) BitConverter.DoubleToInt64Bits(data), 64);
 return;
 }
 data -= min;
 ulong info = (ulong) Math.Round(data * mult, MidpointRounding.AwayFromZero);
 Write(info, BitsRequired((ulong) infoMax));
 }
 }
 #endregion
 #region Read
 /// <summary>
 /// Read bits from the stream. Returns 0 if bits > StoredBits.
 /// </summary>
 /// <param name="bits">How many bits to read. Range(0, 64]</param>
 /// <returns>bits read in ulong form</returns>
 protected ulong Read(int bits)
 {
 if (bits <= 0 || bits > 64)
 {
 return 0;
 }
 StoredBits -= bits;
 ulong data = scratchRead >> (64 - bits);
 if (scratchReadBits < bits)
 {
 ulong tempScratch;
 int difference = bits - scratchReadBits;
 if (buffer.Count == 0)
 {
 scratchRead |= (scratchWrite >> scratchReadBits);
 data = scratchRead >> (64 - bits);
 scratchWrite <<= difference;
 scratchWriteBits -= difference;
 scratchRead = 0;
 scratchReadBits = 0;
 }
 else
 {
 tempScratch = buffer.Dequeue();
 scratchRead |= (tempScratch >> scratchReadBits);
 data = scratchRead >> (64 - bits);
 tempScratch <<= difference;
 scratchRead = tempScratch;
 scratchReadBits = 64 - difference;
 }
 }
 else
 {
 scratchRead <<= bits;
 scratchReadBits -= bits;
 }
 if (StoredBits < 0) // handle the case of asking for more bits than exist in the stream
 {
 data = 0;
 StoredBits = 0;
 scratchWrite = 0;
 scratchWriteBits = 0;
 scratchRead = 0;
 scratchReadBits = 0;
 }
 return data;
 }
 /// <summary>
 /// Read a bit from the stream and write it to data
 /// </summary>
 /// <param name="data">the variable to be written to</param>
 public void Read(out bool data)
 {
 data = Read(1) > 0;
 }
 /// <summary>
 /// Read bits from the stream and write that information to data.
 /// WARNING: If you read data in a different order than written, there is a possibility that the actual number written to data is outside of the given range. In such a case, you may want to check the bounds yourself.
 /// </summary>
 /// <param name="data">the variable to be written to</param>
 /// <param name="min">the smallest possible number that could have been written</param>
 /// <param name="max">the largest possible number that could have been written</param>
 public void Read(out ulong data, ulong min, ulong max)
 {
 if (min > max)
 {
 min ^= max;
 max ^= min;
 min ^= max;
 }
 if (min > 0)
 {
 max -= min;
 }
 data = Read(BitsRequired(max)) - min;
 }
 /// <summary>
 /// Read bits from the stream and write that information to data.
 /// WARNING: If you read data in a different order than written, there is a possibility that the actual number written to data is outside of the given range. In such a case, you may want to check the bounds yourself.
 /// </summary>
 /// <param name="data">the variable to be written to</param>
 /// <param name="min">the smallest possible number that could have been written</param>
 /// <param name="max">the largest possible number that could have been written</param>
 public void Read(out uint data, uint min, uint max)
 {
 ulong tempData;
 Read(out tempData, min, max);
 data = (uint) tempData;
 }
 /// <summary>
 /// Read bits from the stream and write that information to data.
 /// WARNING: If you read data in a different order than written, there is a possibility that the actual number written to data is outside of the given range. In such a case, you may want to check the bounds yourself.
 /// </summary>
 /// <param name="data">the variable to be written to</param>
 /// <param name="min">the smallest possible number that could have been written</param>
 /// <param name="max">the largest possible number that could have been written</param>
 public void Read(out ushort data, ushort min, ushort max)
 {
 ulong tempData;
 Read(out tempData, min, max);
 data = (ushort) tempData;
 }
 /// <summary>
 /// Read bits from the stream and write that information to data.
 /// WARNING: If you read data in a different order than written, there is a possibility that the actual number written to data is outside of the given range. In such a case, you may want to check the bounds yourself.
 /// </summary>
 /// <param name="data">the variable to be written to</param>
 /// <param name="min">the smallest possible number that could have been written</param>
 /// <param name="max">the largest possible number that could have been written</param>
 public void Read(out byte data, byte min, byte max)
 {
 ulong tempData;
 Read(out tempData, min, max);
 data = (byte) tempData;
 }
 /// <summary>
 /// Read bits from the stream and write that information to data.
 /// WARNING: If you read data in a different order than written, there is a possibility that the actual number written to data is outside of the given range. In such a case, you may want to check the bounds yourself.
 /// </summary>
 /// <param name="data">the variable to be written to</param>
 /// <param name="min">the smallest possible number that could have been written</param>
 /// <param name="max">the largest possible number that could have been written</param>
 public void Read(out char data, char min, char max)
 {
 ulong tempData;
 Read(out tempData, min, max);
 data = (char) tempData;
 }
 /// <summary>
 /// Read bits from the stream and write that information to data.
 /// WARNING: If you read data in a different order than written, there is a possibility that the actual number written to data is outside of the given range. In such a case, you may want to check the bounds yourself.
 /// </summary>
 /// <param name="data">the variable to be written to</param>
 /// <param name="min">the smallest possible number that could have been written</param>
 /// <param name="max">the largest possible number that could have been written</param>
 public void Read(out long data, long min, long max)
 {
 if (min > max)
 {
 min ^= max;
 max ^= min;
 min ^= max;
 }
 ulong bmin = 0;
 ulong bmax = 0;
 if (max < 0)
 {
 max -= min;
 bmax = (ulong) max;
 }
 else
 {
 bmax = (ulong) max;
 if (min == long.MinValue)
 {
 bmax += (ulong) long.MaxValue + 1;
 }
 else if (min < 0)
 {
 bmin = (ulong) -min;
 bmax += bmin;
 }
 else
 {
 bmin = (ulong) min;
 bmax -= bmin;
 }
 }
 if (BitsRequired(bmax) > StoredBits)
 {
 data = 0;
 }
 else
 {
 data = (long) (Read(BitsRequired(bmax)) - bmin);
 }
 }
 /// <summary>
 /// Read bits from the stream and write that information to data.
 /// WARNING: If you read data in a different order than written, there is a possibility that the actual number written to data is outside of the given range. In such a case, you may want to check the bounds yourself.
 /// </summary>
 /// <param name="data">the variable to be written to</param>
 /// <param name="min">the smallest possible number that could have been written</param>
 /// <param name="max">the largest possible number that could have been written</param>
 public void Read(out int data, int min, int max)
 {
 long tempData;
 Read(out tempData, min, max);
 data = (int) tempData;
 }
 /// <summary>
 /// Read bits from the stream and write that information to data.
 /// WARNING: If you read data in a different order than written, there is a possibility that the actual number written to data is outside of the given range. In such a case, you may want to check the bounds yourself.
 /// </summary>
 /// <param name="data">the variable to be written to</param>
 /// <param name="min">the smallest possible number that could have been written</param>
 /// <param name="max">the largest possible number that could have been written</param>
 public void Read(out short data, short min, short max)
 {
 long tempData;
 Read(out tempData, min, max);
 data = (short) tempData;
 }
 /// <summary>
 /// Read bits from the stream and write that information to data.
 /// WARNING: If you read data in a different order than written, there is a possibility that the actual number written to data is outside of the given range. In such a case, you may want to check the bounds yourself.
 /// </summary>
 /// <param name="data">the variable to be written to</param>
 /// <param name="min">the smallest possible number that could have been written</param>
 /// <param name="max">the largest possible number that could have been written</param>
 public void Read(out sbyte data, sbyte min, sbyte max)
 {
 long tempData;
 Read(out tempData, min, max);
 data = (sbyte) tempData;
 }
 /// <summary>
 /// Read bits from the stream and write that information to data.
 /// WARNING: If you read data in a different order than written, there is a possibility that the actual number written to data is outside of the given range. In such a case, you may want to check the bounds yourself.
 /// </summary>
 /// <param name="data">the variable to be written to</param>
 /// <param name="min">the smallest possible number that could have been written</param>
 /// <param name="max">the largest possible number that could have been written</param>
 /// <param name="precision">how many digits after the decimal</param>
 public void Read(out double data, double min, double max, byte precision)
 {
 if (min > max)
 {
 var temp = min;
 min = max;
 max = temp;
 }
 double newMax = max - min;
 int mult = IntPow(10, precision);
 if (double.IsInfinity(newMax))
 {
 if (precision > 0)
 {
 data = BitConverter.Int64BitsToDouble((long) Read(64));
 return;
 }
 double infoMax = Math.Round(max, MidpointRounding.AwayFromZero);
 double infoMin = Math.Round(min, MidpointRounding.AwayFromZero);
 if (infoMax > ulong.MaxValue || -infoMin > ulong.MaxValue)
 {
 data = BitConverter.Int64BitsToDouble((long) Read(64));
 return;
 }
 long tempData;
 Read(out tempData, (long) infoMin, (long) infoMax);
 data = tempData / (double) mult;
 }
 else
 {
 double infoMax = Math.Round(newMax * mult, MidpointRounding.AwayFromZero);
 if (infoMax > ulong.MaxValue)
 {
 data = BitConverter.Int64BitsToDouble((long) Read(64));
 return;
 }
 long tempData = (long) Read(BitsRequired((ulong) infoMax));
 data = tempData / (double) mult;
 }
 }
 /// <summary>
 /// Read bits from the stream and write that information to data.
 /// WARNING: If you read data in a different order than written, there is a possibility that the actual number written to data is outside of the given range. In such a case, you may want to check the bounds yourself.
 /// </summary>
 /// <param name="data">the variable to be written to</param>
 /// <param name="min">the smallest possible number that could have been written</param>
 /// <param name="max">the largest possible number that could have been written</param>
 /// <param name="precision">how many digits after the decimal</param>
 public void Read(out float data, float min, float max, byte precision)
 {
 if (min > max)
 {
 var temp = min;
 min = max;
 max = temp;
 }
 float newMax = max - min;
 int mult = IntPow(10, precision);
 if (float.IsInfinity(newMax))
 {
 if (precision > 0)
 {
 data = (float) BitConverter.Int64BitsToDouble((long) Read(64));
 return;
 }
 float infoMax = (float) Math.Round(max, MidpointRounding.AwayFromZero);
 float infoMin = (float) Math.Round(min, MidpointRounding.AwayFromZero);
 if (infoMax > uint.MaxValue || -infoMin > uint.MaxValue)
 {
 data = (float) BitConverter.Int64BitsToDouble((long) Read(64));
 return;
 }
 int tempData;
 Read(out tempData, (int) infoMin, (int) infoMax);
 data = tempData / (float) mult;
 }
 else
 {
 float infoMax = (float) Math.Round(newMax * mult, MidpointRounding.AwayFromZero);
 if (infoMax > uint.MaxValue)
 {
 data = (float) BitConverter.Int64BitsToDouble((long) Read(64));
 return;
 }
 int tempData = (int) Read(BitsRequired((ulong) infoMax));
 data = tempData / (float) mult;
 }
 }
 #endregion
 /// <summary>
 /// Returns how many bits it takes to store a number
 /// </summary>
 /// <param name="max">the maximum number that will be written</param>
 /// <returns>how many bits are needed</returns>
 protected int BitsRequired(ulong max)
 {
 if (max == 0)
 {
 return 1;
 }
 for (int i = 1; i < 64; i++)
 {
 if (max < ((ulong) 1 << i))
 {
 return i;
 }
 }
 return 64;
 }
 /// <summary>
 /// If scratch_read contains any bits, moves them to the head of the buffer.
 /// NOTE: Not a short operation, use only when necessary!
 /// </summary>
 protected void ResetBuffer()
 {
 if (scratchReadBits > 0 && scratchRead != scratchWrite)
 {
 StoredBits = 0;
 var oldBuf = buffer.ToArray();
 buffer.Clear();
 var tempScratch = scratchWrite >> (64 - scratchWriteBits);
 int tempBits = scratchWriteBits;
 scratchWrite = 0;
 scratchWriteBits = 0;
 Write(scratchRead >> (64 - scratchReadBits), scratchReadBits);
 scratchRead = 0;
 scratchReadBits = 0;
 for (int i = 0; i < oldBuf.Length - 1; i++)
 {
 Write(oldBuf[i], 64);
 }
 Write(tempScratch, tempBits);
 }
 }
 long IntDivideRoundUp(long upper, long lower)
 {
 return (upper + lower - 1) / lower;
 }
 int IntPow(int x, uint pow)
 {
 int ret = 1;
 while (pow != 0)
 {
 if ((pow & 1) == 1)
 {
 ret *= x;
 }
 x *= x;
 pow >>= 1;
 }
 return ret;
 }
}

Example use:

BitStream bs = new BitStream();
int min1 = -20, max1 = 1000, num1 = 287;
float min2 = 0f, max2 = 50f, num2 = 16.78634f;
double min3 = double.MinValue, max3 = double.MaxValue, num3 = 9845216.1916526;
byte fltPrec = 2, dblPrec = 0;
 
int num4;
float num5;
double num6;
bool checker;
bs.Write(num1, min1, max1); // 10 bits (difference between max/min is 1020)
bs.Write(num2, min2, max2, fltPrec); // converts to 1679 int, 13 bits (difference between max/min converted to int is 5000)
bs.Read(out num4, min1, max1); // num4 = 287
bs.Write(num3, min3, max3, dblPrec); // precision is ignored here as min/max are too high to try to convert to an integer, so the value is stored using all 64 bits of the double
bs.Write(true); // 1 bit
bs.Read(out num5, min2, max2, fltPrec); // num5 = 16.79, there is some loss of precision here
bs.Read(out num6, min3, max3, dblPrec); // num6 = 9845216.1916526, no loss of precision
bs.Read(out checker); // checker = true
int newNum;
bs.Read(out newNum, -100, 100); // newNum = 0 as there are no bits left in the BitStream

And I also made a simple serializer class utilizing the BitStream class:

using System;
/// <summary>
/// Used to serialize and deserialize data using the BitStream class.
/// </summary>
public class BitSerializer
{
 BitStream bs = new BitStream();
 bool isReading;
 public bool IsReading
 {
 get { return isReading; }
 private set { isReading = value; }
 }
 public bool IsWriting
 {
 get { return !isReading; }
 private set { isReading = !value; }
 }
 /// <summary>
 /// Create a new BitSerializer
 /// </summary>
 /// <param name="read">Writing or reading. Set to true to read, false to write.</param>
 public BitSerializer(bool read = false)
 {
 isReading = read;
 }
 /// <summary>
 /// Create a new BitSerializer
 /// </summary>
 /// <param name="bits">How many bits you expect this serializer will hold. A closer value nets increased performance.</param>
 /// <param name="read">Writing or reading. Set to true to read, false to write.</param>
 public BitSerializer(long bits, bool read = false)
 : this(read)
 {
 bs = new BitStream(bits);
 }
 /// <summary>
 /// Create a new BitSerializer
 /// </summary>
 /// <param name="bitStream">The BitStream to read from or write to</param>
 /// <param name="read">Writing or reading. Set to true to read, false to write.</param>
 public BitSerializer(BitStream bitStream, bool read = true)
 : this(read)
 {
 bs = bitStream;
 }
 /// <summary>
 /// Create a new BitSerializer
 /// </summary>
 /// <param name="data">The data to be read from</param>
 /// <param name="read">Writing or reading. Set to true to read, false to write.</param>
 public BitSerializer(byte[] data, bool read = true)
 : this(read)
 {
 bs = new BitStream(data);
 }
 /// <summary>
 /// Get the bits stored in a byte array (left-endian)
 /// </summary>
 /// <returns>byte array of bits</returns>
 public byte[] GetByteArray()
 {
 return bs.GetByteArray();
 }
 /// <summary>
 /// Serialize or Deserialize the data
 /// </summary>
 /// <param name="data">Data to be serialized or deserialized</param>
 /// <param name="min">The smallest possible number to be serialized</param>
 /// <param name="max">The largest possible number to be serialized</param>
 public void Serialize(ref ulong data, ulong min, ulong max)
 {
 if (IsReading)
 {
 bs.Read(out data, min, max);
 if (data < min || data > max)
 {
 throw new Exception("An error occurred: data is outside of bounds!");
 }
 }
 else
 {
 bs.Write(data, min, max);
 }
 }
 /// <summary>
 /// Serialize or Deserialize the data
 /// </summary>
 /// <param name="data">Data to be serialized or deserialized</param>
 /// <param name="min">The smallest possible number to be serialized</param>
 /// <param name="max">The largest possible number to be serialized</param>
 public void Serialize(ref uint data, uint min, uint max)
 {
 if (IsReading)
 {
 bs.Read(out data, min, max);
 if (data < min || data > max)
 {
 throw new Exception("An error occurred: data is outside of bounds!");
 }
 }
 else
 {
 bs.Write(data, min, max);
 }
 }
 /// <summary>
 /// Serialize or Deserialize the data
 /// </summary>
 /// <param name="data">Data to be serialized or deserialized</param>
 /// <param name="min">The smallest possible number to be serialized</param>
 /// <param name="max">The largest possible number to be serialized</param>
 public void Serialize(ref ushort data, ushort min, ushort max)
 {
 if (IsReading)
 {
 bs.Read(out data, min, max);
 if (data < min || data > max)
 {
 throw new Exception("An error occurred: data is outside of bounds!");
 }
 }
 else
 {
 bs.Write(data, min, max);
 }
 }
 /// <summary>
 /// Serialize or Deserialize the data
 /// </summary>
 /// <param name="data">Data to be serialized or deserialized</param>
 /// <param name="min">The smallest possible number to be serialized</param>
 /// <param name="max">The largest possible number to be serialized</param>
 public void Serialize(ref byte data, byte min, byte max)
 {
 if (IsReading)
 {
 bs.Read(out data, min, max);
 if (data < min || data > max)
 {
 throw new Exception("An error occurred: data is outside of bounds!");
 }
 }
 else
 {
 bs.Write(data, min, max);
 }
 }
 /// <summary>
 /// Serialize or Deserialize the data
 /// </summary>
 /// <param name="data">Data to be serialized or deserialized</param>
 /// <param name="min">The smallest possible number to be serialized</param>
 /// <param name="max">The largest possible number to be serialized</param>
 public void Serialize(ref char data, char min, char max)
 {
 if (IsReading)
 {
 bs.Read(out data, min, max);
 if (data < min || data > max)
 {
 throw new Exception("An error occurred: data is outside of bounds!");
 }
 }
 else
 {
 bs.Write(data, min, max);
 }
 }
 /// <summary>
 /// Serialize or Deserialize the data
 /// </summary>
 /// <param name="data">Data to be serialized or deserialized</param>
 /// <param name="min">The smallest possible number to be serialized</param>
 /// <param name="max">The largest possible number to be serialized</param>
 public void Serialize(ref long data, long min, long max)
 {
 if (IsReading)
 {
 bs.Read(out data, min, max);
 if (data < min || data > max)
 {
 throw new Exception("An error occurred: data is outside of bounds!");
 }
 }
 else
 {
 bs.Write(data, min, max);
 }
 }
 /// <summary>
 /// Serialize or Deserialize the data
 /// </summary>
 /// <param name="data">Data to be serialized or deserialized</param>
 /// <param name="min">The smallest possible number to be serialized</param>
 /// <param name="max">The largest possible number to be serialized</param>
 public void Serialize(ref int data, int min, int max)
 {
 if (IsReading)
 {
 bs.Read(out data, min, max);
 if (data < min || data > max)
 {
 throw new Exception("An error occurred: data is outside of bounds!");
 }
 }
 else
 {
 bs.Write(data, min, max);
 }
 }
 /// <summary>
 /// Serialize or Deserialize the data
 /// </summary>
 /// <param name="data">Data to be serialized or deserialized</param>
 /// <param name="min">The smallest possible number to be serialized</param>
 /// <param name="max">The largest possible number to be serialized</param>
 public void Serialize(ref short data, short min, short max)
 {
 if (IsReading)
 {
 bs.Read(out data, min, max);
 if (data < min || data > max)
 {
 throw new Exception("An error occurred: data is outside of bounds!");
 }
 }
 else
 {
 bs.Write(data, min, max);
 }
 }
 /// <summary>
 /// Serialize or Deserialize the data
 /// </summary>
 /// <param name="data">Data to be serialized or deserialized</param>
 /// <param name="min">The smallest possible number to be serialized</param>
 /// <param name="max">The largest possible number to be serialized</param>
 public void Serialize(ref sbyte data, sbyte min, sbyte max)
 {
 if (IsReading)
 {
 bs.Read(out data, min, max);
 if (data < min || data > max)
 {
 throw new Exception("An error occurred: data is outside of bounds!");
 }
 }
 else
 {
 bs.Write(data, min, max);
 }
 }
 /// <summary>
 /// Serialize or Deserialize the data
 /// </summary>
 /// <param name="data">Data to be serialized or deserialized</param>
 public void Serialize(ref bool data)
 {
 if (IsReading)
 {
 bs.Read(out data);
 }
 else
 {
 bs.Write(data);
 }
 }
 /// <summary>
 /// Serialize or Deserialize the data
 /// </summary>
 /// <param name="data">Data to be serialized or deserialized</param>
 /// <param name="min">The smallest possible number to be serialized</param>
 /// <param name="max">The largest possible number to be serialized</param>
 /// <param name="precision">How many digits after the decimal</param>
 public void Serialize(ref float data, float min, float max, byte precision)
 {
 if (IsReading)
 {
 bs.Read(out data, min, max, precision);
 if (data < min || data > max)
 {
 throw new Exception("An error occurred: data is outside of bounds!");
 }
 }
 else
 {
 bs.Write(data, min, max, precision);
 }
 }
 /// <summary>
 /// Serialize or Deserialize the data
 /// </summary>
 /// <param name="data">Data to be serialized or deserialized</param>
 /// <param name="min">The smallest possible number to be serialized</param>
 /// <param name="max">The largest possible number to be serialized</param>
 /// <param name="precision">How many digits after the decimal</param>
 public void Serialize(ref double data, double min, double max, byte precision)
 {
 if (IsReading)
 {
 bs.Read(out data, min, max, precision);
 if (data < min || data > max)
 {
 throw new Exception("An error occurred: data is outside of bounds!");
 }
 }
 else
 {
 bs.Write(data, min, max, precision);
 }
 }
}

Any comments welcome.

lang-cs

AltStyle によって変換されたページ (->オリジナル) /