Skip to content

Instantly share code, notes, and snippets.

@Lachee
Last active May 12, 2021 02:23
Show Gist options
  • Save Lachee/ce041206e18c49e5020017a4eeff02f4 to your computer and use it in GitHub Desktop.
Save Lachee/ce041206e18c49e5020017a4eeff02f4 to your computer and use it in GitHub Desktop.
Variable Data Type for C# and Unity3D
using System;
using System.IO;
#if UNITY_5_3_OR_NEWER
using UnityEngine;
#else
using System.Numerics;
#endif
namespace Lachee.Dynamic
{
public class UnkownTypeException : Exception { }
public enum DataType
{
Null = 0x00,
String = 0x01,
Byte = 0x02,
Int16 = 0x03,
Int32 = 0x04,
Int64 = 0x05,
Float = 0x06,
Double = 0x07,
Boolean = 0x08,
Variable = 0x09,
Vector2 = 0x0A,
Vector3 = 0x0B,
Vector4 = 0x0C,
}
public class VarData
{
private DataType _type;
public DataType Type { get { return _type; } }
private bool _isArray;
public bool IsArray { get { return _isArray; } }
private int _arrayLength;
public int ArrayLength { get { return _arrayLength; } }
#if (!NET_LEGACY && !NET_STANDARD_2_0) || DYNAMIC
private dynamic _value;
public dynamic Value
#else
private object _value;
public object Value
#endif
{
get { return _value; }
set
{
bool isArray;
DataType type;
//Get the type
if (!TryGetType(value, out type, out isArray))
throw new Exception(value.GetType().FullName + " is of an invalid type. ");
//Manually determine the length of the array
#if (!NET_LEGACY && !NET_STANDARD_2_0) || DYNAMIC
_arrayLength = isArray ? value.Length : 1;
#else
_arrayLength = isArray ? ((Array)value).Length : 1;
#endif
//Set the raw value
SetRawValue(Value, type, isArray, _arrayLength);
}
}
/// <summary>Encodes the typed data into the binary reader</summary>
public void Encode(BinaryWriter writer)
{
//Console.WriteLine(Type.ToString());
//Write the type, if we are an array we will set the most significant bit
int type = (int) Type;
if (IsArray) type |= 0x80;
writer.Write((byte)type);
if (Type == DataType.Null) return;
//Write the data
if (IsArray) writer.Write(ArrayLength);
//Iterate over every element we are storing (1 by default) and write it to the stream
for (int i = 0; i < ArrayLength; i++)
{
#if (!NET_LEGACY && !NET_STANDARD_2_0) || DYNAMIC
dynamic v = IsArray ? Value.GetValue(i) : Value;
#else
object v = IsArray ? ((Array)Value).GetValue(i) : Value;
#endif
switch (Type)
{
default:
throw new UnkownTypeException();
case DataType.Null: break;
case DataType.Byte:
writer.Write((byte)v); break;
case DataType.Boolean:
writer.Write((bool)v); break;
case DataType.Double:
writer.Write((double)v); break;
case DataType.Float:
writer.Write((float)v); break;
case DataType.Int16:
writer.Write((short)v); break;
case DataType.Int32:
writer.Write((int)v); break;
case DataType.Int64:
writer.Write((long)v); break;
case DataType.String:
writer.Write((string)v); break;
#if UNITY_5_3_OR_NEWER
case DataType.Vector2:
Vector2 vector2 = (Vector2)v;
writer.Write(vector2.x); writer.Write(vector2.y);
break;
case DataType.Vector3:
Vector3 vector3 = (Vector3)v;
writer.Write(vector3.x); writer.Write(vector3.y); writer.Write(vector3.z);
break;
case DataType.Vector4:
Vector4 vector4 = (Vector4)v;
writer.Write(vector4.x); writer.Write(vector4.y); writer.Write(vector4.z); writer.Write(vector4.w);
break;
#else
case DataType.Vector2:
Vector2 vector2 = (Vector2)v;
writer.Write(vector2.X); writer.Write(vector2.Y);
break;
case DataType.Vector3:
Vector3 vector3 = (Vector3)v;
writer.Write(vector3.X); writer.Write(vector3.Y); writer.Write(vector3.Z);
break;
case DataType.Vector4:
Vector4 vector4 = (Vector4)v;
writer.Write(vector4.X); writer.Write(vector4.Y); writer.Write(vector4.Z); writer.Write(vector4.W);
break;
#endif
case DataType.Variable:
#if (!NET_LEGACY && !NET_STANDARD_2_0) || DYNAMIC
v.Encode(writer);
#else
((VarData)v).Encode(writer);
#endif
break;
}
}
}
/// <summary>Decodes the Typed Data from the binary reader</summary>
public void Decode(BinaryReader reader)
{
//Read the type and if its an array
byte b = reader.ReadByte();
int arrayFlag = b >> 7; // Get the MSB which indicates if its an array or not
int typeTag = b & 0x7F; // Get the type
DataType type = (DataType)typeTag;
bool isArray = arrayFlag != 0;
if (type == DataType.Null) return;
//Read the length
int length = isArray ? reader.ReadInt32() : 1;
//Prepare an array of contents
#if (!NET_LEGACY && !NET_STANDARD_2_0) || DYNAMIC
dynamic[] contents = new dynamic[length];
#else
object[] contents = new object[length];
#endif
for (int i = 0; i < length; i++)
{
switch (type)
{
default:
throw new UnkownTypeException();
case DataType.Null: break;
case DataType.Byte:
contents[i] = reader.ReadByte(); break;
case DataType.Boolean:
contents[i] = reader.ReadBoolean(); break;
case DataType.Double:
contents[i] = reader.ReadDouble(); break;
case DataType.Float:
contents[i] = reader.ReadSingle(); break;
case DataType.Int16:
contents[i] = reader.ReadInt16(); break;
case DataType.Int32:
contents[i] = reader.ReadInt32(); break;
case DataType.Int64:
contents[i] = reader.ReadInt64(); break;
case DataType.String:
contents[i] = reader.ReadString(); break;
case DataType.Vector2:
contents[i] = new Vector2(reader.ReadSingle(), reader.ReadSingle());
break;
case DataType.Vector3:
contents[i] = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
break;
case DataType.Vector4:
contents[i] = new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
break;
case DataType.Variable:
VarData data = new VarData();
data.Decode(reader);
contents[i] = data;
break;
}
}
//Set the raw content. If we are an array use the entire thing, otherwise just the first element
if (isArray) SetRawValue(contents, type, true, contents.Length);
else SetRawValue(contents[0], type, false, 1);
}
public override string ToString()
{
if (_type == DataType.Null) return "null";
return "(" + _type.ToString() + ") " + Value.ToString();
}
#if (!NET_LEGACY && !NET_STANDARD_2_0) || DYNAMIC
public static bool TryGetType(dynamic obj, out DataType type, out bool isArray)
#else
public static bool TryGetType(object obj, out DataType type, out bool isArray)
#endif
{
//Prepare the outputs
type = DataType.Null;
isArray = false;
//Its a null object, so return the null type
if (obj == null)
return true;
//Get the object type
Type t = obj.GetType();
//Check if its an array. If it is, select the subtype.
if (isArray = t.IsArray) t = t.GetElementType();
if (t == typeof(string)) type = DataType.String;
else if (t == typeof(byte)) type = DataType.Byte;
else if (t == typeof(short)) type = DataType.Int16;
else if (t == typeof(int)) type = DataType.Int32;
else if (t == typeof(long)) type = DataType.Int64;
else if (t == typeof(float)) type = DataType.Float;
else if (t == typeof(double)) type = DataType.Double;
else if (t == typeof(bool)) type = DataType.Boolean;
else if (t == typeof(VarData)) type = DataType.Variable;
else if (t == typeof(Vector2)) type = DataType.Vector2;
else if (t == typeof(Vector3)) type = DataType.Vector3;
else if (t == typeof(Vector4)) type = DataType.Vector4;
else return false;
return true;
}
/// <summary>Sets the raw value directly</summary>
#if (!NET_LEGACY && !NET_STANDARD_2_0) || DYNAMIC
private void SetRawValue(dynamic value, DataType type, bool isArray, int arrayLength)
#else
private void SetRawValue(object value, DataType type, bool isArray, int arrayLength)
#endif
{
_value = value;
_type = type;
_isArray = isArray;
_arrayLength = _isArray ? arrayLength : 1;
}
#region Casts
/*
=========================================================================================================
| Some casts I was possibly going to do. It just makes for messy and unreadable code though. |
| Plus I won't be able to cast the Variable type and woudl require a bunch of work for the Array types. |
=========================================================================================================
public static explicit operator string(VarData data) { return (string)data.Value; }
public static explicit operator short(VarData data) { return (short)data.Value; }
public static explicit operator int(VarData data) { return (int)data.Value; }
public static explicit operator long(VarData data) { return (long)data.Value; }
public static explicit operator float(VarData data) { return (float)data.Value; }
public static explicit operator double(VarData data) { return (double)data.Value; }
*/
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment