lexkey-dotnet is the .NET implementation of the language-independent LexKey encoding
specification used by lexkey-rs. It produces
byte-for-byte compatible, lexicographically sortable keys for typed numbers, strings,
UUIDs, time values, composites, and storage ranges.
The package is published to Cntryl's GitHub Packages feed:
<PackageReference Include="Cntryl.LexKey" Version="0.1.2" />The package namespace is Cntryl.Keys so the primary type remains the unambiguous
LexKey rather than colliding with its namespace.
using Cntryl.Keys;
var userKey = LexKey.EncodeComposite("tenant", "user", userId);
var numeric = LexKey.EncodeInt64(42);
var (lower, upper) = LexKey.EncodeRangeBounds(userKey.AsSpan());Typed numeric widths are preserved: byte, ushort, uint, and ulong encode to
1, 2, 4, and 8 bytes respectively, and signed/floating-point types behave likewise.
Normalize values to a common explicit width when a schema needs cross-width ordering.
For hot paths, reuse an Encoder:
var encoder = new Encoder(64);
encoder.EncodeString("tenant");
encoder.PushSeparator();
encoder.EncodeInt64(42);
var key = encoder.ToLexKey();
encoder.Clear();The composite convenience API uses typed, stack-friendly descriptors and allocates only
the immutable result for strings, numbers, UUIDs, existing keys, and custom encoders.
The reusable encoder can write without managed allocations after its buffer reaches capacity;
materializing a LexKey or array intentionally creates an owned copy.
Use AsMemory() to pass an encoded key to memory-oriented storage APIs such as Fitz without
copying; use ToArray() only when the caller needs mutable ownership.
PrefixEnd(P)returns structuredP || 0xFF.PrefixSuccessor(P)returns the finite upper bound for arbitrary raw prefixes by incrementing the final non-0xFFbyte and truncating.PrefixScanBounds(P)returns(P, PrefixSuccessor(P)).PrefixRangeBounds(P)returns structured(P || 0x00, P || 0xFF).EncodeRangeLowerandEncodeRangeUpperpreserve the Rust distinction between a missing row bound and an explicitly empty row bound.
These operations are intentionally separate. P || 0xFF is not a safe upper bound for
arbitrary raw prefixes whose children may begin with 0xFF.
- exact portable byte vectors and ordering tests
- immutable input/output ownership
- warnings-as-errors and full .NET analyzers
- XML documentation for every shipped public member
- locked restores and deterministic SourceLink packages
- external consumer restored only from freshly packed artifacts
- whole-assembly trimming and NativeAOT validation
AssemblyVersion remains 1.0.0.0; package versions advance independently. The project
is licensed under Apache-2.0.