Files
CypherCore/Source/Game/DataStorage/ClientReader/BitStream.cs
T
2018-02-26 13:13:54 -05:00

419 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Framework.IO;
namespace Game.DataStorage
{
public class BitStream : IDisposable
{
private byte currentByte;
private long offset;
private int bit;
private Stream stream;
private Encoding encoding = Encoding.UTF8;
private bool canWrite = true;
public long Length => stream.Length;
public long BitPosition => bit;
public long Offset => offset;
private bool ValidPosition => offset < Length;
public BitStream(int capacity = 0)
{
this.stream = new MemoryStream(capacity);
offset = bit = 0;
canWrite = true;
currentByte = 0;
}
public BitStream(byte[] buffer)
{
this.stream = new MemoryStream(buffer);
offset = bit = 0;
canWrite = false;
currentByte = buffer[0];
}
#region Methods
public void Seek(long offset, int bit)
{
if (offset > Length)
{
this.offset = Length;
}
else
{
if (offset >= 0)
{
this.offset = offset;
}
else
{
offset = 0;
}
}
if (bit >= 8)
{
this.offset += bit / 8;
this.bit = bit % 8;
}
else
{
this.bit = bit;
}
UpdateCurrentByte();
}
public bool AdvanceBit()
{
bit = (bit + 1) % 8;
if (bit == 0)
{
offset++;
if (canWrite)
stream.WriteByte(currentByte);
UpdateCurrentByte();
return true;
}
return false;
}
public byte[] GetStreamData()
{
stream.Position = 0;
MemoryStream s = new MemoryStream();
stream.CopyTo(s);
Seek(offset, bit);
return s.ToArray();
}
public bool ChangeLength(long length)
{
if (stream.CanSeek && stream.CanWrite)
{
stream.SetLength(length);
return true;
}
else
{
return false;
}
}
public void CopyStreamTo(Stream stream)
{
Seek(0, 0);
this.stream.CopyTo(stream);
}
public MemoryStream CloneAsMemoryStream() => new MemoryStream(GetStreamData());
#endregion
#region Bit Read
private void UpdateCurrentByte()
{
stream.Position = offset;
if (canWrite)
{
currentByte = 0;
}
else
{
currentByte = (byte)stream.ReadByte();
stream.Position = offset;
}
}
private Bit ReadBit()
{
if (!ValidPosition)
throw new IOException("Cannot read in an offset bigger than the length of the stream");
byte value = (byte)((currentByte >> (bit)) & 1);
AdvanceBit();
return value;
}
#endregion
public byte[] ReadBytes(long length, bool isBytes = false, long byteLength = 0)
{
if (isBytes)
length *= 8;
byteLength = (byteLength == 0 ? length / 8 : byteLength);
byte[] data = new byte[byteLength];
for (long i = 0; i < length;)
{
byte value = 0;
for (int p = 0; p < 8 && i < length; i++, p++)
value |= (byte)(ReadBit() << p);
data[((i + 7) / 8) - 1] = value;
}
return data;
}
public byte[] ReadBytesPadded(long length)
{
int requiredSize = NextPow2((int)(length + 7) / 8);
byte[] data = ReadBytes(length, false, requiredSize);
return data;
}
public byte ReadByte()
{
return ReadBytes(8)[0];
}
public byte ReadByte(int bits)
{
bits = Math.Min(Math.Max(bits, 0), 8); // clamp values
return ReadBytes(bits)[0];
}
public string ReadString(int length)
{
// UTF8 - revert if encoding gets exposed
return encoding.GetString(ReadBytes(8 * length));
}
public short ReadInt16()
{
short value = BitConverter.ToInt16(ReadBytes(16), 0);
return value;
}
public int ReadInt32()
{
int value = BitConverter.ToInt32(ReadBytes(32), 0);
return value;
}
public long ReadInt64()
{
long value = BitConverter.ToInt64(ReadBytes(64), 0);
return value;
}
public ushort ReadUInt16()
{
ushort value = BitConverter.ToUInt16(ReadBytes(16), 0);
return value;
}
public uint ReadUInt32(int bitWidth = 32)
{
bitWidth = Math.Min(Math.Max(bitWidth, 0), 32); // clamp values
byte[] data = ReadBytes(bitWidth, false, 4);
return BitConverter.ToUInt32(data, 0);
}
public ulong ReadUInt64()
{
ulong value = BitConverter.ToUInt64(ReadBytes(64), 0);
return value;
}
private int NextPow2(int v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return Math.Max(v, 1);
}
public void Dispose()
{
((IDisposable)stream)?.Dispose();
}
internal struct Bit
{
private byte value;
public Bit(int value)
{
this.value = (byte)(value & 1);
}
public static implicit operator Bit(int value) => new Bit(value);
public static implicit operator byte(Bit bit) => bit.value;
}
}
public class BitReader
{
private byte[] m_array;
private int m_readPos;
private int m_readOffset;
public int Position { get => m_readPos; set => m_readPos = value; }
public int Offset { get => m_readOffset; set => m_readOffset = value; }
public byte[] Data { get => m_array; set => m_array = value; }
public BitReader(byte[] data)
{
m_array = data;
}
public BitReader(byte[] data, int offset)
{
m_array = data;
m_readOffset = offset;
}
public uint ReadUInt32(int numBits)
{
uint result = FastStruct<uint>.ArrayToStructure(ref m_array[m_readOffset + (m_readPos >> 3)]) << (32 - numBits - (m_readPos & 7)) >> (32 - numBits);
m_readPos += numBits;
return result;
}
public ulong ReadUInt64(int numBits)
{
ulong result = FastStruct<ulong>.ArrayToStructure(ref m_array[m_readOffset + (m_readPos >> 3)]) << (64 - numBits - (m_readPos & 7)) >> (64 - numBits);
m_readPos += numBits;
return result;
}
public Value32 ReadValue32(int numBits)
{
unsafe
{
ulong result = ReadUInt32(numBits);
return *(Value32*)&result;
}
}
public Value64 ReadValue64(int numBits)
{
unsafe
{
ulong result = ReadUInt64(numBits);
return *(Value64*)&result;
}
}
// this will probably work in C# 7.3 once blittable generic constrain added, or not...
//public unsafe T Read<T>(int numBits) where T : struct
//{
// //fixed (byte* ptr = &m_array[m_readOffset + (m_readPos >> 3)])
// //{
// // T val = *(T*)ptr << (sizeof(T) - numBits - (m_readPos & 7)) >> (sizeof(T) - numBits);
// // m_readPos += numBits;
// // return val;
// //}
// //T result = FastStruct<T>.ArrayToStructure(ref m_array[m_readOffset + (m_readPos >> 3)]) << (32 - numBits - (m_readPos & 7)) >> (32 - numBits);
// //m_readPos += numBits;
// //return result;
//}
}
public struct Value32
{
unsafe fixed byte Value[4];
public T GetValue<T>() where T : struct
{
unsafe
{
fixed (byte* ptr = Value)
return FastStruct<T>.ArrayToStructure(ref ptr[0]);
}
}
public byte[] GetBytes(int bitSize)
{
byte[] data = new byte[NextPow2((int)(bitSize + 7) / 8)];
unsafe
{
fixed (byte* ptr = Value)
{
for (var i = 0; i < data.Length; ++i)
data[i] = ptr[i];
}
}
return data;
}
private int NextPow2(int v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return Math.Max(v, 1);
}
}
public struct Value64
{
unsafe fixed byte Value[8];
public T GetValue<T>() where T : struct
{
unsafe
{
fixed (byte* ptr = Value)
return FastStruct<T>.ArrayToStructure(ref ptr[0]);
}
}
public byte[] GetBytes(int bitSize)
{
byte[] data = new byte[NextPow2((int)(bitSize + 7) / 8)];
unsafe
{
fixed (byte* ptr = Value)
{
for (var i = 0; i < data.Length; ++i)
data[i] = ptr[i];
}
}
return data;
}
private int NextPow2(int v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return Math.Max(v, 1);
}
}
}