Last active
March 13, 2022 07:23
-
-
Save redheadgektor/3c508955868306a81a0a19308656ae40 to your computer and use it in GitHub Desktop.
simple byte stream
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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