ln.objects/serialization/binary/BinarySerializer.cs

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);
}
}
}
}