Repository navigation
Adds deterministic ids from a from input - #5
Merged
Merged
Conversation
UxidOptions.Builder.from asks for an id derived from the input and the prefix, as the wire format at uxid-ex v2.10.0 lays it out: SHA-256 over the prefix, a zero byte, the input as UTF-8, a zero byte and one byte holding the size's hash character count, then the z marker and the digest's first count x 5 bits. The clock, time form, random width and byte source are not read, and fixed random bytes with it are refused with rand_with_from, as the reference does. An input or prefix holding an unpaired surrogate is refused rather than hashed with a replacement character, which would give two different strings one id. The corpus's deterministic cases and the rand_with_from refusal are copied into tests until the corpus runner lands. Refs: uxjv-iq8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Deterministic ids: the same prefix, input and size always give the same id.
UxidOptions.Builder.from(String)asks for a deterministic id.Uxid.generate(UxidOptions)then hashes, withMessageDigestSHA-256 (a new instance per call), the byte string the wire format (uxid-exguides/wire-format.mdatv2.10.0, "Deterministic bodies") lays out: the prefix's UTF-8 bytes, a zero byte, the input's UTF-8 bytes, a zero byte and one byte holding the size's hash character count. The body is the markerz(Zin upper case) followed by the digest's first count x 5 bits, 5 bits per character, at the size's standard body length.fromare refused withErrorCode.RAND_WITH_FROM(rand_with_from), whatever their count.Sizeholds each size's hash character count beside its random widths (package-private), andBase32gains a package-private writer for a whole number of leading 5-bit groups.Uxid,UxidOptions, the builder'sfromandbyteSource, andSizedescribes the deterministic form; CLAUDE.md's description of the tree now names it;changelog.d/uxjv-iq8.mdrecords the additions.Behaviour worth reading
fromset, the clock, the time form, the random width and the byte source are not read, as in the reference's deterministic path (uxid-exUXID.Encoder.processatv2.10.0): a compact time at the marker or a random width above 10 is not refused on a deterministic request, because neither is used.rand_with_fromrefusal comes before anything else on a deterministic request, as in the reference'sUXID.new, so fixed bytes of any count are refused with that name and never withrand_width_mismatch.fromrefuses it withIllegalArgumentExceptionwhen set, rather than hashing it with a replacement character, which would give two different strings one id; a prefix holding one is refused the same way when a deterministic id is generated, since the prefix is part of the hash input. Time-based generation does not encode the prefix and is unchanged.from_with_monotonicbelongs to the monotonic generator, which a later change adds.Provenance
fromfollows the reference's deterministic path rather than refusing those combinations; the wire format names no refusal for them.IllegalArgumentException, not aUxidException, soErrorCodestays exactly the wire format's closed set; it follows the rule the builder already states, that a setter refuses a value wrong on its own.Tests
DeterministicCorpusTestcopies every case ofconformance/deterministic.jsonatv2.10.0into a parameterized test, each named by its corpus id (the three golden ids, each size underusr, the empty and absent prefix, upper case and a non-ASCII input), and theinvalid/generate-rand-with-fromrefusal fromconformance/invalid.json. The full corpus runner comes with a later change.UxidDeterministicTestrecomputes the body a second way (the hash input built byte by byte, the digest read as one unsigned number and cut into characters) for each size; checks that a request loaded with a compact time at the marker, a random width of 11 and a refusing byte source still gives the golden id; that fixed bytes of several counts are refused withrand_with_from; that two prefixes give unrelated bodies for one input; and the unpaired-surrogate refusals for the input and the prefix.Gate
mise run gateis green on this head (Temurin 25.0.4, Maven 3.9.16): the enforcer rules, the compile with every javac lint and Error Prone, the tests, Spotless, the JaCoCo line check, and the sources and javadoc jars with doclint. The commit is the exact tree that run passed on, and main had not moved.Refs: uxjv-iq8