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

Commit ea3e64a

Browse files
committed
Loop unrolled direct string inject
1 parent 6fbb9a0 commit ea3e64a

File tree

2 files changed

+86
-80
lines changed

2 files changed

+86
-80
lines changed

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

Lines changed: 84 additions & 78 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 readonly Encoding _utf8 = Encoding.UTF8;
1513

1614
public const string HttpConnectMethod = "CONNECT";
@@ -70,102 +68,110 @@ private unsafe static long GetAsciiStringAsLong(string str)
7068
}
7169
}
7270

73-
private static unsafe string GetAsciiStringStack(byte[] input, int inputOffset, int length)
71+
public unsafe static string GetAsciiString(this MemoryPoolIterator2 start, MemoryPoolIterator2 end)
7472
{
75-
// avoid declaring other local vars, or doing work with stackalloc
76-
// to prevent the .locals init cil flag , see: https://github.com/dotnet/coreclr/issues/1279
77-
char* output = stackalloc char[length];
78-
79-
return GetAsciiStringImplementation(output, input, inputOffset, length);
80-
}
81-
82-
private static unsafe string GetAsciiStringImplementation(char* output, byte[] input, int inputOffset, int length)
83-
{
84-
for (var i = 0; i < length; i++)
73+
if (start.IsDefault || end.IsDefault)
8574
{
86-
output[i] = (char)input[inputOffset + i];
75+
return null;
8776
}
8877

89-
return new string(output, 0, length);
90-
}
91-
92-
private static unsafe string GetAsciiStringStack(MemoryPoolBlock2 start, MemoryPoolIterator2 end, int inputOffset, int length)
93-
{
94-
// avoid declaring other local vars, or doing work with stackalloc
95-
// to prevent the .locals init cil flag , see: https://github.com/dotnet/coreclr/issues/1279
96-
char* output = stackalloc char[length];
97-
98-
return GetAsciiStringImplementation(output, start, end, inputOffset, length);
99-
}
100-
101-
private unsafe static string GetAsciiStringHeap(MemoryPoolBlock2 start, MemoryPoolIterator2 end, int inputOffset, int length)
102-
{
103-
var buffer = new char[length];
78+
var length = start.GetLength(end);
10479

105-
fixed (char* output = buffer)
80+
if (length == 0)
10681
{
107-
return GetAsciiStringImplementation(output, start, end, inputOffset, length);
82+
return null;
10883
}
109-
}
11084

111-
private static unsafe string GetAsciiStringImplementation(char* output, MemoryPoolBlock2 start, MemoryPoolIterator2 end, int inputOffset, int length)
112-
{
113-
var outputOffset = 0;
114-
var block = start;
115-
var remaining = length;
85+
// Bytes out of the range of ascii are treated as "opaque data"
86+
// and kept in string as a char value that casts to same input byte value
87+
// https://tools.ietf.org/html/rfc7230#section-3.2.4
11688

117-
var endBlock = end.Block;
118-
var endIndex = end.Index;
89+
var inputOffset = start.Index;
90+
var block = start.Block;
11991

120-
while (true)
92+
var asciiString = new string('\0', length);
93+
94+
fixed (char* outputStart = asciiString)
12195
{
122-
int following = (block != endBlock ? block.End : endIndex) - inputOffset;
96+
var output = outputStart;
97+
var remaining = length;
12398

124-
if (following > 0)
99+
var endBlock = end.Block;
100+
var endIndex = end.Index;
101+
102+
while (true)
125103
{
126-
var input = block.Array;
127-
for (var i = 0; i < following; i++)
104+
int following = (block != endBlock ? block.End : endIndex) - inputOffset;
105+
106+
if (following > 0)
128107
{
129-
output[i + outputOffset] = (char)input[i + inputOffset];
108+
fixed (byte* blockStart = block.Array)
109+
{
110+
var input = blockStart + inputOffset;
111+
var i = 0;
112+
while (i < following - 11)
113+
{
114+
i += 12;
115+
*(output) = (char)*(input);
116+
*(output + 1) = (char)*(input + 1);
117+
*(output + 2) = (char)*(input + 2);
118+
*(output + 3) = (char)*(input + 3);
119+
*(output + 4) = (char)*(input + 4);
120+
*(output + 5) = (char)*(input + 5);
121+
*(output + 6) = (char)*(input + 6);
122+
*(output + 7) = (char)*(input + 7);
123+
*(output + 8) = (char)*(input + 8);
124+
*(output + 9) = (char)*(input + 9);
125+
*(output + 10) = (char)*(input + 10);
126+
*(output + 11) = (char)*(input + 11);
127+
output += 12;
128+
input += 12;
129+
}
130+
if (i < following - 5)
131+
{
132+
i += 6;
133+
*(output) = (char)*(input);
134+
*(output + 1) = (char)*(input + 1);
135+
*(output + 2) = (char)*(input + 2);
136+
*(output + 3) = (char)*(input + 3);
137+
*(output + 4) = (char)*(input + 4);
138+
*(output + 5) = (char)*(input + 5);
139+
output += 6;
140+
input += 6;
141+
}
142+
if (i < following - 3)
143+
{
144+
i += 4;
145+
*(output) = (char)*(input);
146+
*(output + 1) = (char)*(input + 1);
147+
*(output + 2) = (char)*(input + 2);
148+
*(output + 3) = (char)*(input + 3);
149+
output += 4;
150+
input += 4;
151+
}
152+
while (i < following)
153+
{
154+
i++;
155+
*output = (char)*input;
156+
output++;
157+
input++;
158+
}
159+
160+
remaining -= following;
161+
}
130162
}
131163

132-
remaining -= following;
133-
outputOffset += following;
134-
}
164+
if (remaining == 0)
165+
{
166+
break;
167+
}
135168

136-
if (remaining == 0)
137-
{
138-
return new string(output, 0, length);
169+
block = block.Next;
170+
inputOffset = block.Start;
139171
}
140-
141-
block = block.Next;
142-
inputOffset = block.Start;
143-
}
144-
}
145-
146-
public static string GetAsciiString(this MemoryPoolIterator2 start, MemoryPoolIterator2 end)
147-
{
148-
if (start.IsDefault || end.IsDefault)
149-
{
150-
return default(string);
151-
}
152-
153-
var length = start.GetLength(end);
154-
155-
// Bytes out of the range of ascii are treated as "opaque data"
156-
// and kept in string as a char value that casts to same input byte value
157-
// https://tools.ietf.org/html/rfc7230#section-3.2.4
158-
if (end.Block == start.Block)
159-
{
160-
return GetAsciiStringStack(start.Block.Array, start.Index, length);
161-
}
162-
163-
if (length > _maxStackAllocBytes)
164-
{
165-
return GetAsciiStringHeap(start.Block, end, start.Index, length);
166172
}
167173

168-
return GetAsciiStringStack(start.Block, end, start.Index, length);
174+
return asciiString;
169175
}
170176

171177
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)