Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions assemblySize.include.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
|----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------|
| netstandard2.0 | 8.0KB | 351.5KB | +343.5KB | +9.5KB | +6.5KB | +9.5KB | +14.0KB |
| netstandard2.1 | 8.5KB | 306.0KB | +297.5KB | +8.5KB | +6.0KB | +9.0KB | +13.5KB |
| net461 | 8.5KB | 350.5KB | +342.0KB | +9.0KB | +6.0KB | +9.0KB | +13.5KB |
| net461 | 8.5KB | 350.5KB | +342.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| net462 | 7.0KB | 354.0KB | +347.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| net47 | 7.0KB | 353.5KB | +346.5KB | +9.0KB | +6.5KB | +9.5KB | +13.5KB |
| net471 | 8.5KB | 353.0KB | +344.5KB | +9.0KB | +6.0KB | +9.0KB | +13.5KB |
| net472 | 8.5KB | 351.5KB | +343.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
| net48 | 8.5KB | 351.5KB | +343.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| net48 | 8.5KB | 351.5KB | +343.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
| net481 | 8.5KB | 351.5KB | +343.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
| netcoreapp2.0 | 9.0KB | 327.5KB | +318.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| netcoreapp2.1 | 9.0KB | 308.0KB | +299.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
Expand All @@ -31,12 +31,12 @@
|----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------|
| netstandard2.0 | 8.0KB | 513.4KB | +505.4KB | +17.2KB | +8.2KB | +14.4KB | +19.4KB |
| netstandard2.1 | 8.5KB | 441.7KB | +433.2KB | +16.2KB | +7.7KB | +13.9KB | +18.9KB |
| net461 | 8.5KB | 513.5KB | +505.0KB | +16.7KB | +7.7KB | +13.9KB | +18.9KB |
| net461 | 8.5KB | 513.5KB | +505.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| net462 | 7.0KB | 517.0KB | +510.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| net47 | 7.0KB | 516.2KB | +509.2KB | +16.7KB | +8.2KB | +14.4KB | +18.9KB |
| net471 | 8.5KB | 515.4KB | +506.9KB | +16.7KB | +7.7KB | +13.9KB | +18.9KB |
| net472 | 8.5KB | 512.8KB | +504.3KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
| net48 | 8.5KB | 512.8KB | +504.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| net48 | 8.5KB | 512.8KB | +504.3KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
| net481 | 8.5KB | 512.8KB | +504.3KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
| netcoreapp2.0 | 9.0KB | 478.9KB | +469.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| netcoreapp2.1 | 9.0KB | 447.4KB | +438.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
Expand Down
11 changes: 7 additions & 4 deletions src/Polyfill/Polyfill_StringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@ public static bool Equals(this StringBuilder target, ReadOnlySpan<char> span)
return false;
}

for (var index = 0; index < target.Length; index++)
// Walk the contiguous chunks and compare each with the vectorized SequenceEqual,
// rather than indexing the StringBuilder per char (which walks the chunk list on every access).
foreach (var chunk in target.GetChunks())
{
var ch1 = target[index];
var ch2 = span[index];
if (ch1 != ch2)
var chunkSpan = chunk.Span;
if (!chunkSpan.SequenceEqual(span.Slice(0, chunkSpan.Length)))
{
return false;
}

span = span.Slice(chunkSpan.Length);
}

