Last active
May 12, 2021 02:23
-
-
Save Lachee/ce041206e18c49e5020017a4eeff02f4 to your computer and use it in GitHub Desktop.
Variable Data Type for C# and Unity3D
This file contains hidden or 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; | |
#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