Skip to content

Latest commit

 

History

History
64 lines (53 loc) · 2.36 KB

File metadata and controls

64 lines (53 loc) · 2.36 KB

FastHashGenerator

Efficient matching of well-known short string tokens is a high-volume scenario, for example when matching RESP literals.

The purpose of this generator is to interpret inputs like:

[FastHash] public static partial class bin { }
[FastHash] public static partial class f32 { }

Usually the token is inferred from the name; [FastHash("real value")] can be used if the token is not a valid identifier. Underscore is replaced with hyphen, so a field called my_token has the default value "my-token". The generator demands all of [FastHash] public static partial class, and note that any containing types must also be declared partial.

The output is of the form:

static partial class bin
{
    public const int Length = 3;
    public const long Hash = 7235938;
    public static ReadOnlySpan<byte> U8 => @"bin"u8;
    public static string Text => @"bin";
    public static bool Is(long hash, in RawResult value) => ...
    public static bool Is(long hash, in ReadOnlySpan<byte> value) => ...
}
static partial class f32
{
    public const int Length = 3;
    public const long Hash = 3289958;
    public static ReadOnlySpan<byte> U8 => @"f32"u8;
    public const string Text = @"f32";
    public static bool Is(long hash, in RawResult value) => ...
    public static bool Is(long hash, in ReadOnlySpan<byte> value) => ...
}

(this API is strictly an internal implementation detail, and can change at any time)

This generated code allows for fast, efficient, and safe matching of well-known tokens, for example:

var key = ...
var hash = key.Hash64();
switch (key.Length)
{
    case bin.Length when bin.Is(hash, key):
        // handle bin
        break;
    case f32.Length when f32.Is(hash, key):
        // handle f32
        break;
}

The switch on the Length is optional, but recommended - these low values can often be implemented (by the compiler) as a simple jump-table, which is very fast. However, switching on the hash itself is also valid. All hash matches must also perform a sequence equality check - the Is(hash, value) convenience method validates both hash and equality.

Note that switch requires const values, hence why we use generated types rather than partial-properties that emit an instance with the known values. Also, the "..."u8 syntax emits a span which is awkward to store, but easy to return via a property.