Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit f993941

Browse files
committed
Loop unrolled direct string inject
1 parent 5d6fdc5 commit f993941

File tree

2 files changed

+48
-73
lines changed

2 files changed

+48
-73
lines changed

src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2Extensions.cs

Lines changed: 46 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure
99
{
1010
public static class MemoryPoolIterator2Extensions
1111
{
12-
private const int _maxStackAllocBytes = 16384;
13-
1412
private static Encoding _utf8 = Encoding.UTF8;
1513

1614
public const string HttpConnectMethod = "CONNECT";
@@ -69,77 +67,58 @@ private unsafe static long GetAsciiStringAsLong(string str)
6967
}
7068
}
7169

72-
private static unsafe string GetAsciiStringStack(byte[] input, int inputOffset, int length)
70+
private unsafe static string GetAsciiString(MemoryPoolBlock2 start, MemoryPoolIterator2 end, int inputOffset, int length)
7371
{
74-
// avoid declaring other local vars, or doing work with stackalloc
75-
// to prevent the .locals init cil flag , see: https://github.com/dotnet/coreclr/issues/1279
76-
char* output = stackalloc char[length];
77-
78-
return GetAsciiStringImplementation(output, input, inputOffset, length);
79-
}
72+
var asciiString = new string('\0', length);
8073

81-
private static unsafe string GetAsciiStringImplementation(char* output, byte[] input, int inputOffset, int length)
82-
{
83-
for (var i = 0; i < length; i++)
74+
fixed (char* outputStart = asciiString)
8475
{
85-
output[i] = (char)input[inputOffset + i];
86-
}
87-
88-
return new string(output, 0, length);
89-
}
90-
91-
private static unsafe string GetAsciiStringStack(MemoryPoolBlock2 start, MemoryPoolIterator2 end, int inputOffset, int length)
92-
{
93-
// avoid declaring other local vars, or doing work with stackalloc
94-
// to prevent the .locals init cil flag , see: https://github.com/dotnet/coreclr/issues/1279
95-
char* output = stackalloc char[length];
96-
97-
return GetAsciiStringImplementation(output, start, end, inputOffset, length);
98-
}
99-
100-
private unsafe static string GetAsciiStringHeap(MemoryPoolBlock2 start, MemoryPoolIterator2 end, int inputOffset, int length)
101-
{
102-
var buffer = new char[length];
103-
104-
fixed (char* output = buffer)
105-
{
106-
return GetAsciiStringImplementation(output, start, end, inputOffset, length);
107-
}
108-
}
109-
110-
private static unsafe string GetAsciiStringImplementation(char* output, MemoryPoolBlock2 start, MemoryPoolIterator2 end, int inputOffset, int length)
111-
{
112-
var outputOffset = 0;
113-
var block = start;
114-
var remaining = length;
115-
116-
var endBlock = end.Block;
117-
var endIndex = end.Index;
76+
var output = outputStart;
77+
var block = start;
78+
var remaining = length;
11879

119-
while (true)
120-
{
121-
int following = (block != endBlock ? block.End : endIndex) - inputOffset;
80+
var endBlock = end.Block;
81+
var endIndex = end.Index;
12282

123-
if (following > 0)
83+
while (true)
12484
{
125-
var input = block.Array;
126-
for (var i = 0; i < following; i++)
85+
int following = (block != endBlock ? block.End : endIndex) - inputOffset;
86+
87+
if (following > 0)
12788
{
128-
output[i + outputOffset] = (char)input[i + inputOffset];
89+
fixed (byte* blockStart = block.Array)
90+
{
91+
var input = blockStart + inputOffset;
92+
var i = 0;
93+
var followingMinusSpan = following - 3;
94+
for (; i < followingMinusSpan; i += 4)
95+
{
96+
*(output) = (char)*(input);
97+
*(output + 1) = (char)*(input + 1);
98+
*(output + 2) = (char)*(input + 2);
99+
*(output + 3) = (char)*(input + 3);
100+
output += 4;
101+
input += 4;
102+
}
103+
for (; i < following; i++)
104+
{
105+
*(output++) = (char)*(input++);
106+
}
107+
}
108+
remaining -= following;
129109
}
130110

131-
remaining -= following;
132-
outputOffset += following;
133-
}
111+
if (remaining == 0)
112+
{
113+
break;
114+
}
134115

135-
if (remaining == 0)
136-
{
137-
return new string(output, 0, length);
116+
block = block.Next;
117+
inputOffset = block.Start;
138118
}
139-
140-
block = block.Next;
141-
inputOffset = block.Start;
142119
}
120+
121+
return asciiString;
143122
}
144123

145124
public static string GetAsciiString(this MemoryPoolIterator2 start, MemoryPoolIterator2 end)
@@ -151,20 +130,16 @@ public static string GetAsciiString(this MemoryPoolIterator2 start, MemoryPoolIt
151130

152131
var length = start.GetLength(end);
153132

154-
// Bytes out of the range of ascii are treated as "opaque data"
155-
// and kept in string as a char value that casts to same input byte value
156-
// https://tools.ietf.org/html/rfc7230#section-3.2.4
157-
if (end.Block == start.Block)
133+
if (length == 0)
158134
{
159-
return GetAsciiStringStack(start.Block.Array, start.Index, length);
135+
return default(string);
160136
}
161137

162-
if (length > _maxStackAllocBytes)
163-
{
164-
return GetAsciiStringHeap(start.Block, end, start.Index, length);
165-
}
138+
// Bytes out of the range of ascii are treated as "opaque data"
139+
// and kept in string as a char value that casts to same input byte value
140+
// https://tools.ietf.org/html/rfc7230#section-3.2.4
166141

167-
return GetAsciiStringStack(start.Block, end, start.Index, length);
142+
return GetAsciiString(start.Block, end, start.Index, length);
168143
}
169144

170145
public static string GetUtf8String(this MemoryPoolIterator2 start, MemoryPoolIterator2 end)

test/Microsoft.AspNet.Server.KestrelTests/AsciiDecoder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class AsciiDecoderTests
1313
[Fact]
1414
private void FullByteRangeSupported()
1515
{
16-
var byteRange = Enumerable.Range(0, 255).Select(x => (byte)x).ToArray();
16+
var byteRange = Enumerable.Range(0, 256).Select(x => (byte)x).ToArray();
1717

1818
var mem = MemoryPoolBlock2.Create(new ArraySegment<byte>(byteRange), IntPtr.Zero, null, null);
1919
mem.End = byteRange.Length;
@@ -74,7 +74,7 @@ private void MultiBlockProducesCorrectResults()
7474
}
7575

7676
[Fact]
77-
private void HeapAllocationProducesCorrectResults()
77+
private void LargeAllocationProducesCorrectResults()
7878
{
7979
var byteRange = Enumerable.Range(0, 16384 + 64).Select(x => (byte)x).ToArray();
8080
var expectedByteRange = byteRange.Concat(byteRange).ToArray();

0 commit comments

Comments
 (0)