-
Notifications
You must be signed in to change notification settings - Fork 0
/
SharedSerializerKnownObject.cs
108 lines (97 loc) · 4.03 KB
/
SharedSerializerKnownObject.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
using CK.Core;
using System;
using System.Collections.Generic;
using System.Text;
namespace CK.BinarySerialization;
/// <summary>
/// Thread safe implementation of unique association between a key string and its object.
/// <see cref="StringComparer.Ordinal"/> and all other static comparer are preregistered.
/// </summary>
public sealed class SharedSerializerKnownObject : ISerializerKnownObject
{
/// <summary>
/// Gets a shared thread safe instance.
/// <para>
/// If dynamic registration is used (during serialization), it's more efficient to first
/// call <see cref="ISerializerKnownObject.GetKnownObjectKey(object)"/> with the object and if null is returned
/// then call <see cref="ISerializerKnownObject.RegisterKnownObject(object, string)"/>.
/// </para>
/// <para>
/// It is recommended to register the known objects once for all in static constructors whenever possible.
/// </para>
/// </summary>
public static readonly SharedSerializerKnownObject Default = new SharedSerializerKnownObject();
// We use a simple array since there should not be a lot of instances.
// Interlocked set is used to add new entries in a thread safe way (don't really care
// of performance and there will be barely no concurrency here) so that
// reads can be done lock free (where performance matters).
(object O, string K)[] _objects;
/// <summary>
/// Initializes a new <see cref="SharedSerializerKnownObject"/> that
/// contains preregistered <see cref="StringComparer"/> singletons (like <see cref="StringComparer.OrdinalIgnoreCase"/>).
/// </summary>
public SharedSerializerKnownObject()
{
_objects = new (object O, string K)[]
{
(DBNull.Value, "DBNull.Value" ),
(Type.Missing, "Type.Missing" ),
(StringComparer.Ordinal, "StringComparer.Ordinal" ),
(StringComparer.OrdinalIgnoreCase, "StringComparer.OrdinalIgnoreCase" ),
(StringComparer.InvariantCulture, "StringComparer.InvariantCulture" ),
(StringComparer.InvariantCultureIgnoreCase, "StringComparer.InvariantCultureIgnoreCase" ),
(StringComparer.CurrentCulture, "StringComparer.CurrentCulture" ),
(StringComparer.CurrentCultureIgnoreCase, "StringComparer.CurrentCultureIgnoreCase" )
};
}
/// <inheritdoc />
public void RegisterKnownObject( object o, string key )
{
Util.InterlockedSet( ref _objects, t => Add( new List<(object, string)>( t ), (o, key) ).ToArray() );
}
/// <inheritdoc />
public void RegisterKnownObject( params (object o, string key)[] association )
{
Util.InterlockedSet( ref _objects, t =>
{
var l = new List<(object, string)>( t );
foreach( var o in association ) Add( l, o );
return l.ToArray();
} );
}
static List<(object O, string K)> Add( List<(object O, string K)> l, (object O, string K) a )
{
foreach( var e in l )
{
if( e.K == a.K )
{
if( e.O == a.O ) return l;
ThrowOnDuplicateKnownKey( e.O, e.K );
}
if( e.O == a.O )
{
if( e.K == a.K ) return l;
ThrowOnDuplicateObject( e.O, e.K, a.K );
}
}
l.Add( a );
return l;
}
static void ThrowOnDuplicateObject( object oExist, string kExist, string kNew )
{
throw new InvalidOperationException( $"Known Object instance (of type '{oExist.GetType()}') cannot be associated to key '{kNew}' since it is already associated to key '{kExist}'." );
}
static void ThrowOnDuplicateKnownKey( object o, string key )
{
throw new InvalidOperationException( $"Known Object key '{key}' is already associated to another instance (of type '{o.GetType()}')." );
}
/// <inheritdoc />
public string? GetKnownObjectKey( object o )
{
foreach( var e in _objects )
{
if( e.O == o ) return e.K;
}
return null;
}
}