using System; using System.Collections.Generic; using System.IO; using System.Reflection; using Apex.Serialization.Extensions; using Apex.Serialization.Internal; using BinaryReader = Apex.Serialization.Extensions.BinaryReader; using BinaryWriter = Apex.Serialization.Extensions.BinaryWriter; using BufferedStream = Apex.Serialization.Internal.BufferedStream; namespace Apex.Serialization { internal static class WriteMethods { public static Action[] Methods = new Action[ImmutableSettings.MaxSettingsIndex + 1]; } internal static class ReadMethods { public static Func[] Methods = new Func[ImmutableSettings.MaxSettingsIndex + 1]; } public sealed partial class Binary : ISerializer, IDisposable { internal static bool Instantiated; public ImmutableSettings Settings { get; } = Serialization.Settings.Default; private readonly int _settingsIndex; internal readonly BufferedStream _stream; List ISerializer.LoadedObjectRefs => _loadedObjectRefs; private readonly DictionarySlim _savedObjectLookup; private readonly List _loadedObjectRefs; private readonly List _internedObjects = new List(); List ISerializer.LoadedTypeRefs => _loadedTypeRefs; private readonly DictionarySlim _savedTypeLookup = new DictionarySlim(); private readonly List _loadedTypeRefs = new List(); private readonly DictionarySlim> VirtualWriteMethods = new DictionarySlim>(); private readonly DictionarySlim> VirtualReadMethods = new DictionarySlim>(); private Type _lastWriteType; private Action _lastWriteMethod; private Type _lastReadType; private Func _lastReadMethod; private readonly TypeLookup _knownTypes = new TypeLookup(); private readonly List,object>> _deserializationHooks; private DictionarySlim _methodParametersCache = new DictionarySlim(); private DictionarySlim _methodGenericsCache = new DictionarySlim(); private readonly DictionarySlim _delegateCache = new DictionarySlim(); private readonly Func _clone = CreateCloneFunc(); private readonly Action _setTarget = CreateSetTargetAction(); private Type[] _parameterTypeBuffer = new Type[256]; private Type[][] _genericTypeBuffers = new Type[][] { new Type[1], new Type[2], new Type[3], new Type[4] }; private readonly IBinaryWriter _binaryWriter; private readonly IBinaryReader _binaryReader; public Binary() { Instantiated = true; _binaryWriter = new BinaryWriter(this); _binaryReader = new BinaryReader(this); _settingsIndex = Settings.SettingsIndex; _stream = new BufferedStream(); if (Settings.SerializationMode == Mode.Graph) { _savedObjectLookup = new DictionarySlim(16); _loadedObjectRefs = new List(16); } if (Settings.SupportSerializationHooks) { _deserializationHooks = new List, object>>(); } } public Binary(Settings settings) { Instantiated = true; _binaryWriter = new BinaryWriter(this); _binaryReader = new BinaryReader(this); Settings = settings; _settingsIndex = Settings.SettingsIndex; _stream = new BufferedStream(); if (Settings.SerializationMode == Mode.Graph) { _savedObjectLookup = new DictionarySlim(16); _loadedObjectRefs = new List(16); } if (Settings.SupportSerializationHooks) { _deserializationHooks = new List, object>>(); } } public void Write(T value, Stream outputStream) { _stream.WriteTo(outputStream); WriteObjectEntry(value); _stream.Flush(); if (Settings.SerializationMode == Mode.Graph) { _savedObjectLookup.Clear(); if (_internedObjects.Count > 0) { foreach (var o in _internedObjects) { _savedObjectLookup.GetOrAddValueRef(o) = _savedObjectLookup.Count; } } } _savedTypeLookup.Clear(); } public T Read(Stream inputStream) { _stream.ReadFrom(inputStream); var result = ReadObjectEntry(); if (Settings.SerializationMode == Mode.Graph) { _loadedObjectRefs.Clear(); if (_internedObjects.Count > 0) { _loadedObjectRefs.AddRange(_internedObjects); } } _loadedTypeRefs.Clear(); if (Settings.SupportSerializationHooks) { foreach (var a in _deserializationHooks) { a.Item1(a.Item2); } _deserializationHooks.Clear(); } return result; } public void Precompile(Type type) { ref var readMethod = ref VirtualReadMethods.GetOrAddValueRef(type); if (readMethod == null) { readMethod = (Func)DynamicCode.GenerateReadMethod(type, Settings, true); } ref var writeMethod = ref VirtualWriteMethods.GetOrAddValueRef(type); if (writeMethod == null) { writeMethod = (Action)DynamicCode.GenerateWriteMethod(type, Settings, true); } } public void Precompile() { var readMethod = ReadMethods.Methods[_settingsIndex]; if (readMethod == null) { readMethod = (Func)DynamicCode.GenerateReadMethod(typeof(T), Settings, false); ReadMethods.Methods[_settingsIndex] = readMethod; } var writeMethod = WriteMethods.Methods[_settingsIndex]; if (writeMethod == null) { writeMethod = (Action)DynamicCode.GenerateWriteMethod(typeof(T), Settings, false); WriteMethods.Methods[_settingsIndex] = writeMethod; } } public void Intern(object o) { if(Settings.SerializationMode != Mode.Graph) { throw new InvalidOperationException("Object interning is only supported for Graph serialization"); } _internedObjects.Add(o); _savedObjectLookup.GetOrAddValueRef(o) = _savedObjectLookup.Count; _loadedObjectRefs.Add(o); } public void Dispose() { _stream.Dispose(); } } }