4
4
using System ;
5
5
using System . Buffers ;
6
6
using System . Collections . Generic ;
7
+ using System . IO . Pipelines ;
7
8
using System . Text . Json ;
8
9
using System . Threading . Tasks ;
9
10
using Microsoft . AspNetCore . Components . Lifetime ;
@@ -24,7 +25,12 @@ public PrerenderComponentApplicationStore(string existingState)
24
25
throw new ArgumentNullException ( nameof ( existingState ) ) ;
25
26
}
26
27
27
- var state = JsonSerializer . Deserialize < Dictionary < string , byte [ ] > > ( Convert . FromBase64String ( existingState ) ) ;
28
+ DeserializeState ( Convert . FromBase64String ( existingState ) ) ;
29
+ }
30
+
31
+ protected void DeserializeState ( byte [ ] existingState )
32
+ {
33
+ var state = JsonSerializer . Deserialize < Dictionary < string , byte [ ] > > ( existingState ) ;
28
34
if ( state == null )
29
35
{
30
36
throw new ArgumentException ( "Could not deserialize state correctly" , nameof ( existingState ) ) ;
@@ -50,48 +56,74 @@ public Task<IDictionary<string, ReadOnlySequence<byte>>> GetPersistedStateAsync(
50
56
return Task . FromResult ( ( IDictionary < string , ReadOnlySequence < byte > > ) ExistingState ) ;
51
57
}
52
58
53
- protected virtual byte [ ] SerializeState ( IReadOnlyDictionary < string , ReadOnlySequence < byte > > state )
59
+ protected virtual async Task < byte [ ] > SerializeState ( IReadOnlyDictionary < string , ReadOnlySequence < byte > > state )
54
60
{
55
61
// System.Text.Json doesn't support serializing ReadonlySequence<byte> so we need to buffer
56
62
// the data with a memory pool here. We will change our serialization strategy in the future here
57
63
// so that we can avoid this step.
58
- var pool = MemoryPool < byte > . Shared ;
59
- var memory = new List < IMemoryOwner < byte > > ( ) ;
60
- var serialization = new Dictionary < string , Memory < byte > > ( ) ;
61
- try
64
+ var pipe = new Pipe ( ) ;
65
+ var pipeWriter = pipe . Writer ;
66
+ var jsonWriter = new Utf8JsonWriter ( pipeWriter ) ;
67
+ jsonWriter . WriteStartObject ( ) ;
68
+ foreach ( var ( key , value ) in state )
69
+ {
70
+ if ( value . IsSingleSegment )
71
+ {
72
+ jsonWriter . WriteBase64String ( key , value . First . Span ) ;
73
+ }
74
+ else
75
+ {
76
+ WriteMultipleSegments ( jsonWriter , key , value ) ;
77
+ }
78
+ jsonWriter . Flush ( ) ;
79
+ }
80
+
81
+ jsonWriter . WriteEndObject ( ) ;
82
+ jsonWriter . Flush ( ) ;
83
+ pipe . Writer . Complete ( ) ;
84
+ var result = await ReadToEnd ( pipe . Reader ) ;
85
+ return result . ToArray ( ) ;
86
+
87
+ async Task < ReadOnlySequence < byte > > ReadToEnd ( PipeReader reader )
62
88
{
63
- foreach ( var ( key , value ) in state )
89
+ var result = await reader . ReadAsync ( ) ;
90
+ reader . AdvanceTo ( result . Buffer . Start , result . Buffer . End ) ;
91
+ while ( ! result . IsCompleted )
64
92
{
65
- IMemoryOwner < byte > buffer = null ;
66
- if ( value . Length < pool . MaxBufferSize )
67
- {
68
- buffer = pool . Rent ( ( int ) value . Length ) ;
69
- memory . Add ( buffer ) ;
70
- value . CopyTo ( buffer . Memory . Span . Slice ( 0 , ( int ) value . Length ) ) ;
71
- }
72
-
73
- serialization . Add ( key , buffer != null ? buffer . Memory . Slice ( 0 , ( int ) value . Length ) : value . ToArray ( ) ) ;
93
+ // Consume nothing, just wait for everything
94
+ result = await reader . ReadAsync ( ) ;
95
+ reader . AdvanceTo ( result . Buffer . Start , result . Buffer . End ) ;
74
96
}
75
97
76
- return JsonSerializer . SerializeToUtf8Bytes ( serialization , JsonSerializerOptionsProvider . Options ) ;
98
+ return result . Buffer ;
77
99
}
78
- finally
100
+
101
+ static void WriteMultipleSegments ( Utf8JsonWriter jsonWriter , string key , ReadOnlySequence < byte > value )
79
102
{
80
- serialization . Clear ( ) ;
81
- foreach ( var item in memory )
103
+ byte [ ] unescapedArray = null ;
104
+ var valueLenght = ( int ) value . Length ;
105
+
106
+ Span < byte > valueSegment = value . Length <= 256 ?
107
+ stackalloc byte [ valueLenght ] :
108
+ ( unescapedArray = ArrayPool < byte > . Shared . Rent ( valueLenght ) ) . AsSpan ( ) . Slice ( 0 , valueLenght ) ;
109
+
110
+ value . CopyTo ( valueSegment ) ;
111
+ jsonWriter . WriteBase64String ( key , valueSegment ) ;
112
+
113
+ if ( unescapedArray != null )
82
114
{
83
- item . Dispose ( ) ;
115
+ valueSegment . Clear ( ) ;
116
+ ArrayPool < byte > . Shared . Return ( unescapedArray ) ;
84
117
}
85
118
}
86
119
}
87
120
88
- public Task PersistStateAsync ( IReadOnlyDictionary < string , ReadOnlySequence < byte > > state )
121
+ public async Task PersistStateAsync ( IReadOnlyDictionary < string , ReadOnlySequence < byte > > state )
89
122
{
90
- var bytes = SerializeState ( state ) ;
123
+ var bytes = await SerializeState ( state ) ;
91
124
92
125
var result = Convert . ToBase64String ( bytes ) ;
93
126
PersistedState = result ;
94
- return Task . CompletedTask ;
95
127
}
96
128
}
97
129
}
0 commit comments