165 lines
5.8 KiB
C#
165 lines
5.8 KiB
C#
using ln.type;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
|
|
namespace ln.objects.serialization.binary
|
|
{
|
|
public class BinarySerializer : Serializer
|
|
{
|
|
public override bool SerializeObject(object o, out byte[] serializedBytes)
|
|
{
|
|
MemoryStream serializedStream = new MemoryStream();
|
|
Type type = o.GetType();
|
|
|
|
if (type.IsPrimitive || (type == typeof(string)))
|
|
SerializePrimitive(serializedStream, o);
|
|
else if (type.IsEnum)
|
|
SerializeEnum(serializedStream, o);
|
|
else if (type.IsValueType)
|
|
SerializeValue(serializedStream, o);
|
|
else if (type.IsArray)
|
|
SerializeArray(serializedStream, o);
|
|
else
|
|
SerializeStructured(serializedStream, o);
|
|
|
|
serializedBytes = serializedStream.ToArray();
|
|
return true;
|
|
}
|
|
|
|
void Serialize(Stream stream, object o)
|
|
{
|
|
if (Object.ReferenceEquals(null, o))
|
|
{
|
|
stream.WriteByte('0');
|
|
}
|
|
else
|
|
{
|
|
Type type = o.GetType();
|
|
|
|
if (type.IsPrimitive || (type == typeof(string)))
|
|
SerializePrimitive(stream, o);
|
|
else if (type.IsEnum)
|
|
SerializeEnum(stream, o);
|
|
else if (type.IsValueType)
|
|
SerializeValue(stream, o);
|
|
else if (type.IsArray)
|
|
SerializeArray(stream, o);
|
|
else
|
|
{
|
|
if (TryLookupReference(o, out object reference))
|
|
{
|
|
stream.WriteByte('R');
|
|
SerializePrimitive(stream, type.GetSimpleQualifiedName());
|
|
Serialize(stream, reference);
|
|
}
|
|
else
|
|
{
|
|
SerializeStructured(stream, o);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SerializePrimitive(Stream stream, object value)
|
|
{
|
|
stream.WriteByte('P');
|
|
|
|
if (value is int i) { stream.WriteByte('I'); stream.WriteInteger(i); }
|
|
else if (value is uint ui) { stream.WriteByte('i'); stream.WriteUInteger(ui); }
|
|
else if (value is byte b) { stream.WriteByte('B'); stream.WriteByte(b); }
|
|
else if (value is char ch) { stream.WriteByte('C'); stream.WriteShort((short)ch); }
|
|
else if (value is short sh) { stream.WriteByte('S'); stream.WriteShort(sh); }
|
|
else if (value is ushort us) { stream.WriteByte('s'); stream.WriteUShort(us); }
|
|
else if (value is long il) { stream.WriteByte('L'); stream.WriteLong(il); }
|
|
else if (value is ulong ul) { stream.WriteByte('l'); stream.WriteULong(ul); }
|
|
else if (value is float f) { stream.WriteByte('F'); stream.WriteFloat(f); }
|
|
else if (value is double d) { stream.WriteByte('D'); stream.WriteDouble(d); }
|
|
else if (value is bool bo) { stream.WriteByte('b'); stream.WriteByte(bo ? (byte)0x01 : (byte)0x00); }
|
|
else if (value is string str)
|
|
{
|
|
stream.WriteByte('T');
|
|
byte[] bytes = Encoding.UTF8.GetBytes(str);
|
|
stream.WriteInteger(bytes.Length);
|
|
stream.WriteBytes(bytes);
|
|
}
|
|
else
|
|
throw new NotSupportedException(String.Format("Unsupported primitive type: {0}", value.GetType().Name));
|
|
|
|
// ToDo: Add struct System.Decimal
|
|
}
|
|
|
|
void SerializeEnum(Stream stream, object value)
|
|
{
|
|
Type eType = value.GetType();
|
|
|
|
stream.WriteByte('E');
|
|
SerializePrimitive(stream, eType.GetSimpleQualifiedName());
|
|
|
|
if (eType.GetCustomAttribute<FlagsAttribute>() != null)
|
|
SerializePrimitive(stream, (int)value);
|
|
else
|
|
SerializePrimitive(stream, value.ToString());
|
|
}
|
|
|
|
void SerializeArray(Stream stream, object value)
|
|
{
|
|
if (value is byte[] ba)
|
|
{
|
|
stream.WriteByte('B');
|
|
stream.WriteInteger(ba.Length);
|
|
stream.WriteBytes(ba);
|
|
}
|
|
else
|
|
{
|
|
Array array = (Array)value;
|
|
|
|
stream.WriteByte('A');
|
|
SerializePrimitive(stream, array.GetType().GetElementType().GetSimpleQualifiedName());
|
|
stream.WriteInteger(array.Length);
|
|
|
|
for (int n = 0; n < array.Length; n++)
|
|
Serialize(stream, array.GetValue(n));
|
|
}
|
|
}
|
|
|
|
void SerializeValue(Stream stream, object value)
|
|
{
|
|
Type valueType = value.GetType();
|
|
|
|
if (value is Guid guid)
|
|
{
|
|
stream.WriteByte('G');
|
|
stream.WriteBytes(guid.ToByteArray());
|
|
}
|
|
else
|
|
SerializeStructured(stream, value);
|
|
}
|
|
|
|
void SerializeStructured(Stream stream, object value)
|
|
{
|
|
Type type = value.GetType();
|
|
FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
|
|
|
stream.WriteByte('S');
|
|
SerializePrimitive(stream, type.GetSimpleQualifiedName());
|
|
stream.WriteInteger(fields.Length);
|
|
|
|
foreach (FieldInfo fieldInfo in fields)
|
|
{
|
|
object v = fieldInfo.GetValue(value);
|
|
|
|
if (MangleValue(fieldInfo.FieldType, ref v))
|
|
fieldInfo.SetValue(value, v);
|
|
|
|
SerializePrimitive(stream, fieldInfo.Name);
|
|
Serialize(stream, v);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|