Skip to content

Instantly share code, notes, and snippets.

@redheadgektor
Last active March 13, 2022 07:23
Show Gist options
  • Save redheadgektor/3c508955868306a81a0a19308656ae40 to your computer and use it in GitHub Desktop.
Save redheadgektor/3c508955868306a81a0a19308656ae40 to your computer and use it in GitHub Desktop.
simple byte stream
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
/*
ByteStream stream = new ByteStream();
stream.Write<int>(1234);
stream.Write<byte>(123);
stream.Read<int>();
stream.Read<byte>();
*/
namespace Stream
{
/// <summary>
/// Write/Read from byte buffer
/// </summary>
public unsafe class ByteStream : IDisposable
{
public const int DefaultBufferSize = 2048;
public const int MaxStringBufferSize = short.MaxValue;
public ByteStream()
{
RawBuffer = new byte[DefaultBufferSize];
}
public ByteStream(int size)
{
RawBuffer = new byte[size];
}
#region Disposing
public virtual void Dispose()
{
lock (this)
{
GC.SuppressFinalize(this);
}
}
~ByteStream()
{
Dispose();
}
#endregion
protected byte[] RawBuffer;
public int Position;
public int Length { get; private set; }
public int Remain { get { return Length - Position; } }
public int Capacity { get { return RawBuffer != null ? RawBuffer.Length : 0; } }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FixLength()
{
if (Length < Position)
{
Length = Position;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CheckRead(int size)
{
if (Position + size > Length)
{
throw new EndOfStreamException($"ByteStream -> End of buffer! Position: {Position} Length: {Length} Sz: {size}");
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CheckRead<T>() where T : unmanaged
{
unsafe
{
int size = sizeof(T);
CheckRead(size);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetData(byte[] data)
{
RawBuffer = data;
Position = 0;
Length = data.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetData(byte* data, int length)
{
unsafe
{
int i = 0;
while (i < length)
{
RawBuffer[i] = data[i];
i++;
}
}
Position = 0;
Length = length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte[] GetData(bool rawData = true)
{
if (!rawData)
{
byte[] data = new byte[Length];
Array.ConstrainedCopy(RawBuffer, 0, data, 0, Position);
return data;
}
else
{
return RawBuffer;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureCapacity(int value)
{
if (RawBuffer.Length < value)
{
int capacity = Math.Max(value, RawBuffer.Length * 2);
Array.Resize(ref RawBuffer, capacity);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset(bool resetLength = false, bool clearRawBuffer = false)
{
Position = 0;
if (clearRawBuffer)
{
Array.Clear(RawBuffer, 0, RawBuffer.Length);
Length = 0;
}
if (resetLength)
{
Length = 0;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Skip<T>() where T : unmanaged
{
unsafe
{
SkipWrite(sizeof(T));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SkipWrite(int bytes)
{
Position += bytes;
FixLength();
}
public bool CanRead { get { return Position + 1 > Length; } }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write<T>(T value) where T : unmanaged
{
unsafe
{
int size = sizeof(T);
EnsureCapacity(Position + size);
fixed (byte* ptr = &RawBuffer[Position])
{
*(T*)ptr = value;
}
Position += size;
FixLength();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SkipRead<T>() where T : unmanaged
{
unsafe
{
SkipRead(sizeof(T));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SkipRead(int bytes)
{
CheckRead(bytes);
Position += bytes;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Read<T>() where T : unmanaged
{
unsafe
{
int size = sizeof(T);
CheckRead(size);
T value;
fixed (byte* ptr = &RawBuffer[Position])
{
value = *(T*)ptr;
}
Position += size;
return value;
}
}
/* BYTE ARRAY */
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteBytes(byte[] buffer, int offset, int count)
{
EnsureCapacity(Position + count);
Array.ConstrainedCopy(buffer, offset, RawBuffer, Position, count);
Position += count;
FixLength();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte[] ReadBytes(int offset, int count)
{
CheckRead(count);
byte[] data = new byte[count];
Array.ConstrainedCopy(RawBuffer, Position + offset, data, 0, count);
return data;
}
/* STRING */
private readonly UTF8Encoding encoding = new UTF8Encoding(false, true);
private readonly byte[] stringBuffer = new byte[MaxStringBufferSize];
public void WriteString(string value, char terminator = '\0')
{
if (value == null)
{
Write(false);
return;
}
int size = encoding.GetBytes(value, 0, value.Length, stringBuffer, 0);
bool is_empty = size == 0 || string.IsNullOrEmpty(value);
Write(is_empty);
if (is_empty)
return;
int i = 0;
while (i < size)
{
Write(stringBuffer[i]);
i++;
}
Write((byte)terminator);
}
public string ReadString(char terminator = '\0')
{
bool is_empty = Read<bool>();
if (is_empty)
{
return string.Empty;
}
int oldPos = Position;
char c = char.MaxValue;
int length = 0;
while (c != terminator)
{
c = (char)Read<byte>();
length = Position - oldPos;
if (c == terminator)
{
break;
}
}
length -= 1;//ignore terminator
return encoding.GetString(RawBuffer, oldPos, length);
}
}
}
public static class ByteStreamEx
{
public static void Write8(this ByteStream stream, float value, float min, float max)
{
stream.Write((byte)(255f * (value - min) / (max - min)));
}
public static float Read8(this ByteStream stream, float min, float max)
{
return min + stream.Read<byte>() / 255f * (max - min);
}
public static void Write8(this ByteStream stream, float value)
{
stream.Write((byte)(value * 255f));
}
public static float Read8(this ByteStream stream)
{
return stream.Read<byte>() / 255f;
}
public static void Write16(this ByteStream stream, float value, float min, float max)
{
stream.Write((ushort)(65535.0f * (value - min) / (max - min)));
}
public static float Read16(this ByteStream stream, float min, float max)
{
return min + stream.Read<ushort>() / 65535.0f * (max - min);
}
public static void Write16(this ByteStream stream, float value)
{
stream.Write((short)(value * 32767f));
}
public static float Read16(this ByteStream stream)
{
return stream.Read<short>() / 32767f;
}
public static void Write(this ByteStream stream, Vector2 vec, Vector2 min, Vector2 max)
{
stream.Write16(vec.x, min.x, max.x);
stream.Write16(vec.y, min.y, max.y);
}
public static Vector2 Read(this ByteStream stream, Vector2 min, Vector2 max)
{
return new Vector2(stream.Read16(min.x, max.x), stream.Read16(min.y, max.y));
}
public static void Write(this ByteStream stream, Vector3 vec, Vector3 min, Vector3 max)
{
stream.Write16(vec.x, min.x, max.x);
stream.Write16(vec.y, min.y, max.y);
stream.Write16(vec.z, min.z, max.z);
}
public static Vector3 Read(this ByteStream stream, Vector3 min, Vector3 max)
{
return new Vector3(stream.Read16(min.x, max.x), stream.Read16(min.y, max.y), stream.Read16(min.z, max.z));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment