rd-net/RdFramework/Impl/Serializers.cs (269 lines of code) (raw):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Core;
using JetBrains.Diagnostics;
using JetBrains.Lifetimes;
using JetBrains.Serialization;
using JetBrains.Util.Util;
namespace JetBrains.Rd.Impl
{
public static class Polymorphic<T>
{
public static readonly CtxReadDelegate<T> Read = (ctx, reader) => ctx.Serializers.Read<T>(ctx, reader)!;
public static readonly CtxWriteDelegate<T> Write = (ctx, writer, value) => ctx.Serializers.Write(ctx, writer, value);
public static CtxReadDelegate<T?> ReadAbstract(CtxReadDelegate<T> unknownInstanceReader)
{
return (ctx, reader) => ctx.Serializers.Read<T>(ctx, reader, unknownInstanceReader);
}
}
public class Serializers : ISerializers
{
private readonly Dictionary<Type, RdId> myTypeMapping = new Dictionary<Type, RdId>();
private readonly Dictionary<RdId, CtxReadDelegate<object?>> myReaders = new Dictionary<RdId, CtxReadDelegate<object?>>();
private readonly Dictionary<RdId, CtxWriteDelegate<object?>> myWriters = new Dictionary<RdId, CtxWriteDelegate<object?>>();
private readonly ITypesRegistrar? myRegistrar;
private readonly object myLock = new object();
private readonly StealingScheduler myBackgroundRegistrar;
public Serializers() : this(null, null)
{
}
public Serializers(TaskScheduler? scheduler, ITypesRegistrar? registrar)
{
myRegistrar = registrar;
myBackgroundRegistrar = new StealingScheduler(new ConcurrentExclusiveSchedulerPair(scheduler ?? TaskScheduler.Default).ExclusiveScheduler, false);
RegisterToplevelOnce(typeof(Serializers), RegisterFrameworkMarshallers);
}
public Serializers(ITypesRegistrar? registrar)
: this()
{
myRegistrar = registrar;
}
[Obsolete("Lifetime is not required anymore", false)]
public Serializers(Lifetime lifetime, TaskScheduler? scheduler, ITypesRegistrar? registrar)
: this(scheduler, registrar)
{
}
//readers
public static readonly CtxReadDelegate<byte> ReadByte = (ctx, reader) => reader.ReadByte();
public static readonly CtxReadDelegate<short> ReadShort = (ctx, reader) => reader.ReadShort();
public static readonly CtxReadDelegate<int> ReadInt = (ctx, reader) => reader.ReadInt();
public static readonly CtxReadDelegate<long> ReadLong = (ctx, reader) => reader.ReadLong();
public static readonly CtxReadDelegate<float> ReadFloat = (ctx, reader) => reader.ReadFloat();
public static readonly CtxReadDelegate<double> ReadDouble = (ctx, reader) => reader.ReadDouble();
public static readonly CtxReadDelegate<char> ReadChar = (ctx, reader) => reader.ReadChar();
public static readonly CtxReadDelegate<bool> ReadBool = (ctx, reader) => reader.ReadBool();
public static readonly CtxReadDelegate<Unit> ReadVoid = (ctx, reader) => reader.ReadVoid();
public static readonly CtxReadDelegate<string?> ReadString = (ctx, reader) => reader.ReadString();
public static readonly CtxReadDelegate<Guid> ReadGuid = (ctx, reader) => reader.ReadGuid();
public static readonly CtxReadDelegate<DateTime> ReadDateTime = (ctx, reader) => reader.ReadDateTime();
public static readonly CtxReadDelegate<TimeSpan> ReadTimeSpan = (ctx, reader) => reader.ReadTimeSpan();
public static readonly CtxReadDelegate<Uri> ReadUri = (ctx, reader) => reader.ReadUri();
public static readonly CtxReadDelegate<RdId> ReadRdId = (ctx, reader) => reader.ReadRdId();
public static readonly CtxReadDelegate<RdSecureString> ReadSecureString = (ctx, reader) => reader.ReadSecureString();
public static readonly CtxReadDelegate<byte[]?> ReadByteArray = (ctx, reader) => reader.ReadArray(ReadByte, ctx);
public static readonly CtxReadDelegate<short[]?> ReadShortArray = (ctx, reader) => reader.ReadArray(ReadShort, ctx);
public static readonly CtxReadDelegate<int[]?> ReadIntArray = (ctx, reader) => reader.ReadArray(ReadInt, ctx);
public static readonly CtxReadDelegate<long[]?> ReadLongArray = (ctx, reader) => reader.ReadArray(ReadLong, ctx);
public static readonly CtxReadDelegate<float[]?> ReadFloatArray = (ctx, reader) => reader.ReadArray(ReadFloat, ctx);
public static readonly CtxReadDelegate<double[]?> ReadDoubleArray = (ctx, reader) => reader.ReadArray(ReadDouble, ctx);
public static readonly CtxReadDelegate<char[]?> ReadCharArray = (ctx, reader) => reader.ReadArray(ReadChar, ctx);
public static readonly CtxReadDelegate<bool[]?> ReadBoolArray = (ctx, reader) => reader.ReadArray(ReadBool, ctx);
public static readonly CtxReadDelegate<byte> ReadUByte = (ctx, reader) => reader.ReadUByte();
public static readonly CtxReadDelegate<ushort> ReadUShort = (ctx, reader) => reader.ReadUShort();
public static readonly CtxReadDelegate<uint> ReadUInt = (ctx, reader) => reader.ReadUInt();
public static readonly CtxReadDelegate<ulong> ReadULong = (ctx, reader) => reader.ReadULong();
public static readonly CtxReadDelegate<byte[]?> ReadUByteArray = (ctx, reader) => reader.ReadArray(ReadByte, ctx);
public static readonly CtxReadDelegate<ushort[]?> ReadUShortArray = (ctx, reader) => reader.ReadArray(ReadUShort, ctx);
public static readonly CtxReadDelegate<uint[]?> ReadUIntArray = (ctx, reader) => reader.ReadArray(ReadUInt, ctx);
public static readonly CtxReadDelegate<ulong[]?> ReadULongArray = (ctx, reader) => reader.ReadArray(ReadULong, ctx);
//writers
public static readonly CtxWriteDelegate<byte> WriteByte = (ctx, writer, value) => writer.WriteByte(value);
public static readonly CtxWriteDelegate<short> WriteShort = (ctx, writer, value) => writer.WriteInt16(value);
public static readonly CtxWriteDelegate<int> WriteInt = (ctx, writer, value) => writer.WriteInt32(value);
public static readonly CtxWriteDelegate<long> WriteLong = (ctx, writer, value) => writer.WriteInt64(value);
public static readonly CtxWriteDelegate<float> WriteFloat = (ctx, writer, value) => writer.WriteFloat(value);
public static readonly CtxWriteDelegate<double> WriteDouble = (ctx, writer, value) => writer.WriteDouble(value);
public static readonly CtxWriteDelegate<char> WriteChar = (ctx, writer, value) => writer.WriteChar(value);
public static readonly CtxWriteDelegate<bool> WriteBool = (ctx, writer, value) => writer.WriteBoolean(value);
public static readonly CtxWriteDelegate<Unit> WriteVoid = (ctx, writer, value) => writer.Write(value);
public static readonly CtxWriteDelegate<string?> WriteString = (ctx, writer, value) => writer.WriteString(value);
public static readonly CtxWriteDelegate<Guid> WriteGuid = (ctx, writer, value) => writer.WriteGuid(value);
public static readonly CtxWriteDelegate<DateTime> WriteDateTime = (ctx, writer, value) => writer.WriteDateTime(value);
public static readonly CtxWriteDelegate<TimeSpan> WriteTimeSpan = (ctx, writer, value) => writer.WriteTimeSpan(value);
public static readonly CtxWriteDelegate<Uri> WriteUri = (ctx, writer, value) => writer.WriteUri(value);
public static readonly CtxWriteDelegate<RdId> WriteRdId = (ctx, writer, value) => writer.Write(value);
public static readonly CtxWriteDelegate<RdSecureString> WriteSecureString = (ctx, writer, value) => writer.WriteString(value.Contents);
public static readonly CtxWriteDelegate<byte[]?> WriteByteArray = (ctx, writer, value) => writer.WriteArray(WriteByte, ctx, value);
public static readonly CtxWriteDelegate<short[]?> WriteShortArray = (ctx, writer, value) => writer.WriteArray(WriteShort, ctx, value);
public static readonly CtxWriteDelegate<int[]?> WriteIntArray = (ctx, writer, value) => writer.WriteArray(WriteInt, ctx, value);
public static readonly CtxWriteDelegate<long[]?> WriteLongArray = (ctx, writer, value) => writer.WriteArray(WriteLong, ctx, value);
public static readonly CtxWriteDelegate<float[]?> WriteFloatArray = (ctx, writer, value) => writer.WriteArray(WriteFloat, ctx, value);
public static readonly CtxWriteDelegate<double[]?> WriteDoubleArray = (ctx, writer, value) => writer.WriteArray(WriteDouble, ctx, value);
public static readonly CtxWriteDelegate<char[]?> WriteCharArray = (ctx, writer, value) => writer.WriteArray(WriteChar, ctx, value);
public static readonly CtxWriteDelegate<bool[]?> WriteBoolArray = (ctx, writer, value) => writer.WriteArray(WriteBool, ctx, value);
public static readonly CtxWriteDelegate<byte> WriteUByte = (ctx, writer, value) => writer.WriteByte(value);
public static readonly CtxWriteDelegate<ushort> WriteUShort = (ctx, writer, value) => writer.WriteUInt16(value);
public static readonly CtxWriteDelegate<uint> WriteUInt = (ctx, writer, value) => writer.WriteUInt32(value);
public static readonly CtxWriteDelegate<ulong> WriteULong = (ctx, writer, value) => writer.WriteUInt64(value);
public static readonly CtxWriteDelegate<byte[]?> WriteUByteArray = (ctx, writer, value) => writer.WriteArray(WriteByte, ctx, value);
public static readonly CtxWriteDelegate<ushort[]?> WriteUShortArray = (ctx, writer, value) => writer.WriteArray(WriteUShort, ctx, value);
public static readonly CtxWriteDelegate<uint[]?> WriteUIntArray = (ctx, writer, value) => writer.WriteArray(WriteUInt, ctx, value);
public static readonly CtxWriteDelegate<ulong[]?> WriteULongArray = (ctx, writer, value) => writer.WriteArray(WriteULong, ctx, value);
public static void RegisterFrameworkMarshallers(ISerializersContainer serializers)
{
serializers.Register(ReadByte, WriteByte, 1);
serializers.Register(ReadShort, WriteShort, 2);
serializers.Register(ReadInt, WriteInt, 3);
serializers.Register(ReadLong, WriteLong, 4);
serializers.Register(ReadFloat, WriteFloat, 5);
serializers.Register(ReadDouble, WriteDouble, 6);
serializers.Register(ReadChar, WriteChar, 7);
serializers.Register(ReadBool, WriteBool, 8);
serializers.Register(ReadVoid, WriteVoid, 9);
serializers.Register(ReadString, WriteString, 10);
serializers.Register(ReadGuid, WriteGuid, 11);
serializers.Register(ReadDateTime, WriteDateTime, 12);
serializers.Register(ReadUri, WriteUri, 13);
serializers.Register(ReadRdId, WriteRdId, 14);
serializers.Register(ReadSecureString, WriteSecureString, 15);
serializers.Register(ReadByteArray, WriteByteArray, 31);
serializers.Register(ReadShortArray, WriteShortArray, 32);
serializers.Register(ReadIntArray, WriteIntArray, 33);
serializers.Register(ReadLongArray, WriteLongArray, 34);
serializers.Register(ReadFloatArray, WriteFloatArray, 35);
serializers.Register(ReadDoubleArray, WriteDoubleArray, 36);
serializers.Register(ReadCharArray, WriteCharArray, 37);
serializers.Register(ReadBoolArray, WriteBoolArray, 38);
//unsigned
//clashes with Byte
// serializers.Register(ReadUByte, WriteUByte, 41);
serializers.Register(ReadUShort, WriteUShort, 42);
serializers.Register(ReadUInt, WriteUInt, 43);
serializers.Register(ReadULong, WriteULong, 44);
//clashes with ByteArray
// serializers.Register(ReadUByteArray, WriteUByteArray, 45);
serializers.Register(ReadUShortArray, WriteUShortArray, 46);
serializers.Register(ReadUIntArray, WriteUIntArray, 47);
serializers.Register(ReadULongArray, WriteULongArray, 48);
}
public static T ReadEnum<T>(SerializationCtx ctx, UnsafeReader reader) where T :
unmanaged,
Enum
{
if (Mode.IsAssertion) Assertion.Assert(typeof(T).IsSubclassOf(typeof(Enum)), "{0}", typeof(T));
return Cast32BitEnum<T>.FromInt(reader.ReadInt());
}
public static void WriteEnum<T>(SerializationCtx ctx, UnsafeWriter writer, T value) where T :
unmanaged,
Enum
{
writer.WriteInt32(Cast32BitEnum<T>.ToInt(value));
}
public void RegisterEnum<T>() where T :
unmanaged,
Enum
{
Register(ReadEnum<T>, WriteEnum<T>);
}
public void Register<T>(CtxReadDelegate<T> reader, CtxWriteDelegate<T> writer, long? predefinedId = null)
{
lock (myLock)
{
var typeId = RdId.Define<T>(predefinedId);
RdId existing;
if (myTypeMapping.TryGetValue(typeof(T), out existing))
{
Assertion.Require(existing == typeId, "Type {0} already present with id={1}, but now is set with {2}", typeof(T).FullName, existing, typeId);
}
else
{
if (myReaders.ContainsKey(typeId))
{
Type existingType;
lock(myLock)
existingType = myTypeMapping.First(p => p.Value == typeId).Key;
throw new ArgumentException(string.Format("Can't register {0} with id {1}. Already registered {2}", typeof(T).FullName, typeId, existingType));
}
Protocol.InitTrace?.Log($"Registering type {typeof(T).Name}, id={typeId}");
myTypeMapping[typeof(T)] = typeId;
myReaders[typeId] = (ctx, unsafeReader) => reader(ctx, unsafeReader);
myWriters[typeId] = (ctx, unsafeWriter, value) => writer(ctx, unsafeWriter, (T)value!);
}
}
}
public T? Read<T>(SerializationCtx ctx, UnsafeReader reader, CtxReadDelegate<T>? unknownInstanceReader = null)
{
bool TryGetReader(RdId rdId, out CtxReadDelegate<object?> readDelegate)
{
lock (myLock)
return myReaders.TryGetValue(rdId, out readDelegate);
}
myBackgroundRegistrar.Join();
var typeId = RdId.Read(reader);
if (typeId.IsNil)
return default;
var size = reader.ReadInt();
if (!TryGetReader(typeId, out var ctxReadDelegate))
{
if (unknownInstanceReader == null)
{
myRegistrar?.TryRegister(typeId, this);
myRegistrar?.TryRegister(typeof(T), this);
if (!TryGetReader(typeId, out ctxReadDelegate))
{
var realType = myTypeMapping.SingleOrDefault(c => EqualityComparer<RdId>.Default.Equals(c.Value, typeId)); //ok because it's rarely needed
throw new KeyNotFoundException(string.Format("There is no readers found. Requested type '{0}'. Real type {1}", typeof(T).FullName, realType));
}
}
else
{
var objectStart = reader.Position;
var result = unknownInstanceReader(ctx, reader);
var bytesRead = reader.Position - objectStart;
reader.Skip(size - bytesRead);
return result;
}
}
var uncasted = ctxReadDelegate(ctx, reader)!;
if (Mode.IsAssertion) Assertion.Assert(uncasted is T, "Bad cast for id {0}. Expected: {1}, actual: {2}", typeId, typeof(T).Name, uncasted.GetType().Name);
return (T)uncasted;
}
public void Write<T>(SerializationCtx ctx, UnsafeWriter writer, T value)
{
bool TryGetTypeMapping(Type type1, out RdId rdId)
{
lock (myLock)
return myTypeMapping.TryGetValue(type1, out rdId);
}
myBackgroundRegistrar.Join();
if (value == null)
{
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
RdId.Nil.Write(writer);
return;
}
var type = value.GetType();
if (!TryGetTypeMapping(type, out var typeId))
{
myRegistrar?.TryRegister(type, this);
if (!TryGetTypeMapping(type, out typeId))
{
throw new KeyNotFoundException($"Type {type.FullName} have not registered");
}
}
typeId.Write(writer);
var bookmark = new UnsafeWriter.Bookmark(writer);
writer.WriteInt32(0);
CtxWriteDelegate<object> writerDelegate;
lock (myLock)
writerDelegate = myWriters[typeId];
writerDelegate(ctx, writer, value);
bookmark.WriteIntLength();
}
private readonly HashSet<Type> myRegisteredToplevels = new HashSet<Type>();
public void RegisterToplevelOnce(Type toplevelType, Action<ISerializers> registerDeclaredTypesSerializers)
{
new Task(() => RegisterToplevelInternal(toplevelType, registerDeclaredTypesSerializers)).Start(myBackgroundRegistrar);
}
private void RegisterToplevelInternal(Type type, Action<ISerializers> register)
{
if (!myRegisteredToplevels.Add(type)) return;
Protocol.InitTrace?.Log($"REGISTER serializers for {type.Name}");
register(this);
}
}
}