Commit 80be84fc authored by Marc Gravell's avatar Marc Gravell

Make StartsWith more efficient

parent c33f7283
using System.Text;
using System.Runtime.CompilerServices;
using System.Text;
using Xunit;
namespace StackExchange.Redis.Tests
......@@ -151,39 +152,40 @@ private static void CheckString(RedisValue value, string expected)
private static byte[] Bytes(string s) => s == null ? null : Encoding.UTF8.GetBytes(s);
string LineNumber([CallerLineNumber] int lineNumber = 0) => lineNumber.ToString();
[Fact]
public void RedisValueStartsWith()
{
// test strings
RedisValue x = "abc";
Assert.True(x.StartsWith("a"));
Assert.True(x.StartsWith("ab"));
Assert.True(x.StartsWith("abc"));
Assert.False(x.StartsWith("abd"));
Assert.False(x.StartsWith("abcd"));
Assert.False(x.StartsWith(123));
Assert.False(x.StartsWith(false));
Assert.True(x.StartsWith("a"), LineNumber());
Assert.True(x.StartsWith("ab"), LineNumber());
Assert.True(x.StartsWith("abc"), LineNumber());
Assert.False(x.StartsWith("abd"), LineNumber());
Assert.False(x.StartsWith("abcd"), LineNumber());
Assert.False(x.StartsWith(123), LineNumber());
Assert.False(x.StartsWith(false), LineNumber());
// test binary
x = Encoding.ASCII.GetBytes("abc");
Assert.True(x.StartsWith("a"));
Assert.True(x.StartsWith("ab"));
Assert.True(x.StartsWith("abc"));
Assert.False(x.StartsWith("abd"));
Assert.False(x.StartsWith("abcd"));
Assert.False(x.StartsWith(123));
Assert.False(x.StartsWith(false));
Assert.True(x.StartsWith(Encoding.ASCII.GetBytes("a")));
Assert.True(x.StartsWith(Encoding.ASCII.GetBytes("ab")));
Assert.True(x.StartsWith(Encoding.ASCII.GetBytes("abc")));
Assert.False(x.StartsWith(Encoding.ASCII.GetBytes("abd")));
Assert.False(x.StartsWith(Encoding.ASCII.GetBytes("abcd")));
Assert.True(x.StartsWith("a"), LineNumber());
Assert.True(x.StartsWith("ab"), LineNumber());
Assert.True(x.StartsWith("abc"), LineNumber());
Assert.False(x.StartsWith("abd"), LineNumber());
Assert.False(x.StartsWith("abcd"), LineNumber());
Assert.False(x.StartsWith(123), LineNumber());
Assert.False(x.StartsWith(false), LineNumber());
Assert.True(x.StartsWith(Encoding.ASCII.GetBytes("a")), LineNumber());
Assert.True(x.StartsWith(Encoding.ASCII.GetBytes("ab")), LineNumber());
Assert.True(x.StartsWith(Encoding.ASCII.GetBytes("abc")), LineNumber());
Assert.False(x.StartsWith(Encoding.ASCII.GetBytes("abd")), LineNumber());
Assert.False(x.StartsWith(Encoding.ASCII.GetBytes("abcd")), LineNumber());
x = 10; // integers are effectively strings in this context
Assert.True(x.StartsWith(1));
Assert.True(x.StartsWith(10));
Assert.False(x.StartsWith(100));
Assert.True(x.StartsWith(1), LineNumber());
Assert.True(x.StartsWith(10), LineNumber());
Assert.False(x.StartsWith(100), LineNumber());
}
}
}
using System;
using System.Buffers;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
......@@ -769,6 +770,10 @@ public static RedisValue CreateFrom(MemoryStream stream)
/// </summary>
public bool StartsWith(RedisValue value)
{
if (this.IsNull || value.IsNull) return false;
if (value.IsNullOrEmpty) return true;
if (this.IsNullOrEmpty) return false;
ReadOnlyMemory<byte> rawThis, rawOther;
var thisType = this.Type;
if (thisType == value.Type) // same? can often optimize
......@@ -785,10 +790,50 @@ public bool StartsWith(RedisValue value)
return rawThis.Span.StartsWith(rawOther.Span);
}
}
rawThis = (ReadOnlyMemory<byte>)this;
rawOther = (ReadOnlyMemory<byte>)value;
return rawThis.Span.StartsWith(rawOther.Span);
byte[] arr0 = null, arr1 = null;
try
{
rawThis = this.AsMemory(out arr0);
rawOther = value.AsMemory(out arr1);
return rawThis.Span.StartsWith(rawOther.Span);
}
finally
{
if (arr0 != null) ArrayPool<byte>.Shared.Return(arr0);
if (arr1 != null) ArrayPool<byte>.Shared.Return(arr1);
}
}
private ReadOnlyMemory<byte> AsMemory(out byte[] leased)
{
switch(Type)
{
case StorageType.Raw:
leased = null;
return _memory;
case StorageType.String:
string s = (string)_objectOrSentinel;
HaveString:
if(s.Length == 0)
{
leased = null;
return default;
}
leased = ArrayPool<byte>.Shared.Rent(Encoding.UTF8.GetByteCount(s));
var len = Encoding.UTF8.GetBytes(s, 0, s.Length, leased, 0);
return new ReadOnlyMemory<byte>(leased, 0, len);
case StorageType.Double:
s = Format.ToString(OverlappedValueDouble);
goto HaveString;
case StorageType.Int64:
leased = ArrayPool<byte>.Shared.Rent(PhysicalConnection.MaxInt64TextLen + 2); // reused code has CRLF terminator
len = PhysicalConnection.WriteRaw(leased, _overlappedValue64) - 2; // drop the CRLF
return new ReadOnlyMemory<byte>(leased, 0, len);
}
leased = null;
return default;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment