Created
December 19, 2021 22:38
-
-
Save RonenNess/e21828394c200fa58d48886edb22d081 to your computer and use it in GitHub Desktop.
Utility classes to pack and unpack data in bytes buffers in C#
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
/// <summary> | |
/// Utility to serialize data directly into a buffer. | |
/// </summary> | |
public class MessageSerializer | |
{ | |
// the buffer containing the data. | |
private byte[] _buffer; | |
/// <summary> | |
/// Get the buffer itself that contains the data. | |
/// Be sure to use Size and not Buffer.Length as the buffer may contain padding bytes. | |
/// </summary> | |
public byte[] Buffer => _buffer; | |
/// <summary> | |
/// Number of used bytes. | |
/// </summary> | |
public int Size { get; private set; } | |
/// <summary> | |
/// Return how many padding bytes we have. | |
/// </summary> | |
public int PaddingBytes => _buffer.Length - Size; | |
/// <summary> | |
/// Create the message serializer. | |
/// </summary> | |
/// <param name="startingSize">Buffer starting size (grows dynamically).</param> | |
public MessageSerializer(int startingSize = 128) | |
{ | |
_buffer = new byte[startingSize]; | |
Size = 0; | |
} | |
/// <summary> | |
/// Clear the buffer. | |
/// </summary> | |
/// <param name="shrinkTo">If not 0, will also shrink internal buffer to given size to free actual space.</param> | |
public void Clear(int shrinkTo = 0) | |
{ | |
Size = 0; | |
if (shrinkTo != 0 && Buffer.Length > shrinkTo) | |
{ | |
_buffer = new byte[shrinkTo]; | |
} | |
} | |
/// <summary> | |
/// Push bytes into buffer. | |
/// </summary> | |
/// <param name="data">Data to push.</param> | |
/// <param name="len">Bytes to copy from source buffer.</param> | |
public void PushBytes(byte[] data, int len = 0) | |
{ | |
// default length | |
if (len == 0) { len = data.Length; } | |
// check if need to resize buffer | |
if (Size + len >= _buffer.Length) | |
{ | |
Array.Resize(ref _buffer, _buffer.Length * 2 + len + 5); | |
} | |
// copy bytes into buffer (either single or array) and update used size | |
if (len == 1) | |
{ | |
_buffer[Size] = data[0]; | |
} | |
else | |
{ | |
Array.Copy(data, 0, _buffer, Size, len); | |
} | |
Size += len; | |
} | |
/// <summary> | |
/// Push a string to message. | |
/// </summary> | |
/// <param name="data">String to push.</param> | |
public void PushString(string data) | |
{ | |
var stringBytes = StringUtils.ToBytes(data); | |
PushUShort((ushort)stringBytes.Length); | |
PushBytes(stringBytes); | |
} | |
/// <summary> | |
/// Push a short string to message. | |
/// </summary> | |
/// <param name="data">String to push.</param> | |
public void PushStringShort(string data) | |
{ | |
var stringBytes = StringUtils.ToBytes(data); | |
if (stringBytes.Length > byte.MaxValue) { throw new Exception("Short string too long!"); } | |
PushByte((byte)stringBytes.Length); | |
PushBytes(stringBytes); | |
} | |
/// <summary> | |
/// Push a vector to message as shorts. | |
/// </summary> | |
/// <param name="data">Data to push.</param> | |
public void PushVectorShort(Vector3 data) | |
{ | |
PushShort((short)(data.X * 10)); | |
PushShort((short)(data.Y * 10)); | |
PushShort((short)(data.Z * 10)); | |
} | |
/// <summary> | |
/// Push a vector to message as shorts. | |
/// </summary> | |
/// <param name="data">Data to push.</param> | |
public void PushVectorShortXZ(Vector3 data) | |
{ | |
PushShort((short)(data.X * 10)); | |
PushShort((short)(data.Z * 10)); | |
} | |
/// <summary> | |
/// Push a byte to message. | |
/// </summary> | |
/// <param name="data">Data to push.</param> | |
public void PushByte(byte data) | |
{ | |
PushBytes(new byte[] { data }); | |
} | |
/// <summary> | |
/// Push a boolean to message. | |
/// </summary> | |
/// <param name="data">Data to push.</param> | |
public void PushBool(bool data) | |
{ | |
PushBytes(new byte[] { (byte)(data ? 1 : 0) }); | |
} | |
/// <summary> | |
/// Push a short to message. | |
/// </summary> | |
/// <param name="data">Data to push.</param> | |
public void PushShort(short data) | |
{ | |
PushBytes(new byte[] { (byte)(data & 0xff), (byte)((data >> 8) & 0xff) }); | |
} | |
/// <summary> | |
/// Push a ushort to message. | |
/// </summary> | |
/// <param name="data">Data to push.</param> | |
public void PushUShort(ushort data) | |
{ | |
PushBytes(new byte[] { (byte)(data & 0xff), (byte)((data >> 8) & 0xff) }); | |
} | |
/// <summary> | |
/// Push a ushort to message at offset. | |
/// </summary> | |
/// <param name="data">Data to push.</param> | |
/// <param name="offset">Offset to push ushort into.</param> | |
public void PushUShortInto(ushort data, int offset) | |
{ | |
_buffer[offset] = (byte)(data & 0xff); | |
_buffer[offset + 1] = (byte)((data >> 8) & 0xff); | |
} | |
/// <summary> | |
/// Push a byte to message at offset. | |
/// </summary> | |
/// <param name="data">Data to push.</param> | |
/// <param name="offset">Offset to push byte into.</param> | |
public void PushByteInto(byte data, int offset) | |
{ | |
_buffer[offset] = data; | |
} | |
/// <summary> | |
/// Push an int to message. | |
/// </summary> | |
/// <param name="data">Data to push.</param> | |
public void PushInt(int data) | |
{ | |
PushBytes(new byte[] { (byte)(data & 0xff), (byte)((data >> 8) & 0xff), (byte)((data >> 16) & 0xff), (byte)((data >> 24) & 0xff) }); | |
} | |
/// <summary> | |
/// Push an uint to message. | |
/// </summary> | |
/// <param name="data">Data to push.</param> | |
public void PushUInt(UInt32 data) | |
{ | |
PushBytes(new byte[] { (byte)(data & 0xff), (byte)((data >> 8) & 0xff), (byte)((data >> 16) & 0xff), (byte)((data >> 24) & 0xff) }); | |
} | |
} | |
/// <summary> | |
/// Utility to deserialize data. | |
/// </summary> | |
public struct MessageDeserializer | |
{ | |
// serialized data to deserialize | |
byte[] _data; | |
// offset in buffer | |
int _offset; | |
/// <summary> | |
/// Check if finished buffer. | |
/// </summary> | |
public bool IsDone => _offset >= _data.Length; | |
/// <summary> | |
/// Create the message deserializer. | |
/// </summary> | |
/// <param name="data">Data to deserialize.</param> | |
/// <param name="offset">Offset to start deserializing from.</param> | |
public MessageDeserializer(byte[] data, int offset = 0) | |
{ | |
_data = data; | |
_offset = offset; | |
} | |
/// <summary> | |
/// Pop a string from message. | |
/// </summary> | |
public string PopString() | |
{ | |
var len = PopUShort(); | |
var asString = StringUtils.ToString(_data, _offset, len); | |
_offset += len; | |
return asString; | |
} | |
/// <summary> | |
/// Pop a string from message. | |
/// </summary> | |
public string PopStringShort() | |
{ | |
var len = PopByte(); | |
var asString = StringUtils.ToString(_data, _offset, len); | |
_offset += len; | |
return asString; | |
} | |
/// <summary> | |
/// Pop a vector that is stored by shorts. | |
/// </summary> | |
/// <returns>Vector instance.</returns> | |
public Vector3 PopVectorShort() | |
{ | |
var x = PopShort() / 10.0f; | |
var y = PopShort() / 10.0f; | |
var z = PopShort() / 10.0f; | |
return new Vector3(x, y, z); | |
} | |
/// <summary> | |
/// Pop a vector that is stored by shorts, but only X and Z. | |
/// </summary> | |
/// <returns>Vector instance.</returns> | |
public Vector3 PopVectorShortXZ() | |
{ | |
var x = PopShort() / 10.0f; | |
var z = PopShort() / 10.0f; | |
return new Vector3(x, 0, z); | |
} | |
/// <summary> | |
/// Pop a byte from message. | |
/// </summary> | |
public byte PopByte() | |
{ | |
return _data[_offset++]; | |
} | |
/// <summary> | |
/// Pop number of bytes from message. | |
/// </summary> | |
public byte[] PopBytes(int amount) | |
{ | |
byte[] ret = new byte[amount]; | |
Array.Copy(_data, _offset, ret, 0, amount); | |
_offset += amount; | |
return ret; | |
} | |
/// <summary> | |
/// Pop a boolean from message. | |
/// </summary> | |
public bool PopBool() | |
{ | |
return _data[_offset++] != 0; | |
} | |
/// <summary> | |
/// Pop a short from message. | |
/// </summary> | |
public short PopShort() | |
{ | |
var a = _data[_offset++]; | |
var b = _data[_offset++]; | |
return (short)((int)a | (int)(b << 8)); | |
} | |
/// <summary> | |
/// Pop a ushort from message. | |
/// </summary> | |
public ushort PopUShort() | |
{ | |
var a = _data[_offset++]; | |
var b = _data[_offset++]; | |
return (ushort)((int)a | (int)(b << 8)); | |
} | |
/// <summary> | |
/// Pop an int from message. | |
/// </summary> | |
public int PopInt() | |
{ | |
var a = _data[_offset++]; | |
var b = _data[_offset++]; | |
var c = _data[_offset++]; | |
var d = _data[_offset++]; | |
return ((int)a | (int)(b << 8) | (int)(c << 16) | (int)(d << 24)); | |
} | |
/// <summary> | |
/// Pop an uint from message. | |
/// </summary> | |
public UInt32 PopUInt() | |
{ | |
var a = _data[_offset++]; | |
var b = _data[_offset++]; | |
var c = _data[_offset++]; | |
var d = _data[_offset++]; | |
return (UInt32)((int)a | (int)(b << 8) | (int)(c << 16) | (int)(d << 24)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment