-
Notifications
You must be signed in to change notification settings - Fork 0
/
SharedBinaryDeserializerContext.cs
205 lines (188 loc) · 9.87 KB
/
SharedBinaryDeserializerContext.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
using CK.Core;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace CK.BinarySerialization;
/// <summary>
/// Thread safe composite implementation for <see cref="IDeserializerResolver"/>.
/// A singleton instance is exposed by <see cref="BinaryDeserializer.DefaultSharedContext"/>.
/// <para>
/// Caching deserialization drivers is not as easy as caching serializers: two serialized types
/// can perfectly be deserialized into the same local type. The actual cache is the <see cref="ITypeReadInfo"/>
/// itself that is resolved once per deserialization session.
/// </para>
/// </summary>
public sealed class SharedBinaryDeserializerContext : IDeserializerResolver
{
IDeserializerResolver[] _resolvers;
readonly IDeserializerKnownObject _knownObjects;
readonly ConcurrentDictionary<Type, IDeserializationDriver> _localTypedDrivers;
Action<IMutableTypeReadInfo>[] _hooks;
/// <summary>
/// See <see cref="SharedBinarySerializerContext._rSliced"/> for rationales.
/// </summary>
static readonly IDeserializerResolver? _rSliced = (IDeserializerResolver?)SharedBinarySerializerContext.GetInstance( "CK.BinarySerialization.SlicedDeserializerResolver, CK.BinarySerialization.Sliced" );
static readonly IDeserializerResolver? _rPoco = (IDeserializerResolver?)SharedBinarySerializerContext.GetInstance( "CK.BinarySerialization.PocoDeserializerResolver, CK.BinarySerialization.IPoco" );
/// <summary>
/// Abstract drivers are statically cached once for all.
/// Note that the local type here may itself be sealed: it's the way it has been written that matters.
/// </summary>
static readonly ConcurrentDictionary<Type, IDeserializationDriver> _abstractDrivers = new();
/// <summary>
/// Public cache for drivers that depend only on the local type to deserialize.
/// This is the case for <see cref="ICKSimpleBinarySerializable"/>, <see cref="ICKVersionedBinarySerializable"/>
/// and may be the case for others like the "Sliced" serialization.
/// Since deserialization context is irrelevant for these drivers, this dictionary is exposed to avoid
/// creating too much concurrent dictionaries.
/// </summary>
static public readonly ConcurrentDictionary<Type, IDeserializationDriver> PureLocalTypeDependentDrivers = new();
/// <summary>
/// Initializes a new registry bound to a possibly independent <see cref="SharedDeserializerKnownObject"/> and
/// the <see cref="BasicTypesDeserializerResolver.Instance"/>, <see cref="SimpleBinaryDeserializerResolver.Instance"/>
/// and <see cref="StandardGenericDeserializerResolver"/>.
/// <para>
/// Caution: if <see cref="SharedDeserializerKnownObject.Default"/> is not used, default comparers for dictionary keys will NOT be automatically
/// registered in the <see cref="KnownObjects"/> (they are only automatically registered in <see cref="SharedDeserializerKnownObject.Default"/>).
/// </para>
/// </summary>
/// <param name="useDefaultResolvers">True to register the default resolvers.</param>
/// <param name="knownObjects">By default the <see cref="SharedDeserializerKnownObject.Default"/> is used.</param>
public SharedBinaryDeserializerContext( bool useDefaultResolvers = true, SharedDeserializerKnownObject? knownObjects = null )
{
_knownObjects = knownObjects ?? SharedDeserializerKnownObject.Default;
_localTypedDrivers = new ConcurrentDictionary<Type, IDeserializationDriver>();
_hooks = Array.Empty<Action<IMutableTypeReadInfo>>();
if( useDefaultResolvers )
{
_resolvers = _rSliced != null
? (
_rPoco != null
? new IDeserializerResolver[] { BasicTypesDeserializerResolver.Instance,
SimpleBinaryDeserializerResolver.Instance,
new StandardGenericDeserializerResolver( this ),
_rSliced,
_rPoco
}
: new IDeserializerResolver[] { BasicTypesDeserializerResolver.Instance,
SimpleBinaryDeserializerResolver.Instance,
new StandardGenericDeserializerResolver( this ),
_rSliced
}
)
: (
_rPoco != null
? new IDeserializerResolver[] { BasicTypesDeserializerResolver.Instance,
SimpleBinaryDeserializerResolver.Instance,
new StandardGenericDeserializerResolver( this ),
_rPoco
}
: new IDeserializerResolver[] { BasicTypesDeserializerResolver.Instance,
SimpleBinaryDeserializerResolver.Instance,
new StandardGenericDeserializerResolver( this )
}
);
}
else
{
_resolvers = Array.Empty<IDeserializerResolver>();
}
}
internal static IDeserializationDriver GetAbstractDriver( Type t )
{
return _abstractDrivers.GetOrAdd( t, Create );
static IDeserializationDriver Create( Type t )
{
var tD = typeof( Deserialization.DAbstract<> ).MakeGenericType( t );
return (IDeserializationDriver)Activator.CreateInstance( tD )!;
}
}
/// <summary>
/// Gets the known objects registry.
/// </summary>
public IDeserializerKnownObject KnownObjects => _knownObjects;
internal void CallHooks( IMutableTypeReadInfo m )
{
foreach( var h in _hooks )
{
h( m );
}
}
/// <inheritdoc />
public IDeserializationDriver? TryFindDriver( ref DeserializerResolverArg info )
{
// The added local type drivers have the priority.
if( _localTypedDrivers.TryGetValue( info.ExpectedType, out var d ) )
{
return d;
}
// Do not cache TargetType in _localTypedDrivers here: the same type may be
// built by more than one driver.
// We must not lookup the PureLocalTypeDependentDrivers here: some drivers may be resolved
// based on different informations from the TypeReadInfo (typically the DriverName) that
// handle a Type that has such an associated PureLocalTypeDependentDriver.
// It's up to the resolver to decide.
foreach( var resolver in _resolvers )
{
var r = resolver.TryFindDriver( ref info );
if( r != null ) return r;
}
return null;
}
/// <summary>
/// Ensures that a resolver is registered.
/// When new, the resolver can be appended after or inserted before the existing ones.
/// <para>
/// Deserialization drivers are not cached by the shared context (as opposed to the serialization
/// drivers). However, deserialization registrations should be done before any deserialization occur.
/// </para>
/// </summary>
/// <param name="resolver">The resolver that must be found or added.</param>
/// <param name="beforeExisting">Whether to register the resolver before the existing ones.</param>
public void AddResolver( IDeserializerResolver resolver, bool beforeExisting = false )
{
Util.InterlockedAddUnique( ref _resolvers, resolver, beforeExisting );
}
/// <summary>
/// Registers an explicit deserialization driver for a type.
/// <para>
/// This <see cref="IDeserializationDriver.ResolvedType"/> MUST not already be mapped otherwise
/// an <see cref="InvalidOperationException"/> is raised.
/// </para>
/// <para>
/// These explicitly registered drivers take precedence over all other resolvers.
/// </para>
/// </summary>
/// <param name="driver">The driver to register.</param>
public void AddDeserializerDriver( IDeserializationDriver driver )
{
var n = driver.ToNullable.ResolvedType;
bool done = false;
if( _localTypedDrivers.TryAdd( n, driver.ToNullable ) )
{
done = true;
var nn = driver.ToNonNullable.ResolvedType;
if( nn != n )
{
done = _localTypedDrivers.TryAdd( nn, driver.ToNonNullable );
n = nn;
}
}
if( !done ) Throw.InvalidOperationException( $"A deserialization driver for type '{n}' is already registered." );
}
/// <summary>
/// Registers a deserialization hook that will called each time a <see cref="ITypeReadInfo"/> is read
/// and a deserialization driver must be resolved. See <see cref="IMutableTypeReadInfo"/>.
/// <para>
/// This hook enables setting the local type to deserialize or the driver name or the <see cref="IDeserializationDriver"/>
/// to use instead of calling <see cref="IDeserializerResolver.TryFindDriver(ref DeserializerResolverArg)"/>.
/// </para>
/// </summary>
/// <param name="hook">The hook to register.</param>
public void AddDeserializationHook( Action<IMutableTypeReadInfo> hook )
{
Throw.CheckNotNullArgument( hook );
Util.InterlockedAdd( ref _hooks, hook );
}
}