Skip to content
Merged

WIP v3 #3035

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
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IsPackable>false</IsPackable>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<PublicKey>00240000048000009400000006020000002400005253413100040000010001007791a689e9d8950b44a9a8886baad2ea180e7a8a854f158c9b98345ca5009cdd2362c84f368f1c3658c132b3c0f74e44ff16aeb2e5b353b6e0fe02f923a050470caeac2bde47a2238a9c7125ed7dab14f486a5a64558df96640933b9f2b6db188fc4a820f96dce963b662fa8864adbff38e5b4542343f162ecdc6dad16912fff</PublicKey>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
Expand Down
48 changes: 22 additions & 26 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,43 +1,39 @@
<Project>
<ItemGroup>
<!-- Packages we depend on for StackExchange.Redis, upgrades can create binding redirect pain! -->
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
<PackageVersion Include="Pipelines.Sockets.Unofficial" Version="2.2.8" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="5.0.0" />
<PackageVersion Include="System.Threading.Channels" Version="5.0.0" />
<PackageVersion Include="System.Threading.Channels" Version="10.0.5" />
<PackageVersion Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" />
<PackageVersion Include="System.IO.Compression" Version="4.3.0" />
<!-- note that this bumps System.Buffers, so is pinned in down-level in SE csproj -->
<PackageVersion Include="System.IO.Hashing" Version="10.0.2" />
<PackageVersion Include="System.IO.Pipelines" Version="10.0.5" />
<!-- note that this bumps System.Buffers, so is pinned in down-level in SE csproj -->
<PackageVersion Include="System.IO.Hashing" Version="10.0.5" />
<!-- for RESPite -->
<PackageVersion Include="System.Buffers" Version="4.6.1" />
<PackageVersion Include="System.Memory" Version="4.6.1" />

<PackageVersion Include="System.Memory" Version="4.6.3" />
<!-- For analyzers, tied to the consumer's build SDK; at the moment, that means "us" -->
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />

<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.3.0" />
<!-- Packages only used in the solution, upgrade at will -->
<PackageVersion Include="BenchmarkDotNet" Version="0.15.2" />
<PackageVersion Include="GitHubActionsTestLogger" Version="2.4.1" />
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="GitHubActionsTestLogger" Version="3.0.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="4.14.0" />
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="Microsoft.Testing.Platform" Version="1.7.3" />
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.7.115" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.201" />
<PackageVersion Include="Microsoft.Testing.Platform" Version="2.1.0" />
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.9.50" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="StackExchange.Redis" Version="2.6.96" />
<PackageVersion Include="StackExchange.Redis" Version="2.12.4" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<PackageVersion Include="System.Collections.Immutable" Version="9.0.0" />
<PackageVersion Include="System.Reflection.Metadata" Version="9.0.0" />

<PackageVersion Include="System.Collections.Immutable" Version="10.0.5" />
<PackageVersion Include="System.Reflection.Metadata" Version="10.0.5" />
<!-- For binding redirect testing, main package gets this transitively -->
<PackageVersion Include="System.IO.Pipelines" Version="9.0.0" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.0" />
<PackageVersion Include="xunit.v3" Version="3.0.0" />
<PackageVersion Include="xunit.v3.runner.console" Version="3.0.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.3" />
<PackageVersion Include="System.Runtime.Caching" Version="10.0.5" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="xunit.v3.runner.console" Version="3.2.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
</ItemGroup>
</Project>
3 changes: 2 additions & 1 deletion StackExchange.Redis.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=xreadgroup/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=xrevrange/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=zcard/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=zscan/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/UserDictionary/Words/=zscan/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=zset/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
15 changes: 15 additions & 0 deletions docs/exp/SER004.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# RESPite

RESPite is an experimental library that provides high-performance low-level RESP (Redis, etc) parsing and serialization.
It is used as the IO core for StackExchange.Redis v3+. You should not (yet) use it directly unless you have a very
good reason to do so.

```xml
<NoWarn>$(NoWarn);SER004</NoWarn>
```

or more granularly / locally in C#:

``` c#
#pragma warning disable SER004
```
21 changes: 21 additions & 0 deletions docs/exp/SER005.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Unit Testing

Unit testing is great! Yay, do more of that!

This type is provided for external unit testing, in particular by people using modules or server features
not directly implemented by SE.Redis - for example to verify messsage parsing or formatting without
talking to a RESP server.

These types are considered slightly more... *mercurial*. We encourage you to use them, but *occasionally*
(not just for fun) you might need to update your test code if we tweak something. This should not impact
"real" library usage.

```xml
<NoWarn>$(NoWarn);SER005</NoWarn>
```

or more granularly / locally in C#:

