@@ -11,80 +11,128 @@ public static class MemoryPoolIterator2Extensions
11
11
private const int _maxStackAllocBytes = 16384 ;
12
12
13
13
private static Encoding _utf8 = Encoding . UTF8 ;
14
+ private static uint _startHash ;
14
15
15
- private static unsafe string GetAsciiStringStack ( byte [ ] input , int inputOffset , int length )
16
+ static MemoryPoolIterator2Extensions ( )
17
+ {
18
+ using ( var rnd = System . Security . Cryptography . RandomNumberGenerator . Create ( ) )
19
+ {
20
+ var randomBytes = new byte [ 8 ] ;
21
+ rnd . GetBytes ( randomBytes ) ;
22
+ _startHash =
23
+ ( randomBytes [ 0 ] ) |
24
+ ( ( ( uint ) randomBytes [ 1 ] ) << 8 ) |
25
+ ( ( ( uint ) randomBytes [ 2 ] ) << 16 ) |
26
+ ( ( ( uint ) randomBytes [ 3 ] ) << 24 ) ;
27
+ }
28
+ }
29
+
30
+ private static unsafe string GetAsciiStringStack ( byte * input , int length , StringPool stringPool )
16
31
{
17
32
// avoid declaring other local vars, or doing work with stackalloc
18
33
// to prevent the .locals init cil flag , see: https://github.com/dotnet/coreclr/issues/1279
19
34
char * output = stackalloc char [ length ] ;
20
35
21
- return GetAsciiStringImplementation ( output , input , inputOffset , length ) ;
36
+ return GetAsciiStringImplementation ( output , input , length , stringPool ) ;
22
37
}
23
- private static unsafe string GetAsciiStringImplementation ( char * output , byte [ ] input , int inputOffset , int length )
38
+ private static unsafe string GetAsciiStringImplementation ( char * outputStart , byte * input , int length , StringPool stringPool )
24
39
{
25
- for ( var i = 0 ; i < length ; i ++ )
40
+ var hash = _startHash ;
41
+
42
+ var output = outputStart ;
43
+ var i = 0 ;
44
+ var lengthMinusSpan = length - 3 ;
45
+ for ( ; i < lengthMinusSpan ; i += 4 )
46
+ {
47
+ // span hashing with fix https://github.com/dotnet/corefxlab/pull/455
48
+ hash = hash * 31 + * ( ( uint * ) input ) ;
49
+
50
+ * ( output ) = ( char ) * ( input ) ;
51
+ * ( output + 1 ) = ( char ) * ( input + 1 ) ;
52
+ * ( output + 2 ) = ( char ) * ( input + 2 ) ;
53
+ * ( output + 3 ) = ( char ) * ( input + 3 ) ;
54
+ output += 4 ;
55
+ input += 4 ;
56
+ }
57
+ for ( ; i < length ; i ++ )
26
58
{
27
- output [ i ] = ( char ) input [ inputOffset + i ] ;
59
+ hash = hash * 31 + * ( ( uint * ) input ) ;
60
+ * ( output ++ ) = ( char ) * ( input ++ ) ;
28
61
}
29
62
30
- return new string ( output , 0 , length ) ;
63
+ return stringPool . GetString ( outputStart , hash , length ) ;
31
64
}
32
65
33
- private static unsafe string GetAsciiStringStack ( MemoryPoolBlock2 start , MemoryPoolIterator2 end , int inputOffset , int length )
66
+ private static unsafe string GetAsciiStringStack ( MemoryPoolBlock2 start , MemoryPoolIterator2 end , int inputOffset , int length , StringPool stringPool )
34
67
{
35
68
// avoid declaring other local vars, or doing work with stackalloc
36
69
// to prevent the .locals init cil flag , see: https://github.com/dotnet/coreclr/issues/1279
37
70
char * output = stackalloc char [ length ] ;
38
71
39
- return GetAsciiStringImplementation ( output , start , end , inputOffset , length ) ;
72
+ return GetAsciiStringImplementation ( output , start , end , inputOffset , length , stringPool ) ;
40
73
}
41
74
42
- private unsafe static string GetAsciiStringHeap ( MemoryPoolBlock2 start , MemoryPoolIterator2 end , int inputOffset , int length )
75
+ private unsafe static string GetAsciiStringHeap ( MemoryPoolBlock2 start , MemoryPoolIterator2 end , int inputOffset , int length , StringPool stringPool )
43
76
{
44
77
var buffer = new char [ length ] ;
45
78
46
79
fixed ( char * output = buffer )
47
80
{
48
- return GetAsciiStringImplementation ( output , start , end , inputOffset , length ) ;
81
+ return GetAsciiStringImplementation ( output , start , end , inputOffset , length , stringPool ) ;
49
82
}
50
83
}
51
84
52
- private static unsafe string GetAsciiStringImplementation ( char * output , MemoryPoolBlock2 start , MemoryPoolIterator2 end , int inputOffset , int length )
85
+ private static unsafe string GetAsciiStringImplementation ( char * outputStart , MemoryPoolBlock2 start , MemoryPoolIterator2 end , int inputOffset , int length , StringPool stringPool )
53
86
{
54
- var outputOffset = 0 ;
87
+ var hash = _startHash ;
88
+
89
+ var output = outputStart ;
90
+
55
91
var block = start ;
56
92
var remaining = length ;
57
93
58
94
var endBlock = end . Block ;
59
95
var endIndex = end . Index ;
60
96
61
- while ( true )
97
+ while ( remaining > 0 )
62
98
{
63
99
int following = ( block != endBlock ? block . End : endIndex ) - inputOffset ;
64
100
65
101
if ( following > 0 )
66
102
{
67
- var input = block . Array ;
68
- for ( var i = 0 ; i < following ; i ++ )
103
+ fixed ( byte * blockStart = block . Array )
69
104
{
70
- output [ i + outputOffset ] = ( char ) input [ i + inputOffset ] ;
71
- }
105
+ var input = blockStart + inputOffset ;
106
+ var i = 0 ;
107
+ var followingMinusSpan = following - 3 ;
108
+ for ( ; i < followingMinusSpan ; i += 4 )
109
+ {
110
+ // span hashing with fix https://github.com/dotnet/corefxlab/pull/455
111
+ hash = hash * 31 + * ( ( uint * ) input ) ;
72
112
113
+ * ( output ) = ( char ) * ( input ) ;
114
+ * ( output + 1 ) = ( char ) * ( input + 1 ) ;
115
+ * ( output + 2 ) = ( char ) * ( input + 2 ) ;
116
+ * ( output + 3 ) = ( char ) * ( input + 3 ) ;
117
+ output += 4 ;
118
+ input += 4 ;
119
+ }
120
+ for ( ; i < following ; i++ )
121
+ {
122
+ hash = hash * 31 + * ( ( uint * ) input ) ;
123
+ * ( output ++ ) = ( char ) * ( input ++ ) ;
124
+ }
125
+ }
73
126
remaining -= following ;
74
- outputOffset += following ;
75
- }
76
-
77
- if ( remaining == 0 )
78
- {
79
- return new string ( output , 0 , length ) ;
80
127
}
81
128
82
129
block = block . Next ;
83
- inputOffset = block . Start ;
130
+ inputOffset = block ? . Start ?? 0 ;
84
131
}
132
+ return stringPool. GetString ( outputStart , hash , length ) ;
85
133
}
86
134
87
- public static string GetAsciiString ( this MemoryPoolIterator2 start , MemoryPoolIterator2 end )
135
+ public unsafe static string GetAsciiString( this MemoryPoolIterator2 start, MemoryPoolIterator2 end , StringPool stringPool )
88
136
{
89
137
if ( start . IsDefault || end . IsDefault )
90
138
{
@@ -93,20 +141,28 @@ public static string GetAsciiString(this MemoryPoolIterator2 start, MemoryPoolIt
93
141
94
142
var length = start . GetLength ( end ) ;
95
143
144
+ if ( length <= 0 )
145
+ {
146
+ return default ( string ) ;
147
+ }
148
+
96
149
// Bytes out of the range of ascii are treated as "opaque data"
97
150
// and kept in string as a char value that casts to same input byte value
98
151
// https://tools.ietf.org/html/rfc7230#section-3.2.4
99
152
if ( end . Block = = start . Block )
100
153
{
101
- return GetAsciiStringStack ( start . Block . Array , start . Index , length ) ;
154
+ fixed ( byte * input = start. Block . Array )
155
+ {
156
+ return GetAsciiStringStack( input + start . Index , length , stringPool ) ;
157
+ }
102
158
}
103
159
104
160
if ( length > _maxStackAllocBytes )
105
161
{
106
- return GetAsciiStringHeap ( start . Block , end , start . Index , length ) ;
162
+ return GetAsciiStringHeap( start . Block , end , start . Index , length , stringPool ) ;
107
163
}
108
164
109
- return GetAsciiStringStack ( start . Block , end , start . Index , length ) ;
165
+ return GetAsciiStringStack( start . Block , end , start . Index , length , stringPool ) ;
110
166
}
111
167
112
168
public static string GetUtf8String( this MemoryPoolIterator2 start, MemoryPoolIterator2 end )
@@ -120,9 +176,14 @@ public static string GetUtf8String(this MemoryPoolIterator2 start, MemoryPoolIte
120
176
return _utf8 . GetString ( start . Block . Array , start . Index , end . Index - start . Index ) ;
121
177
}
122
178
123
- var decoder = _utf8 . GetDecoder ( ) ;
124
-
125
179
var length = start. GetLength ( end ) ;
180
+
181
+ if ( length <= 0 )
182
+ {
183
+ return default ( string ) ;
184
+ }
185
+
186
+ var decoder = _utf8 . GetDecoder ( ) ;
126
187
var charLength = length * 2 ;
127
188
var chars = new char [ charLength ] ;
128
189
var charIndex = 0 ;
0 commit comments