1- using System . Runtime . CompilerServices ;
2- using Solver . Core . Base ;
1+ using Microsoft . Extensions . Logging ;
32
43namespace Solver . Core . Serialization ;
54
6- public class SolutionSerializer < TState , TStep > ( IStateSerializer < TState , TStep > stateSerializer )
5+ public class SolutionSerializer < TState , TStep > ( IStateSerializer < TState , TStep > stateSerializer , ILogger < SolutionSerializer < TState , TStep > > ? logger = null )
76{
8- public async Task Serialize ( IEnumerable < Solution < TState , TStep > > solutions , Stream stream , CancellationToken token = default )
7+
8+ public async Task SerializeSolutionAsync ( Solution < TState , TStep > solution , Stream stream , CancellationToken token = default )
99 {
10- var storedSolutions = new HashSet < Guid > ( ) ;
11- foreach ( var solution in solutions )
12- {
13- if ( ! storedSolutions . Add ( solution . Id ) ) continue ;
10+ logger ? . LogDebug ( "Serializing solution {Guid:D}" , solution . Id ) ;
1411
15- var idBuffer = solution . Id . ToByteArray ( ) ;
12+ var idBuffer = solution . Id . ToByteArray ( ) ;
13+ logger ? . LogTrace ( "GUID bytes: {Bytes}" , PrintBytes ( idBuffer ) ) ;
1614
17- var previousStepBuffer = new byte [ 16 + stateSerializer . SerializedStepLength ] ;
18- var stepBuffer = new byte [ stateSerializer . SerializedStepLength ] ;
19- if ( solution . PreviousStep != null )
20- {
21- Array . Copy ( solution . PreviousStep . Value . Solution . Id . ToByteArray ( ) , previousStepBuffer , 16 ) ;
22- stateSerializer . SerializeStep ( solution . PreviousStep . Value . Step , stepBuffer ) ;
23- Array . Copy ( stepBuffer , 0 , previousStepBuffer , 16 , stateSerializer . SerializedStepLength ) ;
24- }
25- else
26- {
27- Array . Fill ( previousStepBuffer , byte . MinValue ) ;
28- }
15+ var solutionLengthBuffer = BitConverter . GetBytes ( solution . Length ) ;
16+ logger ? . LogTrace ( "Solution length bytes: {Bytes}" , PrintBytes ( solutionLengthBuffer ) ) ;
2917
30- var stateBuffer = new byte [ 1024 ] ;
31- var stateLength = stateSerializer . SerializeState ( solution . State , stateBuffer ) ;
18+ var previousStepBuffer = new byte [ 16 + stateSerializer . SerializedStepLength ] ;
19+ if ( solution . Previous != null )
20+ {
21+ var stepBuffer = new byte [ stateSerializer . SerializedStepLength ] ;
22+ Array . Copy ( solution . Previous . Value . Id . ToByteArray ( ) , previousStepBuffer , 16 ) ;
23+ stateSerializer . SerializeStep ( solution . Previous . Value . Step , stepBuffer ) ;
24+ logger ? . LogTrace ( "Step bytes: {Bytes}" , PrintBytes ( stepBuffer ) ) ;
25+ Array . Copy ( stepBuffer , 0 , previousStepBuffer , 16 , stateSerializer . SerializedStepLength ) ;
3226
33- var totalLength = idBuffer . Length + previousStepBuffer . Length + stateBuffer . Length ;
34- await stream . WriteAsync ( BitConverter . GetBytes ( totalLength ) , token ) ;
35- await stream . WriteAsync ( idBuffer , token ) ;
36- await stream . WriteAsync ( previousStepBuffer , token ) ;
37- await stream . WriteAsync ( stateBuffer . AsMemory ( 0 , stateLength ) , token ) ;
38- await stream . FlushAsync ( token ) ;
3927 }
40- }
28+ else
29+ {
30+ Array . Fill ( previousStepBuffer , byte . MinValue ) ;
31+ }
4132
42- public async Task Serialize ( Solver < TState , TStep > solver , Stream stream , CancellationToken token = default )
43- {
44- await Serialize ( solver . GetAllSolutions ( ) , stream , token ) ;
33+ var stateBuffer = new byte [ 1024 ] ;
34+ var stateLength = stateSerializer . SerializeState ( solution . State , stateBuffer ) ;
35+ logger ? . LogTrace ( "State length: {Length}" , stateLength ) ;
36+
37+ var totalLength = idBuffer . Length + previousStepBuffer . Length + solutionLengthBuffer . Length + stateLength ;
38+ var lengthBytes = BitConverter . GetBytes ( totalLength ) ;
39+ logger ? . LogTrace ( "Total length: {Length} ({Bytes})" , totalLength , PrintBytes ( lengthBytes ) ) ;
40+
41+ logger ? . LogDebug ( "Writing bytes to file..." ) ;
42+ await stream . WriteAsync ( lengthBytes , token ) ;
43+ await stream . WriteAsync ( idBuffer , token ) ;
44+ await stream . WriteAsync ( solutionLengthBuffer , token ) ;
45+ await stream . WriteAsync ( previousStepBuffer , token ) ;
46+ await stream . WriteAsync ( stateBuffer . AsMemory ( 0 , stateLength ) , token ) ;
47+ await stream . FlushAsync ( token ) ;
4548 }
4649
47- public async IAsyncEnumerable < ( Guid id , Guid parent , TStep step , TState state ) > Deserialize ( Stream stream , [ EnumeratorCancellation ] CancellationToken token = default )
50+ public async Task < Solution < TState , TStep > > DeserializeSolutionAsync ( Stream stream , CancellationToken token = default )
4851 {
49- while ( stream . CanRead )
52+ void CheckStreamLength ( int length )
5053 {
51- var lengthBuffer = new byte [ 4 ] ;
52- await stream . ReadExactlyAsync ( lengthBuffer , token ) ;
53- var length = BitConverter . ToInt32 ( lengthBuffer ) ;
54- var solutionBuffer = new byte [ length ] ;
55- await stream . ReadExactlyAsync ( solutionBuffer , token ) ;
56- var id = new Guid ( new ArraySegment < byte > ( solutionBuffer , 0 , 16 ) ) ;
57- var parent = new Guid ( new ArraySegment < byte > ( solutionBuffer , 16 , 16 ) ) ;
58- var step = stateSerializer . DeserializeStep ( new ArraySegment < byte > ( solutionBuffer , 32 ,
59- stateSerializer . SerializedStepLength ) ) ;
60- var start = 32 + stateSerializer . SerializedStepLength ;
61- var stateLength = length - start ;
62- var state = stateSerializer . DeserializeState ( new ArraySegment < byte > ( solutionBuffer , start , stateLength ) ) ;
63- yield return ( id , parent , step , state ) ;
54+ if ( stream . Position + length > stream . Length )
55+ {
56+ throw new IOException ( "Input stream too short." ) ;
57+ }
6458 }
59+ logger ? . LogDebug ( "Deserializing solution..." ) ;
60+ logger ? . LogTrace ( "Stream length: {Length}" , stream . Length ) ;
61+ logger ? . LogTrace ( "Stream at byte {Byte} ({Byte:X8})" , stream . Position , stream . Position ) ;
62+ CheckStreamLength ( 4 ) ;
63+ var lengthBuffer = new byte [ 4 ] ;
64+ await stream . ReadExactlyAsync ( lengthBuffer , 0 , 4 , token ) ;
65+ logger ? . LogTrace ( "Length bytes: {Bytes}" , PrintBytes ( lengthBuffer ) ) ;
66+ var length = BitConverter . ToInt32 ( lengthBuffer ) ;
67+ logger ? . LogTrace ( "Read length: {Length} bytes" , length ) ;
68+ logger ? . LogTrace ( "Stream at byte {Byte}" , stream . Position ) ;
69+ CheckStreamLength ( length ) ;
70+ var solutionBuffer = new byte [ length ] ;
71+ await stream . ReadExactlyAsync ( solutionBuffer , 0 , length , token ) ;
72+ var idBytes = new ArraySegment < byte > ( solutionBuffer , 0 , 16 ) ;
73+ var id = new Guid ( idBytes ) ;
74+ logger ? . LogTrace ( "Read ID: {Guid:D} ({Bytes})" , id , PrintBytes ( idBytes ) ) ;
75+ var parentBytes = new ArraySegment < byte > ( solutionBuffer , 16 , 16 ) ;
76+ var parent = new Guid ( parentBytes ) ;
77+ logger ? . LogTrace ( "Parent: {Guid:D} ({Bytes})" , parent , PrintBytes ( parentBytes ) ) ;
78+ var solutionLengthBytes = new ArraySegment < byte > ( solutionBuffer , 32 , 4 ) ;
79+ var solutionLength = BitConverter . ToUInt32 ( solutionLengthBytes ) ;
80+ logger ? . LogTrace ( "Read solution length: {Length}" , solutionLength ) ;
81+ var step = parent == Guid . Empty
82+ ? default
83+ : stateSerializer . DeserializeStep ( new ArraySegment < byte > ( solutionBuffer , 32 ,
84+ stateSerializer . SerializedStepLength ) ) ;
85+ var start = 32 + stateSerializer . SerializedStepLength ;
86+ var stateLength = length - start ;
87+ var state = stateSerializer . DeserializeState ( new ArraySegment < byte > ( solutionBuffer , start ,
88+ stateLength ) ) ;
89+
90+ return new Solution < TState , TStep > ( state )
91+ {
92+ Id = id ,
93+ Length = solutionLength ,
94+ Previous = parent == Guid . Empty ? default : ( parent , step ! )
95+ } ;
6596 }
97+
98+ private static string PrintBytes ( byte [ ] bytes ) => string . Join ( "" , bytes . Select ( b => b . ToString ( "X2" ) ) ) ;
99+ private static string PrintBytes ( ReadOnlySpan < byte > bytes ) => PrintBytes ( bytes . ToArray ( ) ) ;
66100}
0 commit comments