``` c#
#pragma warning disable SER005
```
47 changes: 46 additions & 1 deletion src/RESPite/Messages/RespReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,6 @@ private readonly unsafe bool TryParseSlow<T>(
/// <param name="value">The parsed value if successful.</param>
/// <returns><c>true</c> if parsing succeeded; otherwise, <c>false</c>.</returns>
#pragma warning disable RS0016, RS0027 // public API
[Experimental(Experiments.Respite, UrlFormat = Experiments.UrlFormat)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#if DEBUG
[Obsolete("Please prefer the function-pointer API for library-internal use.")]
Expand All @@ -758,6 +757,52 @@ public readonly bool TryParseScalar<T>(ScalarParser<byte, T> parser, out T value
return TryGetSpan(out var span) ? parser(span, out value) : TryParseSlow(parser, out value);
}

private readonly ReadOnlySpan<char> BufferChars(Span<char> target, out char[]? lease)
{
byte[] byteLease = [];
var bytes = Buffer(ref byteLease, byteLease);

int len = RespConstants.UTF8.GetMaxCharCount(bytes.Length);
if (len <= target.Length)
{
lease = null;
}
else
{
target = lease = ArrayPool<char>.Shared.Rent(len);
}
len = RespConstants.UTF8.GetChars(bytes, target);
return target.Slice(0, len);
}

/// <summary>
/// Tries to read the current scalar element using a parser callback.
/// </summary>
/// <typeparam name="T">The type of data being parsed.</typeparam>
/// <param name="parser">The parser callback.</param>
/// <param name="value">The parsed value if successful.</param>
/// <returns><c>true</c> if parsing succeeded; otherwise, <c>false</c>.</returns>
public readonly bool TryParseScalar<T>(ScalarParser<char, T> parser, out T value)
{
// note: no benefit in a function-ptr overload, after we've dealt with decoding bytes etc
var buffer = BufferChars(stackalloc char[128], out var lease);
try
{
return parser(buffer, out value);
}
finally
{
if (lease is not null) ArrayPool<char>.Shared.Return(lease);
}
}

/// <summary>
/// Tries to read the current scalar element using a parser callback.
/// </summary>
/// <typeparam name="T">The type of data being parsed.</typeparam>
/// <param name="parser">The parser callback.</param>
/// <param name="value">The parsed value if successful.</param>
/// <returns><c>true</c> if parsing succeeded; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.NoInlining)]
private readonly bool TryParseSlow<T>(ScalarParser<byte, T> parser, out T value)
{
Expand Down
1 change: 1 addition & 0 deletions src/RESPite/PublicAPI/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
[SER004]RESPite.Messages.RespReader.ScalarParser<TSource, TValue>
[SER004]RESPite.Messages.RespReader.TryParseScalar<T>(delegate*<System.ReadOnlySpan<byte>, out T, bool> parser, out T value) -> bool
[SER004]RESPite.Messages.RespReader.TryParseScalar<T>(RESPite.Messages.RespReader.ScalarParser<byte, T>! parser, out T value) -> bool
[SER004]RESPite.Messages.RespReader.TryParseScalar<T>(RESPite.Messages.RespReader.ScalarParser<char, T>! parser, out T value) -> bool
[SER004]static RESPite.AsciiHash.CaseInsensitiveEqualityComparer.get -> System.Collections.Generic.IEqualityComparer<RESPite.AsciiHash>!
[SER004]static RESPite.AsciiHash.CaseSensitiveEqualityComparer.get -> System.Collections.Generic.IEqualityComparer<RESPite.AsciiHash>!
[SER004]static RESPite.AsciiHash.EqualsCI(System.ReadOnlySpan<byte> first, System.ReadOnlySpan<byte> second) -> bool
Expand Down
1 change: 1 addition & 0 deletions src/RESPite/PublicAPI/net6.0/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#nullable enable
1 change: 1 addition & 0 deletions src/RESPite/PublicAPI/net6.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#nullable enable
2 changes: 0 additions & 2 deletions src/RESPite/RESPite.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,4 @@
<InternalsVisibleTo Include="StackExchange.Redis.Server" />
<InternalsVisibleTo Include="StackExchange.Redis.Benchmarks" />
</ItemGroup>


</Project>
10 changes: 0 additions & 10 deletions src/RESPite/Shared/AsciiHash.Public.cs

This file was deleted.

7 changes: 2 additions & 5 deletions src/RESPite/Shared/AsciiHash.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Buffers.Binary;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
Expand All @@ -7,8 +6,6 @@

namespace RESPite;

#pragma warning disable SA1205 // deliberately omit accessibility - see AsciiHash.Public.cs

/// <summary>
/// This type is intended to provide fast hashing functions for small ASCII strings, for example well-known
/// RESP literals that are usually identifiable by their length and initial bytes; it is not intended
Expand All @@ -22,7 +19,7 @@ namespace RESPite;
Inherited = false)]
[Conditional("DEBUG")] // evaporate in release
[Experimental(Experiments.Respite, UrlFormat = Experiments.UrlFormat)]
sealed partial class AsciiHashAttribute(string token = "") : Attribute
public sealed partial class AsciiHashAttribute(string token = "") : Attribute
{
/// <summary>
/// The token expected when parsing data, if different from the implied value. The implied
Expand All @@ -38,7 +35,7 @@ sealed partial class AsciiHashAttribute(string token = "") : Attribute

// note: instance members are in AsciiHash.Instance.cs.
[Experimental(Experiments.Respite, UrlFormat = Experiments.UrlFormat)]
readonly partial struct AsciiHash
public readonly partial struct AsciiHash
{
/// <summary>
/// In-place ASCII upper-case conversion.
Expand Down
Loading
Loading