using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace QiHe.CodeLib.Compress
{
public class BitStream
{
BufferedStream stream;
public BitStream(Stream stream)
{
//this.stream = stream;
this.stream = new BufferedStream(stream);
}
public long Length
{
get { return stream.Length; }
}
public long Position
{
get { return stream.Position; }
set { stream.Position = value; }
}
public bool IsEndReached
{
get { return Position == Length; }
}
BitArray bitBuffer;
public int bitsRead = 0;
int bitsRemained = 0;
/// <summary>
/// Read num bits from stream, return as a machine integer stored with the most-significant bit first
/// return -1 if not enough bits remained.
/// </summary>
/// <param name="num"></param>
/// <returns></returns>
public int ReadBitsBigEndian(int num)
{
if (num < 1 || num > 16)
{
throw new ArgumentOutOfRangeException("number of bits");
}
if (num <= bitsRemained)
{
int result = GetBinaryInteger(bitBuffer, bitsRead, num);
bitsRead += num;
bitsRemained -= num;
return result;
}
else
{
int count = num - bitsRemained;
int bytesToRead = count <= 8 ? 1 : 2;
byte[] data = ReadBytes(bytesToRead);
if (data.Length == bytesToRead)
{
int result = GetBinaryInteger(bitBuffer, bitsRead, bitsRemained);
bitBuffer = new BitArray(data);
result = GetBinaryInteger(result, bitBuffer, 0, count);
bitsRead = count;
bitsRemained = bytesToRead * 8 - count;
return result;
}
else
{
return -1;
}
}
}
private static int GetBinaryInteger(BitArray array, int start, int count)
{
return GetBinaryInteger(0, array, start, count);
}
private static int GetBinaryInteger(int initial, BitArray array, int start, int count)
{
int result = initial;
for (int n = start; n < start + count; n++)
{
int bit = array[n] ? 1 : 0;
result = result * 2 + bit;
}
return result;
}
/// <summary>
/// Read Bits Little Endian
/// </summary>
/// <param name="num"></param>
/// <returns></returns>
public int ReadBits(int num)
{
int result = ReadBitsBigEndian(num);
BitArray bits = new BitArray(BitConverter.GetBytes(result));
return GetBinaryInteger(0, bits, 0, num);
}
/// <summary>
/// Read one bit.
/// </summary>
/// <returns></returns>
public int ReadBit()
{
return ReadBitsBigEndian(1);
}
/// <summary>
/// go to the next byte boundary, former unread bits are ignored.
/// </summary>
public void GotoNextByte()
{
bitsRead = 0;
bitsRemained = 0;
}
public byte ReadByte()
{
return (byte)stream.ReadByte();
}
public int ReadByte(long offset)
{
stream.Position = offset;
return stream.ReadByte();
}
public byte[] ReadBytes(int count)
{
byte[] data = new byte[count];
int bytesRead = stream.Read(data, 0, count);
if (bytesRead == count)
{
return data;
}
else
{
byte[] bytes = new byte[bytesRead];
if (bytesRead > 0)
{
Array.Copy(data, bytes, bytesRead);
}
return bytes;
}
}
public byte[] ReadToEnd()
{
int bytesRemained = (int)(Length - Position);
return ReadBytes(bytesRemained);
}
public byte[] PeekBytes(int count)
{
return PeekBytes(this.Position, count);
}
public byte[] PeekBytes(long offset, int length)
{
long pos = this.Position;
this.Position = offset;
byte[] data = ReadBytes(length);
this.Position = pos;
return data;
}
public ushort ReadUInt16()
{
byte[] data = ReadBytes(2);
return BitConverter.ToUInt16(data, 0);
}
uint writebuffer = 0;
public int bitsWritten = 0;
public void WriteBits(int value, int num)
{
if (num == 0) return;
BitArray bits = new BitArray(BitConverter.GetBytes(value));
for (int i = 0; i < num; i++)
{
uint bit = bits[i] ? 0x80000000 : 0;
writebuffer = (writebuffer >> 1) | bit;
bitsWritten++;
if (bitsWritten == 32)
{
WriteBytes(BitConverter.GetBytes(writebuffer));
ClearWriteBuffer();
}
}
}
public void WriteBitsBigEndian(int value, int num)
{
BitArray bits = new BitArray(BitConverter.GetBytes(value));
int result = GetBinaryInteger(0, bits, 0, num);
WriteBits(result, num);
}
public void WriteBit(bool bit)
{
WriteBit(bit ? 1 : 0);
}
public void WriteBit(int bit)
{
WriteBits(bit, 1);
}
private void ClearWriteBuffer()
{
writebuffer = 0;
bitsWritten = 0;
}
/// <summary>
/// Flush bits in buffer, zero bits are apended to form a byte border.
/// Or flush read when reading.
/// </summary>
public void Flush()
{
if (bitsWritten > 0)
{
writebuffer = writebuffer >> (32 - bitsWritten);
byte[] bytes = BitConverter.GetBytes(writebuffer);
int count = bitsWritten / 8;
if (bitsWritten % 8 != 0)
{
count++;
}
stream.Write(bytes, 0, count);
ClearWriteBuffer();
}
stream.Flush();
}
public void WriteByte(byte value)
{
stream.WriteByte(value);
}
public void WriteBytes(byte[] data)
{
stream.Write(data, 0, data.Length);
}
public void WriteBytes(byte[] data, int offset, int count)
{
stream.Write(data, offset, count);
}
}
}