Skip to content

Commit 9d9a3ea

Browse files
committed
Optimization: combine checking for final & non-final boundaries
This could reduce "string" comparisons by half when checking which boundary we just encountered. In practice, this changes doesn't seem to have a noticable impact, but it's worth doing anyway.
1 parent d959a06 commit 9d9a3ea

File tree

4 files changed

+84
-48
lines changed

4 files changed

+84
-48
lines changed

MimeKit/AsyncMimeParser.cs

+8-6
Original file line numberDiff line numberDiff line change
@@ -502,12 +502,14 @@ async Task ConstructMultipartAsync (Multipart multipart, MimeEntityEndEventArgs
502502
// We either found the end of the stream or we found a parent's boundary
503503
PopBoundary ();
504504

505-
unsafe {
506-
fixed (byte* inbuf = input) {
507-
if (boundary == BoundaryType.ParentEndBoundary && FoundImmediateBoundary (inbuf, true))
508-
boundary = BoundaryType.ImmediateEndBoundary;
509-
else if (boundary == BoundaryType.ParentBoundary && FoundImmediateBoundary (inbuf, false))
510-
boundary = BoundaryType.ImmediateBoundary;
505+
// If the last boundary we found (before popping one off the stack) was a parent's boundary, we need to check
506+
// to see if that boundary is now an immediate boundary and update our state.
507+
if (boundary == BoundaryType.ParentEndBoundary || boundary == BoundaryType.ParentBoundary) {
508+
unsafe {
509+
fixed (byte* inbuf = input) {
510+
if (FoundImmediateBoundary (inbuf, out var final))
511+
boundary = final ? BoundaryType.ImmediateEndBoundary : BoundaryType.ImmediateBoundary;
512+
}
511513
}
512514
}
513515
}

MimeKit/AsyncMimeReader.cs

+8-6
Original file line numberDiff line numberDiff line change
@@ -626,12 +626,14 @@ async Task<int> ConstructMultipartAsync (ContentType contentType, int depth, Can
626626
// We either found the end of the stream or we found a parent's boundary
627627
PopBoundary ();
628628

629-
unsafe {
630-
fixed (byte* inbuf = input) {
631-
if (boundary == BoundaryType.ParentEndBoundary && FoundImmediateBoundary (inbuf, true))
632-
boundary = BoundaryType.ImmediateEndBoundary;
633-
else if (boundary == BoundaryType.ParentBoundary && FoundImmediateBoundary (inbuf, false))
634-
boundary = BoundaryType.ImmediateBoundary;
629+
// If the last boundary we found (before popping one off the stack) was a parent's boundary, we need to check
630+
// to see if that boundary is now an immediate boundary and update our state.
631+
if (boundary == BoundaryType.ParentEndBoundary || boundary == BoundaryType.ParentBoundary) {
632+
unsafe {
633+
fixed (byte* inbuf = input) {
634+
if (FoundImmediateBoundary (inbuf, out var final))
635+
boundary = final ? BoundaryType.ImmediateEndBoundary : BoundaryType.ImmediateBoundary;
636+
}
635637
}
636638
}
637639

MimeKit/MimeParser.cs

+33-18
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class Boundary
5555
public int FinalLength { get { return Marker.Length; } }
5656
public int Length { get; private set; }
5757
public int MaxLength { get; private set; }
58+
public bool IsMboxMarker { get { return Marker == MboxFrom; } }
5859

5960
public Boundary (string boundary, int currentMaxLength)
6061
{
@@ -1222,24 +1223,33 @@ unsafe bool IsPossibleBoundary (byte* text, int length)
12221223
return false;
12231224
}
12241225

1225-
static unsafe bool IsBoundary (byte* text, int length, byte[] boundary, int boundaryLength)
1226+
static unsafe bool IsBoundary (byte* text, int length, Boundary boundary, out bool final)
12261227
{
1227-
if (boundaryLength > length)
1228+
final = false;
1229+
1230+
if (boundary.Length > length)
12281231
return false;
12291232

1230-
fixed (byte* boundaryptr = boundary) {
1233+
fixed (byte* boundaryptr = boundary.Marker) {
12311234
// make sure that the text matches the boundary
1232-
if (!CStringsEqual (text, boundaryptr, boundaryLength))
1235+
if (!CStringsEqual (text, boundaryptr, boundary.Length))
12331236
return false;
12341237

12351238
// if this is an mbox marker, we're done
1236-
if (IsMboxMarker (text))
1239+
if (boundary.IsMboxMarker) {
1240+
final = true;
12371241
return true;
1242+
}
12381243

1239-
// the boundary may optionally be followed by lwsp
1240-
byte* inptr = text + boundaryLength;
1244+
byte* inptr = text + boundary.Length;
12411245
byte* inend = text + length;
12421246

1247+
if (length >= boundary.FinalLength && inptr[0] == (byte) '-' && inptr[1] == (byte) '-') {
1248+
final = true;
1249+
inptr += 2;
1250+
}
1251+
1252+
// the boundary may optionally be followed by lwsp
12431253
while (inptr < inend) {
12441254
if (!(*inptr).IsWhitespace ())
12451255
return false;
@@ -1266,28 +1276,30 @@ unsafe BoundaryType CheckBoundary (int startIndex, byte* start, int length)
12661276
for (int i = 0; i < count; i++) {
12671277
var boundary = bounds[i];
12681278

1269-
if (IsBoundary (start, length, boundary.Marker, boundary.FinalLength))
1270-
return i == 0 ? BoundaryType.ImmediateEndBoundary : BoundaryType.ParentEndBoundary;
1279+
if (IsBoundary (start, length, boundary, out var final)) {
1280+
if (final)
1281+
return i == 0 ? BoundaryType.ImmediateEndBoundary : BoundaryType.ParentEndBoundary;
12711282

1272-
if (IsBoundary (start, length, boundary.Marker, boundary.Length))
12731283
return i == 0 ? BoundaryType.ImmediateBoundary : BoundaryType.ParentBoundary;
1284+
}
12741285
}
12751286

12761287
if (contentEnd > 0) {
12771288
// now it is time to check the mbox From-marker for the Content-Length case
12781289
long curOffset = GetOffset (startIndex);
12791290
var boundary = bounds[count];
12801291

1281-
if (curOffset >= contentEnd && IsBoundary (start, length, boundary.Marker, boundary.Length))
1292+
if (curOffset >= contentEnd && IsBoundary (start, length, boundary, out _))
12821293
return BoundaryType.ImmediateEndBoundary;
12831294
}
12841295

12851296
return BoundaryType.None;
12861297
}
12871298

1288-
unsafe bool FoundImmediateBoundary (byte* inbuf, bool final)
1299+
unsafe bool FoundImmediateBoundary (byte* inbuf, out bool final)
12891300
{
1290-
int boundaryLength = final ? bounds[0].FinalLength : bounds[0].Length;
1301+
// TODO: If the MimeReader recorded which boundary marker it found, we wouldn't need to re-scan the input buffer for eoln,
1302+
// we could just check if boundary[0] == MimeReader.lastFoundBoundary (or whatever we call it).
12911303
byte* start = inbuf + inputIndex;
12921304
byte* inend = inbuf + inputEnd;
12931305
byte* inptr = start;
@@ -1297,9 +1309,10 @@ unsafe bool FoundImmediateBoundary (byte* inbuf, bool final)
12971309
while (*inptr != (byte) '\n')
12981310
inptr++;
12991311

1300-
return IsBoundary (start, (int) (inptr - start), bounds[0].Marker, boundaryLength);
1312+
return IsBoundary (start, (int) (inptr - start), bounds[0], out final);
13011313
}
13021314

1315+
[MethodImpl (MethodImplOptions.AggressiveInlining)]
13031316
int GetMaxBoundaryLength ()
13041317
{
13051318
return bounds.Count > 0 ? bounds[0].MaxLength + 2 : 0;
@@ -1716,10 +1729,12 @@ unsafe void ConstructMultipart (Multipart multipart, MimeEntityEndEventArgs args
17161729
// We either found the end of the stream or we found a parent's boundary
17171730
PopBoundary ();
17181731

1719-
if (boundary == BoundaryType.ParentEndBoundary && FoundImmediateBoundary (inbuf, true))
1720-
boundary = BoundaryType.ImmediateEndBoundary;
1721-
else if (boundary == BoundaryType.ParentBoundary && FoundImmediateBoundary (inbuf, false))
1722-
boundary = BoundaryType.ImmediateBoundary;
1732+
// If the last boundary we found (before popping one off the stack) was a parent's boundary, we need to check
1733+
// to see if that boundary is now an immediate boundary and update our state.
1734+
if (boundary == BoundaryType.ParentEndBoundary || boundary == BoundaryType.ParentBoundary) {
1735+
if (FoundImmediateBoundary (inbuf, out var final))
1736+
boundary = final ? BoundaryType.ImmediateEndBoundary : BoundaryType.ImmediateBoundary;
1737+
}
17231738
}
17241739

17251740
/// <summary>

MimeKit/MimeReader.cs

+35-18
Original file line numberDiff line numberDiff line change
@@ -1839,24 +1839,33 @@ unsafe bool IsPossibleBoundary (byte* text, int length)
18391839
return false;
18401840
}
18411841

1842-
static unsafe bool IsBoundary (byte* text, int length, byte[] boundary, int boundaryLength)
1842+
static unsafe bool IsBoundary (byte* text, int length, Boundary boundary, out bool final)
18431843
{
1844-
if (boundaryLength > length)
1844+
final = false;
1845+
1846+
if (boundary.Length > length)
18451847
return false;
18461848

1847-
fixed (byte* boundaryptr = boundary) {
1849+
fixed (byte* boundaryptr = boundary.Marker) {
18481850
// make sure that the text matches the boundary
1849-
if (!CStringsEqual (text, boundaryptr, boundaryLength))
1851+
if (!CStringsEqual (text, boundaryptr, boundary.Length))
18501852
return false;
18511853

18521854
// if this is an mbox marker, we're done
1853-
if (IsMboxMarker (text))
1855+
if (boundary.IsMboxMarker) {
1856+
final = true;
18541857
return true;
1858+
}
18551859

1856-
// the boundary may optionally be followed by lwsp
1857-
byte* inptr = text + boundaryLength;
1860+
byte* inptr = text + boundary.Length;
18581861
byte* inend = text + length;
18591862

1863+
if (length >= boundary.FinalLength && inptr[0] == (byte) '-' && inptr[1] == (byte) '-') {
1864+
final = true;
1865+
inptr += 2;
1866+
}
1867+
1868+
// the boundary may optionally be followed by lwsp
18601869
while (inptr < inend) {
18611870
if (!(*inptr).IsWhitespace ())
18621871
return false;
@@ -1883,28 +1892,33 @@ unsafe BoundaryType CheckBoundary (int startIndex, byte* start, int length)
18831892
for (int i = 0; i < count; i++) {
18841893
var boundary = bounds[i];
18851894

1886-
if (IsBoundary (start, length, boundary.Marker, boundary.FinalLength))
1887-
return i == 0 ? BoundaryType.ImmediateEndBoundary : BoundaryType.ParentEndBoundary;
1895+
if (IsBoundary (start, length, boundary, out var final)) {
1896+
if (final)
1897+
return i == 0 ? BoundaryType.ImmediateEndBoundary : BoundaryType.ParentEndBoundary;
18881898

1889-
if (IsBoundary (start, length, boundary.Marker, boundary.Length))
18901899
return i == 0 ? BoundaryType.ImmediateBoundary : BoundaryType.ParentBoundary;
1900+
}
18911901
}
18921902

18931903
if (contentEnd > 0) {
18941904
// now it is time to check the mbox From-marker for the Content-Length case
18951905
long curOffset = GetOffset (startIndex);
18961906
var boundary = bounds[count];
18971907

1898-
if (curOffset >= contentEnd && IsBoundary (start, length, boundary.Marker, boundary.Length))
1908+
if (curOffset >= contentEnd && IsBoundary (start, length, boundary, out _))
18991909
return BoundaryType.ImmediateEndBoundary;
19001910
}
19011911

19021912
return BoundaryType.None;
19031913
}
19041914

1905-
unsafe bool FoundImmediateBoundary (byte* inbuf, bool final)
1915+
#if NET5_0_OR_GREATER
1916+
[SkipLocalsInit]
1917+
#endif
1918+
unsafe bool FoundImmediateBoundary (byte* inbuf, out bool final)
19061919
{
1907-
int boundaryLength = final ? bounds[0].FinalLength : bounds[0].Length;
1920+
// TODO: If the MimeReader recorded which boundary marker it found, we wouldn't need to re-scan the input buffer for eoln,
1921+
// we could just check if boundary[0] == MimeReader.lastFoundBoundary (or whatever we call it).
19081922
byte* start = inbuf + inputIndex;
19091923
byte* inend = inbuf + inputEnd;
19101924
byte* inptr = start;
@@ -1915,9 +1929,10 @@ unsafe bool FoundImmediateBoundary (byte* inbuf, bool final)
19151929
while (*inptr != (byte) '\n')
19161930
inptr++;
19171931

1918-
return IsBoundary (start, (int) (inptr - start), bounds[0].Marker, boundaryLength);
1932+
return IsBoundary (start, (int) (inptr - start), bounds[0], out final);
19191933
}
19201934

1935+
[MethodImpl (MethodImplOptions.AggressiveInlining)]
19211936
int GetMaxBoundaryLength ()
19221937
{
19231938
return bounds.Count > 0 ? bounds[0].MaxLength + 2 : 0;
@@ -2358,10 +2373,12 @@ unsafe int ConstructMultipart (ContentType contentType, byte* inbuf, int depth,
23582373
// We either found the end of the stream or we found a parent's boundary
23592374
PopBoundary ();
23602375

2361-
if (boundary == BoundaryType.ParentEndBoundary && FoundImmediateBoundary (inbuf, true))
2362-
boundary = BoundaryType.ImmediateEndBoundary;
2363-
else if (boundary == BoundaryType.ParentBoundary && FoundImmediateBoundary (inbuf, false))
2364-
boundary = BoundaryType.ImmediateBoundary;
2376+
// If the last boundary we found (before popping one off the stack) was a parent's boundary, we need to check
2377+
// to see if that boundary is now an immediate boundary and update our state.
2378+
if (boundary == BoundaryType.ParentEndBoundary || boundary == BoundaryType.ParentBoundary) {
2379+
if (FoundImmediateBoundary (inbuf, out var final))
2380+
boundary = final ? BoundaryType.ImmediateEndBoundary : BoundaryType.ImmediateBoundary;
2381+
}
23652382

23662383
endOffset = GetEndOffset (inputIndex);
23672384

0 commit comments

Comments
 (0)