v6.1: zero-alloc deterministic generators + ergonomic helpers#23
Conversation
Replaces IncrementalHash with one-shot SHA1.HashData / SHA256.HashData on NET6+. Concat buffer is stackalloc'd up to 256B with ArrayPool fallback. NET8+ writes the namespace directly in network byte order; NET6/7 falls back to TryWriteBytes + in-place swap. netstandard2.0 and NETFRAMEWORK paths unchanged. Result: GuidV5/GuidV8Name no longer allocate on .NET 6+.
Per RFC 9562 §5.10 (Max UUID). Naming matches BCL convention (int.MaxValue / DateTime.MaxValue).
There was a problem hiding this comment.
Pull request overview
This PR is a v6.1 minor release focused on improving performance of deterministic (name-based) UUID generation on modern TFMs and adding new Guid helper APIs for timestamp extraction and sequential-guid detection.
Changes:
- Reworks name-based UUID generation to use one-shot SHA hashing with
stackalloc/ArrayPoolbuffering on modern TFMs, plus a new in-place GUID byte swap helper. - Adds
[SkipLocalsInit]to several hot-path generator methods to avoid redundant stack zeroing. - Introduces new
Guidhelpers (Guid.MaxValue,TryToDateTime,ToDateTimeOffset,TryToDateTimeOffset,IsSequentialGuid) with tests, AOT smoke coverage, and README documentation.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| test/SequentialGuid.Tests/GuidExtensionsTests.cs | Adds unit tests covering the new Guid helper APIs and Guid.MaxValue. |
| test/SequentialGuid.AotSmokeTest/Program.cs | Extends the AOT smoke test to exercise the new helpers and ensure they work under AOT. |
| src/SequentialGuid/README.md | Updates highlights and adds usage examples for the new helper APIs. |
| src/SequentialGuid/GuidV8Time.cs | Applies [SkipLocalsInit] to the internal timestamp-based generator hot path. |
| src/SequentialGuid/GuidV7.cs | Optimizes NewGuid() to avoid an extra DateTimeOffset copy and applies [SkipLocalsInit] to the hot path. |
| src/SequentialGuid/GuidV4.cs | Applies [SkipLocalsInit] to the v4 generation hot path. |
| src/SequentialGuid/GuidNameBased.cs | Rewrites name-based hashing to use one-shot SHA hashing with stack/pooled concatenation buffers on modern TFMs. |
| src/SequentialGuid/Extensions/GuidExtensions.cs | Adds Guid.MaxValue and new timestamp/predicate helper methods. |
| src/SequentialGuid/Extensions/ByteArrayExtensions.cs | Adds SwapGuidBytesInPlace() for efficient mixed-endian ↔ network-endian conversion. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@copilot apply changes based on the comments in this thread |
Agent-Logs-Url: https://github.com/buvinghausen/SequentialGuid/sessions/c270bcd9-7660-4b47-9a2e-30f44b384ddb Co-authored-by: buvinghausen <1130210+buvinghausen@users.noreply.github.com>
Agent-Logs-Url: https://github.com/buvinghausen/SequentialGuid/sessions/c270bcd9-7660-4b47-9a2e-30f44b384ddb Co-authored-by: buvinghausen <1130210+buvinghausen@users.noreply.github.com>
Implemented the requested review-thread fixes in commits 0784be9 and c501f35: added checked length arithmetic in GuidNameBased, fixed the IsSequentialGuid XML summary, and corrected README framework/allocation wording plus the sample variable redeclaration issue. |
Summary
SemVer minor release. No breaking changes.
Perf
GuidV5.Create(byte[])andGuidV8Name.Create(byte[])drop from ~176 B/call to 0 B/call on .NET 6+. RewritesGuidNameBased.Createto use one-shotSHA1.HashData/SHA256.HashData(both .NET 5+) with astackalloc/ArrayPoolconcat buffer.[SkipLocalsInit]applied toGuidV4,GuidV7,GuidV8Time,GuidNameBasedgenerator methods — skips redundant JIT zero-init ofstackallocbuffers (every byte is explicitly written).
GuidV7.NewGuid()no-arg inlinesToUnixTimeMillisecondsextraction, saves oneDateTimeOffsetstruct copy.Ergonomic features (additive, purely)
Guid.MaxValue— RFC 9562 §5.10 max UUID constant. Backed by aprivate static readonlycache.Guid.TryToDateTime(out DateTime)— BCL-style Try-pattern variant of the existingToDateTime().Guid.ToDateTimeOffset()+Guid.TryToDateTimeOffset(out DateTimeOffset)— explicit-UTC variants.Guid.IsSequentialGuid()— public predicate delegating to internalSequentialGuidByteOrder.TryDetect.Benchmark deltas (BenchmarkDotNet on .NET 10)
GuidV5.Create(byte[])20-char nameGuidV5.Create(byte[])71-char nameGuidV8Name.Create(byte[])20-char nameGuidV8Name.Create(byte[])71-char nameGuidV5.Create(string)Guid.NewGuidbaselineGuidV4,GuidV7,GuidV7.NewSqlGuid,GuidV8Time,GuidV8Time.NewSqlGuidEvery generation path is now 0 B allocated on .NET 6+ (for the
byte[]overloads of v5/v8 name-based).Test plan
dotnet buildclean on all 5 production TFMs (net10.0,net9.0,net8.0,net462,netstandard2.0); 0 warnings underTreatWarningsAsErrors=truedotnet test— all tests pass (~25,067 total, +64 new fromGuidExtensionsTestsacross 4 test TFMs)2ed6657d-e927-568b-95e1-2665a8aea6a2for(Dns, "www.example.com")) still passes byte-for-byteNameBenchmarksshowsAllocated: –for both(byte[])overloads at both name lengthswindows-latestwith .NET 8/9/10 + AOT publish stepImplementation notes
[SuppressMessage("Security", "CA5350")]onGuidNameBased.Createis required becauseSHA1.HashDatais flagged by CA5350 (SHA-1 is weak for cryptopurposes). RFC 9562 §A.4 mandates SHA-1 for UUIDv5 — this is interop with the spec, not a security boundary. The suppression is scoped to the single method with
a Justification.
Span<byte>lifetime pattern inGuidNameBased.Create(stackalloc byte[256]declared at outer scope, conditional assignment to slice orArrayPool-rented array,try/finallyfor release) is sound under C# scope-based span safety rules.Guid.MaxValueextension lives inside the existingextension(Guid id)block (instance-receiver block hosting astaticmember). Separating intoits own
extension(Guid)block triggers CA1708 (members differing only by case) — consolidation resolves it.Suggested follow-ups (out of scope here)
IsSequentialGuidTrueForSqlOrderedV8for symmetry with the v7 SQL-order test.Span<char>/ UTF-8 overload ofGuidV5.Create/GuidV8Name.Createto close out the last allocation in thestringpath.ExtensionBenchmarks.csto keepIsSequentialGuid/TryToDateTimeperf honest as the detector grows.References
docs/superpowers/specs/2026-05-12-sequentialguid-v6.1-design.mddocs/superpowers/plans/2026-05-12-sequentialguid-v6.1.md