-
Notifications
You must be signed in to change notification settings - Fork 2k
/
BondSerializer.cs
213 lines (183 loc) · 7.88 KB
/
BondSerializer.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
206
207
208
209
210
211
212
213
using Microsoft.Extensions.Logging;
namespace Orleans.Serialization
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using Bond;
using Runtime;
using BondBinaryReader = Bond.Protocols.SimpleBinaryReader<Orleans.Serialization.InputStream>;
using BondBinaryWriter = Bond.Protocols.SimpleBinaryWriter<Orleans.Serialization.OutputStream>;
using BondTypeDeserializer = Bond.Deserializer<Bond.Protocols.SimpleBinaryReader<Orleans.Serialization.InputStream>>;
using BondTypeSerializer = Bond.Serializer<Bond.Protocols.SimpleBinaryWriter<Orleans.Serialization.OutputStream>>;
/// <summary>
/// An implementation of IExternalSerializer for usage with Bond types.
/// </summary>
public class BondSerializer : IExternalSerializer
{
private static ConcurrentDictionary<RuntimeTypeHandle, ClonerInfo> ClonerInfoDictionary;
private static ConcurrentDictionary<RuntimeTypeHandle, BondTypeSerializer> SerializerDictionary;
private static ConcurrentDictionary<RuntimeTypeHandle, BondTypeDeserializer> DeserializerDictionary;
private ILogger logger;
/// <summary>
/// Constructor
/// </summary>
/// <param name="logger"></param>
public BondSerializer(ILogger<BondSerializer> logger)
{
ClonerInfoDictionary = new ConcurrentDictionary<RuntimeTypeHandle, ClonerInfo>();
SerializerDictionary = new ConcurrentDictionary<RuntimeTypeHandle, BondTypeSerializer>();
DeserializerDictionary = new ConcurrentDictionary<RuntimeTypeHandle, BondTypeDeserializer>();
this.logger = logger;
}
/// <summary>
/// Determines whether this serializer has the ability to serialize a particular type.
/// </summary>
/// <param name="itemType">The type of the item to be serialized</param>
/// <returns>A value indicating whether the type can be serialized</returns>
public bool IsSupportedType(Type itemType)
{
if (ClonerInfoDictionary.ContainsKey(itemType.TypeHandle))
{
return true;
}
if (itemType.IsGenericType && itemType.IsConstructedGenericType == false)
{
return false;
}
if (itemType.GetCustomAttribute<SchemaAttribute>() == null)
{
return false;
}
Register(itemType);
return true;
}
/// <inheritdoc />
public object DeepCopy(object source, ICopyContext context)
{
if (source == null)
{
return null;
}
var clonerInfo = GetClonerInfo(source.GetType().TypeHandle);
if (clonerInfo == null)
{
LogWarning(1, "no copier found for type {0}", source.GetType());
throw new ArgumentOutOfRangeException("original", "no copier provided for the selected type");
}
return clonerInfo.Invoke(source);
}
/// <inheritdoc />
public object Deserialize(Type expectedType, IDeserializationContext context)
{
if (expectedType == null)
{
throw new ArgumentNullException(nameof(expectedType));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var typeHandle = expectedType.TypeHandle;
var deserializer = GetDeserializer(typeHandle);
if (deserializer == null)
{
LogWarning(3, "no deserializer found for type {0}", expectedType.FullName);
throw new ArgumentOutOfRangeException("no deserializer provided for the selected type", "expectedType");
}
var inputStream = InputStream.Create(context.StreamReader);
var bondReader = new BondBinaryReader(inputStream);
return deserializer.Deserialize(bondReader);
}
/// <inheritdoc />
public void Serialize(object item, ISerializationContext context, Type expectedType)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var writer = context.StreamWriter;
if (item == null)
{
writer.WriteNull();
return;
}
var typeHandle = item.GetType().TypeHandle;
var serializer = GetSerializer(typeHandle);
if (serializer == null)
{
LogWarning(2, "no serializer found for type {0}", item.GetType());
throw new ArgumentOutOfRangeException("no serializer provided for the selected type", "untypedInput");
}
var outputStream = OutputStream.Create(writer);
var bondWriter = new BondBinaryWriter(outputStream);
serializer.Serialize(item, bondWriter);
}
private static object DeepCopy<T>(T source, object cloner)
{
return ((Cloner<T>)cloner).Clone<T>(source);
}
private static ClonerInfo GetClonerInfo(RuntimeTypeHandle handle)
{
return Get(ClonerInfoDictionary, handle);
}
private static BondTypeSerializer GetSerializer(RuntimeTypeHandle handle)
{
return Get(SerializerDictionary, handle);
}
private static BondTypeDeserializer GetDeserializer(RuntimeTypeHandle handle)
{
return Get(DeserializerDictionary, handle);
}
private static TValue Get<TValue>(IDictionary<RuntimeTypeHandle, TValue> dictionary, RuntimeTypeHandle key)
{
TValue value;
dictionary.TryGetValue(key, out value);
return value;
}
private void LogWarning(int code, string format, params object[] parameters)
{
if(logger.IsEnabled(LogLevel.Warning))
logger.Warn(code, format, parameters);
}
private void Register(Type type)
{
var clonerType = typeof(Cloner<>);
var realType = clonerType.MakeGenericType(type);
var clonerInstance = Activator.CreateInstance(realType);
var serializer = new BondTypeSerializer(type);
var deserializer = new BondTypeDeserializer(type);
var sourceParameter = Expression.Parameter(typeof(object));
var instanceParameter = Expression.Parameter(typeof(object));
var method = typeof(BondSerializer).GetMethod("DeepCopy", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(type);
var lambda = Expression.Lambda(
typeof(Func<object, object, object>),
Expression.Call(
method,
Expression.Convert(sourceParameter, type),
instanceParameter),
sourceParameter,
instanceParameter);
var copierDelegate = (Func<object, object, object>)lambda.Compile();
ClonerInfoDictionary.TryAdd(type.TypeHandle, new ClonerInfo(clonerInstance, copierDelegate));
SerializerDictionary.TryAdd(type.TypeHandle, serializer);
DeserializerDictionary.TryAdd(type.TypeHandle, deserializer);
}
private class ClonerInfo
{
private readonly object instance;
private readonly Func<object, object, object> func;
internal ClonerInfo(object clonerInstance, Func<object, object, object> func)
{
this.instance = clonerInstance;
this.func = func;
}
public object Invoke(object source)
{
return this.func(source, this.instance);
}
}
}
}