-
Notifications
You must be signed in to change notification settings - Fork 0
/
ReferenceTypeDeserializer.cs
134 lines (123 loc) · 5.45 KB
/
ReferenceTypeDeserializer.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
using CK.Core;
using System;
using System.Runtime.CompilerServices;
namespace CK.BinarySerialization;
/// <summary>
/// Deserializer for reference type <typeparamref name="T"/>.
/// This deserializer handles the value to reference type mutation natively.
/// <para>
/// The default constructor sets <see cref="ReferenceTypeDeserializerBase{T}.IsCached"/> to true. This is fine for basic drivers but as soon as
/// the driver depends on others (like generics drivers), the non default constructor should be used.
/// </para>
/// </summary>
/// <typeparam name="T">The type to deserialize.</typeparam>
public abstract class ReferenceTypeDeserializer<T> : ReferenceTypeDeserializerBase<T> where T : class
{
/// <summary>
/// Initializes a new <see cref="ReferenceTypeDeserializer{T}"/> where <see cref="ReferenceTypeDeserializerBase{T}.IsCached"/> is true.
/// <para>
/// Caution: this cached default is easier for basic types but not for composite drivers that relies on other ones (like generic ones).
/// </para>
/// </summary>
protected ReferenceTypeDeserializer()
: base( true )
{
}
/// <summary>
/// Initializes a new <see cref="ReferenceTypeDeserializer{T}"/> that states whether it is cached or not.
/// </summary>
/// <param name="isCached">Whether this deserializer is cached.</param>
protected ReferenceTypeDeserializer( bool isCached )
: base( isCached )
{
}
/// <summary>
/// Secures object tracking by requiring the deserialized object to first be
/// instantiated before enabling the reading of its content.
/// </summary>
public ref struct RefReader
{
/// <summary>
/// Gets the basic reader than can be used any time, typically
/// before calling <see cref="SetInstance(T)"/> to read data required to
/// instantiate the new object to read.
/// </summary>
public readonly ICKBinaryReader Reader;
/// <summary>
/// Gets the type read information.
/// </summary>
public readonly ITypeReadInfo ReadInfo;
/// <summary>
/// Sets the unitialized instance and returns the deserializer to use
/// to read the object's content.
/// <para>
/// This must be called once and only once.
/// </para>
/// </summary>
/// <param name="o">The instantiated but not yet fully initialized object.</param>
/// <returns>The deserializer to use.</returns>
public IBinaryDeserializer SetInstance( T o )
{
Throw.CheckNotNullArgument( o );
Throw.CheckState( "Result already set.", Instance == null );
Instance = o;
return ReadInfo.IsValueType ? _d : _d.Track( o );
}
/// <summary>
/// Sets the instance by allowing to read a header for the object instantiation.
/// <para></para>
/// This must be used if instantiating the object requires some data: these data must appear first
/// (hence the term <paramref name="headerReader"/>) and should have no back reference to any object of
/// the deserialized graph.
/// <para>
/// This must be called once and only once before reading the actual object's content.
/// </para>
/// </summary>
/// <param name="headerReader">
/// Must return the instantiated but not yet fully initialized object. This function is
/// allowed to read some simple data from the deserializer.
/// </param>
/// <returns>The deserializer to use to read the object's content and the instantiated object.</returns>
public (IBinaryDeserializer d, T o) SetInstance( Func<IBinaryDeserializer, T> headerReader )
{
Throw.CheckNotNullArgument( headerReader );
Throw.CheckState( "Result already set.", Instance == null );
if( ReadInfo.IsValueType )
{
Instance = headerReader( _d );
return (_d, Instance);
}
int idx = _d.PreTrack();
Instance = headerReader( _d );
return (_d.PostTrack( idx, Instance ), Instance);
}
internal RefReader( BinaryDeserializerImpl d, ITypeReadInfo i )
{
_d = d;
ReadInfo = i;
Reader = d.Reader;
Instance = null;
}
internal T? Instance;
readonly BinaryDeserializerImpl _d;
}
/// <summary>
/// Adapts the call to <see cref="ReadInstance(ref RefReader)"/>.
/// </summary>
/// <param name="d">The deserializer.</param>
/// <param name="readInfo">The type information.</param>
/// <returns>The instance.</returns>
protected override sealed T ReadInstance( IBinaryDeserializer d, ITypeReadInfo readInfo )
{
var c = new RefReader( Unsafe.As<BinaryDeserializerImpl>( d ), readInfo );
ReadInstance( ref c );
Throw.CheckState( "ReadInstance must set a non null instance.", c.Instance != null );
return c.Instance;
}
/// <summary>
/// Must instantiate a new object, call <see cref="RefReader.SetInstance(T)"/> (or <see cref="RefReader.SetInstance(Func{IBinaryDeserializer, T})"/>
/// for more advanced scenario) and then read its content thanks to the <see cref="IBinaryDeserializer"/> returned by the call to SetInstance method.
/// </summary>
/// <param name="r">Deserialization context to use.</param>
protected abstract void ReadInstance( ref RefReader r );
}