return true;
Expand Down
8 changes: 4 additions & 4 deletions src/Split/net461/Polyfill_StringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public static bool Equals(this StringBuilder target, ReadOnlySpan<char> span)
{
return false;
}
for (var index = 0; index < target.Length; index++)
foreach (var chunk in target.GetChunks())
{
var ch1 = target[index];
var ch2 = span[index];
if (ch1 != ch2)
var chunkSpan = chunk.Span;
if (!chunkSpan.SequenceEqual(span.Slice(0, chunkSpan.Length)))
{
return false;
}
span = span.Slice(chunkSpan.Length);
}
return true;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Split/net462/Polyfill_StringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public static bool Equals(this StringBuilder target, ReadOnlySpan<char> span)
{
return false;
}
for (var index = 0; index < target.Length; index++)
foreach (var chunk in target.GetChunks())
{
var ch1 = target[index];
var ch2 = span[index];
if (ch1 != ch2)
var chunkSpan = chunk.Span;
if (!chunkSpan.SequenceEqual(span.Slice(0, chunkSpan.Length)))
{
return false;
}
span = span.Slice(chunkSpan.Length);
}
return true;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Split/net47/Polyfill_StringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public static bool Equals(this StringBuilder target, ReadOnlySpan<char> span)
{
return false;
}
for (var index = 0; index < target.Length; index++)
foreach (var chunk in target.GetChunks())
{
var ch1 = target[index];
var ch2 = span[index];
if (ch1 != ch2)
var chunkSpan = chunk.Span;
if (!chunkSpan.SequenceEqual(span.Slice(0, chunkSpan.Length)))
{
return false;
}
span = span.Slice(chunkSpan.Length);
}
return true;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Split/net471/Polyfill_StringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public static bool Equals(this StringBuilder target, ReadOnlySpan<char> span)
{
return false;
}
for (var index = 0; index < target.Length; index++)
foreach (var chunk in target.GetChunks())
{
var ch1 = target[index];
var ch2 = span[index];
if (ch1 != ch2)
var chunkSpan = chunk.Span;
if (!chunkSpan.SequenceEqual(span.Slice(0, chunkSpan.Length)))
{
return false;
}
span = span.Slice(chunkSpan.Length);
}
return true;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Split/net472/Polyfill_StringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public static bool Equals(this StringBuilder target, ReadOnlySpan<char> span)
{
return false;
}
for (var index = 0; index < target.Length; index++)
foreach (var chunk in target.GetChunks())
{
var ch1 = target[index];
var ch2 = span[index];
if (ch1 != ch2)
var chunkSpan = chunk.Span;
if (!chunkSpan.SequenceEqual(span.Slice(0, chunkSpan.Length)))
{
return false;
}
span = span.Slice(chunkSpan.Length);
}
return true;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Split/net48/Polyfill_StringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public static bool Equals(this StringBuilder target, ReadOnlySpan<char> span)
{
return false;
}
for (var index = 0; index < target.Length; index++)
foreach (var chunk in target.GetChunks())
{
var ch1 = target[index];
var ch2 = span[index];
if (ch1 != ch2)
var chunkSpan = chunk.Span;
if (!chunkSpan.SequenceEqual(span.Slice(0, chunkSpan.Length)))
{
return false;
}
span = span.Slice(chunkSpan.Length);
}
return true;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Split/net481/Polyfill_StringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public static bool Equals(this StringBuilder target, ReadOnlySpan<char> span)
{
return false;
}
for (var index = 0; index < target.Length; index++)
foreach (var chunk in target.GetChunks())
{
var ch1 = target[index];
var ch2 = span[index];
if (ch1 != ch2)
var chunkSpan = chunk.Span;
if (!chunkSpan.SequenceEqual(span.Slice(0, chunkSpan.Length)))
{
return false;
}
span = span.Slice(chunkSpan.Length);
}
return true;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Split/netcoreapp2.0/Polyfill_StringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public static bool Equals(this StringBuilder target, ReadOnlySpan<char> span)
{
return false;
}
for (var index = 0; index < target.Length; index++)
foreach (var chunk in target.GetChunks())
{
var ch1 = target[index];
var ch2 = span[index];
if (ch1 != ch2)
var chunkSpan = chunk.Span;
if (!chunkSpan.SequenceEqual(span.Slice(0, chunkSpan.Length)))
{
return false;
}
span = span.Slice(chunkSpan.Length);
}
return true;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Split/netstandard2.0/Polyfill_StringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public static bool Equals(this StringBuilder target, ReadOnlySpan<char> span)
{
return false;
}
for (var index = 0; index < target.Length; index++)
foreach (var chunk in target.GetChunks())
{
var ch1 = target[index];
var ch2 = span[index];
if (ch1 != ch2)
var chunkSpan = chunk.Span;
if (!chunkSpan.SequenceEqual(span.Slice(0, chunkSpan.Length)))
{
return false;
}
span = span.Slice(chunkSpan.Length);
}
return true;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Split/uap10.0/Polyfill_StringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public static bool Equals(this StringBuilder target, ReadOnlySpan<char> span)
{
return false;
}
for (var index = 0; index < target.Length; index++)
foreach (var chunk in target.GetChunks())
{
var ch1 = target[index];
var ch2 = span[index];
if (ch1 != ch2)
var chunkSpan = chunk.Span;
if (!chunkSpan.SequenceEqual(span.Slice(0, chunkSpan.Length)))
{
return false;
}
span = span.Slice(chunkSpan.Length);
}
return true;
}
Expand Down
23 changes: 23 additions & 0 deletions src/Tests/PolyfillTests_Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -527,5 +527,28 @@ public async Task StringEqualsSpan()
{
var builder = new StringBuilder("value");
await Assert.That(builder.Equals("value".AsSpan())).IsTrue();
await Assert.That(builder.Equals("other".AsSpan())).IsFalse();
await Assert.That(builder.Equals("val".AsSpan())).IsFalse();
await Assert.That(builder.Equals("values".AsSpan())).IsFalse();
await Assert.That(builder.Equals("".AsSpan())).IsFalse();
await Assert.That(new StringBuilder().Equals("".AsSpan())).IsTrue();
}

[Test]
public async Task StringEqualsSpanMultipleChunks()
{
// A small initial capacity followed by appends past it forces the
// StringBuilder to span multiple chunks, exercising the chunk-walking path.
var builder = new StringBuilder("a", 1);
builder.Append("bb");
builder.Append("ccc");

await Assert.That(builder.Equals("abbccc".AsSpan())).IsTrue();
// mismatch in the first chunk
await Assert.That(builder.Equals("xbbccc".AsSpan())).IsFalse();
// mismatch in a later chunk, after the span has advanced across a chunk boundary
await Assert.That(builder.Equals("abbcxc".AsSpan())).IsFalse();
// matching prefix but wrong length
await Assert.That(builder.Equals("abbcc".AsSpan())).IsFalse();
}
}
Loading