PersistentMap/PersistentOrderedMap/KeyStrategies.cs

63 lines
1.8 KiB
C#

namespace PersistentOrderedMap;
using System;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
public interface IKeyStrategy<in TK>
{
int Compare(TK x, TK y);
long GetPrefix(TK key);
bool UsesPrefixes => true;
bool IsLossless => false;
bool UseBinarySearch => false;
}
public struct UnicodeStrategy : IKeyStrategy<string>
{
public bool UsesPrefixes => true;
public bool UseBinarySearch => false;
public bool IsLossLess => false;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Compare(string? x, string? y) => string.CompareOrdinal(x, y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public long GetPrefix(string key)
{
if (string.IsNullOrEmpty(key)) return long.MinValue;
// 1. Prepare Buffer (8 bytes)
// stackalloc is virtually free (pointer bump)
Span<byte> utf8Bytes = stackalloc byte[8];
// 2. Transcode (The "Safe" Magic)
// This intrinsic handles ASCII efficiently and converts Surrogates/Chinese
// into bytes that maintain the correct "Magnitude" (Sort Order).
// Invalid surrogates become 0xEF (Replacement Char), which sorts > ASCII.
System.Text.Unicode.Utf8.FromUtf16(
key.AsSpan(0, Math.Min(key.Length, 8)),
utf8Bytes,
out _,
out _,
replaceInvalidSequences: true); // True ensures we get 0xEF for broken chars
// 3. Load as Big Endian Long
long packed = BinaryPrimitives.ReadInt64BigEndian(utf8Bytes);
// 4. Sign Toggle
// Maps the byte range 0x00..0xFF to the signed long range Min..Max
// Essential for the < and > operators to work correctly.
return packed ^ unchecked((long)0x8080808080808080);
}
}