Skip to content

Add net9 Guid.Variant and Guid.Version - #604

Merged
SimonCropp merged 1 commit into
mainfrom
guid-variant-version
Sep 10, 2026
Merged

Add net9 Guid.Variant and Guid.Version#604
SimonCropp merged 1 commit into
mainfrom
guid-variant-version

Conversation

@SimonCropp

Copy link
Copy Markdown
Owner

Two instance properties, both net9. The shortlist named only Variant, but Version arrived with it and is the natural pair. Guid.AllBitsSet and CreateVersion7, the other net9 additions, were already polyfilled — so this closes the type.

Derived empirically, not from the RFC

Both are the high nibble of a single byte of the little-endian layout ToByteArray produces: the version at index 7 and the variant at index 8. Index 7 rather than 6 is the part worth checking rather than assuming, because the third group of a GUID is stored little-endian, so the version nibble that appears first in the text form lands in the second byte of that field.

Verified exhaustively: all 65 536 combinations of bytes 7 and 8 agree with the BCL, plus 500 000 random GUIDs confirming the other fourteen bytes do not participate. The tests also cross-check against the canonical string form — reading the version and variant nibbles straight out of ToString("D") — which is the assertion that would catch a wrong index or a wrong-endianness read, independently of how the implementation gets there.

Variant is not the RFC 4122 variant field

Worth flagging, because it is genuinely surprising. Guid.Variant returns the raw four-bit nibble, whereas the RFC 4122 variant is only the top two bits. So an ordinary UUID does not report a single constant — it reports 8, 9, 10 or 11 depending on the two "don't care" bits underneath:

uuid Version Variant
6ba7b810-9dad-11d1-80b4-00c04fd430c8 1 8
3d813cbb-47fb-32ba-91df-831e1593ac29 3 9
01890a5d-ac96-774b-bcce-b302099a8057 7 11
Guid.Empty 0 0
all bits set 15 15

I got this wrong first time: a test asserting Guid.NewGuid().Variant == 8 failed on net11 as well as on the polyfill, which is what identified it as a bad expectation rather than a broken implementation. The test now asserts the RFC 4122 predicate (Variant & 0b1100 == 0b1000) over 2000 generated GUIDs, which is what a caller actually wants to check.

Incidentally that also cross-checks Polyfill's own CreateVersion7: it passes on net462, so the existing polyfill is setting the variant bits correctly.

Implementation

Reads through TryWriteBytes into a stack buffer where that exists — from netcoreapp2.1 and netstandard2.1 — and falls back to ToByteArray() on net461 and netstandard2.0, which is the only path that allocates. No //Note: needed: on the frameworks where an allocation-free read is possible, the polyfill does it.

Verification

Solution clean in Release, Consume clean across all 22 TFMs, tests green on net11.0 (1735), net10.0 (1735), net9.0 (1735), net8.0 (1732), net462 (1681), plus PublicTests, EmbeddedTests, UnsafeTests, NoRefsTests and NoExtrasTests. net8.0 and net462 cover the two different read paths.

API count 1153 → 1155.

Two instance properties. Guid.AllBitsSet and CreateVersion7, the other net9 additions,
were already polyfilled, so this closes the type.

Both are the high nibble of one byte of the little endian layout ToByteArray produces:
the version at index 7 and the variant at index 8. Derived empirically rather than from
the RFC, and verified exhaustively over all 65536 combinations of those two bytes, plus
500000 random guids to confirm the other fourteen bytes do not participate.

Variant is worth knowing about: it is the raw four bit nibble, not the RFC 4122 variant
field, which is only the top two bits. So an ordinary uuid reports 8, 9, 10 or 11 rather
than one fixed value. A test asserting a single value failed identically on net11, which
is what identified this as a wrong expectation rather than a broken polyfill.

Reads through TryWriteBytes into a stack buffer where that exists, from netcoreapp2.1
and netstandard2.1, and falls back to ToByteArray on net461 and netstandard2.0.

API count 1153 -> 1155.
@SimonCropp SimonCropp added this to the 11.3.0 milestone Sep 10, 2026
@SimonCropp
SimonCropp merged commit bf80007 into main Sep 10, 2026
4 of 6 checks passed
@SimonCropp
SimonCropp deleted the guid-variant-version branch September 10, 2026 10:17
This was referenced Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant