cargo / uuid / audit
cargo : uuid @ 1.23.1
PE Patrick Elsen signed 2026-05-27 published 2026-05-27

Claims

concurrency-documentedconcurrency-safecrypto-safehas-binarieshas-build-exechas-fuzz-testshas-install-exechas-integration-testshas-property-testshas-unit-testsimpl-algorithmimpl-concurrencyimpl-cryptoimpl-datastructureimpl-interpreterimpl-jitimpl-parserimpl-protocolis-benignparser-impl-correctparser-impl-safeparser-impl-testedunsafe-documentedunsafe-minimalunsafe-safeunsafe-testeduses-concurrencyuses-cryptouses-environmentuses-execuses-filesystemuses-interpreteruses-jituses-networkuses-unsafe

Summary

RFC 9562 UUID library (v1/v3/v4/v5/v6/v7/v8, nil, max) with parsers for simple/hyphenated/URN/braced forms. Unsafe is small and load-bearing (transmutes between repr(transparent) wrappers and from_utf8_unchecked on known-ASCII buffers); crypto is delegated to md-5/sha1_smol for the deterministic v3/v5 generators. Two low-severity quality findings (a typo and an operator-precedence-dependent mask); safe to deploy.

Report

Subject

uuid is a no_std-compatible Rust library for generating and parsing universally unique identifiers (UUIDs). It implements the formats defined in RFC 9562 — the nil and max UUIDs, the version 1, 3, 4, 5, 6, 7, and 8 variants, plus user-defined v8 — along with parsers for the simple, hyphenated, URN, and braced ("Microsoft GUID") textual formats. Each UUID version is gated behind a Cargo feature and pulls in its own dependencies only when enabled.

Methodology

The published crate contents were diffed against the upstream Git repository at the commit recorded in .cargo_vcs_info.json; all 23 source files match byte-for-byte and the only manifest difference is cargo's standard normalisation. All src/ files (~8500 lines including the four external/* integration modules) were read in full. Every unsafe block was located via grep and audited individually. The Cargo manifest, README, and lib.rs crate documentation were reviewed to confirm what each feature flag pulls in and to compare documented behaviour to the implementation. The test directory and fuzz target are not shipped in the crate but were inspected in the VCS checkout. The test suite was not executed.

Results

The diff between the published crate and the upstream Git tree shows the source files match byte-for-byte; the only manifest difference is cargo's standard normalisation. The crate ships no binary artefacts (justifying has-binaries), no build.rs, and is not a proc-macro crate, so there is no build-time or install-time code execution on consumers' machines (justifying has-build-exec and has-install-exec).

The crate is purely computational: a search for filesystem (fs::, File::), network (TcpStream, tokio, reqwest, hyper, UdpSocket), process (Command::, process::), and environment (std::env, env!) APIs returned no matches in src/ outside of using std::time::SystemTime for the now_v1/now_v6/now_v7 helpers. This justifies uses-filesystem, uses-network, uses-exec, and uses-environment. The crate does not implement an interpreter, a JIT, a protocol, a non-trivial data structure, or a non-cryptographic algorithm (justifying uses-jit, uses-interpreter, impl-interpreter, impl-jit, impl-protocol, impl-datastructure, and impl-algorithm).

Cryptographic hashing is delegated to the md-5 and sha1_smol dependencies in the v3/v5 modules. The crate's own code (src/md5.rs, src/sha1.rs) only calls update and finalize/digest and copies the first 16 bytes of the digest into the UUID. MD5 and SHA-1 are mandated by RFC 9562 for v3/v5 name-based UUIDs, where their cryptographic weakness is not load-bearing because v3/v5 UUIDs are deterministic identifiers, not security primitives. uses-crypto is true; impl-crypto is false.

Eight unsafe blocks were reviewed (src/error.rs:74, src/non_nil.rs:88-89, src/builder.rs:435, four sites in src/fmt.rs, and one site in src/macros.rs). Three classes appear: from_utf8_unchecked[_mut] on buffers known to contain ASCII (either previously validated UTF-8 input, or hex digits and ASCII separators written by the crate); core::mem::transmute between #[repr(transparent)] wrappers around [u8; 16] (the four formatter newtypes around Uuid, and &[u8; 16]&Uuid); and one transmute from a #[repr(C)] {u8, [u8; 36], u8} to a [u8; 38] byte buffer in encode_braced. All have safety justifications either inline or via the internal unsafe_transmute*! macros, all transmutes hold under Rust's repr-transparent/repr-C layout guarantees, and all from_utf8_unchecked* invariants hold. When the unstable zerocopy feature is enabled the transmute macros expand to zerocopy::transmute*!, which check the same invariants at compile time. This supports unsafe-safe, unsafe-documented, and unsafe-minimal.

Randomness for v4/v7 is taken from getrandom by default, with optional backends for rand (fast-rng/rng-rand) and, on wasm32-unknown-unknown, WebCrypto via wasm-bindgen (js). compile_error! enforces that at least one source is selected on wasm32-unknown-unknown. Random-bytes failures panic with a descriptive message rather than silently producing biased output.

Concurrency surfaces appear in three places: ContextV1 wraps an Atomic<u16> counter incremented with Ordering::AcqRel (RFC 9562 requires a 14-bit clock sequence; the result is masked accordingly); ContextV7 uses non-thread-safe Cell fields and is documented as such, with the shared instance wrapped in std::sync::Mutex via SharedContextV7; and ThreadLocalContext adapts thread-local storage. The thread-safety contract of each is documented on the type. This supports uses-concurrency, concurrency-safe, and concurrency-documented. The crate does not itself implement concurrency primitives — it uses existing ones — so impl-concurrency is false.

The parser (src/parser.rs) is const fn, dispatches on input length, decodes hex via two 256-byte lookup tables with a 0xFF sentinel for invalid characters, and is exercised by both unit tests and a libFuzzer fuzz target (fuzz/fuzz_targets/fuzz_target_parse.rs). This supports impl-parser, parser-impl-safe, parser-impl-tested, and parser-impl-correct.

Two low-severity quality findings were noted (FINDING-1, FINDING-2): a typo in a user-visible error message and an internal code comment, and an operator-precedence-dependent mask expression in encode_unix_timestamp_millis. Neither has a functional impact.

The crate ships in-source #[cfg(test)] modules (justifying has-unit-tests) and a cargo-fuzz harness in the upstream fuzz/ tree exercised by CI (justifying has-fuzz-tests). There is no tests/ directory of integration tests and no proptest/quickcheck use, justifying has-integration-tests and has-property-tests as false. The unsafe surface is exercised by the unit tests and miri-run CI jobs, justifying unsafe-tested. No malicious patterns, obfuscation, or unexpected side effects were observed, justifying is-benign.

Conclusion

The package implements RFC 9562 UUID generation and parsing without doing anything beyond what its description claims: no network, no filesystem, no process execution, no build-time hooks. Its unsafe usage is small, localised, and load-bearing for performance rather than gratuitous, and each block holds under review. Cryptographic operations are delegated to well-known crates and used only for the deterministic v3/v5 generators mandated by the RFC.

Findings(2)

FINDING-1 quality low

Minor typos in error message and code comment

Two minor typos:

  • src/timestamp.rs:209 — user-visible error message reads "unable to convert the system tie into a Unix timestamp"; should be "system time". This message is emitted by TryFrom<SystemTime> for Timestamp when the input is before the Unix epoch.
  • src/lib.rs:446 — code comment reads "Also check NonNilUuid when ading new derives here"; should be "adding".

No functional impact.

FINDING-2 quality low

v7 counter mask expression relies on operator precedence

src/timestamp.rs:318-320 (encode_unix_timestamp_millis) builds the time_high_and_version field as:

let counter_random_version = (counter_random_bytes[1] as u16
    | ((counter_random_bytes[0] as u16) << 8) & 0x0FFF)
    | (0x7 << 12);

Due to Rust's operator precedence (& binds tighter than |), this evaluates as (b1 | ((b0 << 8) & 0x0FFF)) | 0x7000. The result is still correct — b1 as u16 only has bits 0-7 set, so it never affects bits 12-15, and the & 0x0FFF correctly clears the version slot before | 0x7000 sets it — but the parenthesisation makes the intent ambiguous on first read. Compare to the cleaner v1/v6 form at lines 238 and 278 (((ticks >> 48) & 0x0FFF) as u16 | (1 << 12)). No functional impact.

Annotations(10)

src/builder.rs

src/builder.rs, line 433-436

    #[inline]
    pub fn from_bytes_ref(bytes: &Bytes) -> &Uuid {
        unsafe_transmute_ref!(bytes)
    }

from_bytes_ref transmutes &[u8; 16] to &Uuid. Uuid is #[repr(transparent)] over Bytes = [u8; 16] (lib.rs lines 288, 445, 465), so the transmute is sound. Justifies uses-unsafe, unsafe-safe, unsafe-documented, unsafe-minimal.

src/error.rs

src/error.rs, line 72-74

        // SAFETY: the byte array came from a valid utf8 string,
        // and is aligned along char boundaries.
        let uuid_str = unsafe { std::str::from_utf8_unchecked(uuid_str) };

Calls from_utf8_unchecked on bytes extracted from a previously-validated &str (input_str was obtained via std::str::from_utf8 at line 56), so the bytes are guaranteed to be valid UTF-8. Supports unsafe-safe and unsafe-documented.

src/fmt.rs

src/fmt.rs, line 132-180

impl Uuid {
    /// Get a [`Hyphenated`] formatter.
    #[inline]
    pub const fn hyphenated(self) -> Hyphenated {
        Hyphenated(self)
    }

    /// Get a borrowed [`Hyphenated`] formatter.
    #[inline]
    pub fn as_hyphenated(&self) -> &Hyphenated {
        unsafe_transmute_ref!(self)
    }

    /// Get a [`Simple`] formatter.
    #[inline]
    pub const fn simple(self) -> Simple {
        Simple(self)
    }

    /// Get a borrowed [`Simple`] formatter.
    #[inline]
    pub fn as_simple(&self) -> &Simple {
        unsafe_transmute_ref!(self)
    }

    /// Get a [`Urn`] formatter.
    #[inline]
    pub const fn urn(self) -> Urn {
        Urn(self)
    }

    /// Get a borrowed [`Urn`] formatter.
    #[inline]
    pub fn as_urn(&self) -> &Urn {
        unsafe_transmute_ref!(self)
    }

    /// Get a [`Braced`] formatter.
    #[inline]
    pub const fn braced(self) -> Braced {
        Braced(self)
    }

    /// Get a borrowed [`Braced`] formatter.
    #[inline]
    pub fn as_braced(&self) -> &Braced {
        unsafe_transmute_ref!(self)
    }
}

as_hyphenated, as_simple, as_urn, and as_braced transmute &Uuid to &Hyphenated/&Simple/&Urn/&Braced. Each formatter type is #[repr(transparent)] wrapping a single Uuid field (lines 82, 98, 114, 130), so the transmutes are sound by Rust's repr(transparent) guarantees.

src/fmt.rs, line 230-286

#[inline]
fn encode_simple<'b>(src: &[u8; 16], buffer: &'b mut [u8], upper: bool) -> &'b mut str {
    let buf = &mut buffer[..Simple::LENGTH];
    let buf: &mut [u8; Simple::LENGTH] = buf.try_into().unwrap();
    *buf = format_simple(src, upper);

    // SAFETY: The encoded buffer is ASCII encoded
    unsafe { str::from_utf8_unchecked_mut(buf) }
}

#[inline]
fn encode_hyphenated<'b>(src: &[u8; 16], buffer: &'b mut [u8], upper: bool) -> &'b mut str {
    let buf = &mut buffer[..Hyphenated::LENGTH];
    let buf: &mut [u8; Hyphenated::LENGTH] = buf.try_into().unwrap();
    *buf = format_hyphenated(src, upper);

    // SAFETY: The encoded buffer is ASCII encoded
    unsafe { str::from_utf8_unchecked_mut(buf) }
}

#[inline]
fn encode_braced<'b>(src: &[u8; 16], buffer: &'b mut [u8], upper: bool) -> &'b mut str {
    let buf = &mut buffer[..Hyphenated::LENGTH + 2];
    let buf: &mut [u8; Hyphenated::LENGTH + 2] = buf.try_into().unwrap();

    #[cfg_attr(all(uuid_unstable, feature = "zerocopy"), derive(zerocopy::IntoBytes))]
    #[repr(C)]
    struct Braced {
        open_curly: u8,
        hyphenated: [u8; Hyphenated::LENGTH],
        close_curly: u8,
    }

    let braced = Braced {
        open_curly: b'{',
        hyphenated: format_hyphenated(src, upper),
        close_curly: b'}',
    };

    *buf = unsafe_transmute!(braced);

    // SAFETY: The encoded buffer is ASCII encoded
    unsafe { str::from_utf8_unchecked_mut(buf) }
}

#[inline]
fn encode_urn<'b>(src: &[u8; 16], buffer: &'b mut [u8], upper: bool) -> &'b mut str {
    let buf = &mut buffer[..Urn::LENGTH];
    buf[..9].copy_from_slice(b"urn:uuid:");

    let dst = &mut buf[9..(9 + Hyphenated::LENGTH)];
    let dst: &mut [u8; Hyphenated::LENGTH] = dst.try_into().unwrap();
    *dst = format_hyphenated(src, upper);

    // SAFETY: The encoded buffer is ASCII encoded
    unsafe { str::from_utf8_unchecked_mut(buf) }
}

encode_simple, encode_hyphenated, encode_braced, and encode_urn write into the buffer using only ASCII bytes (the UPPER/LOWER lookup tables at lines 182-187, plus literal b'-', b'{', b'}', and the b"urn:uuid:" prefix). str::from_utf8_unchecked_mut is therefore sound. The unsafe_transmute!(braced) at line 269 transmutes a #[repr(C)] struct containing u8 + [u8; 36] + u8 to a [u8; 38] of the same layout. Supports unsafe-safe.

src/macros.rs

src/macros.rs, line 51-73

// SAFETY: Callers must ensure this call would be safe when handled by zerocopy
#[cfg(not(all(uuid_unstable, feature = "zerocopy")))]
macro_rules! unsafe_transmute_ref(
    ($e:expr) => { unsafe { core::mem::transmute::<&_, &_>($e) } }
);

// SAFETY: Callers must ensure this call would be safe when handled by zerocopy
#[cfg(all(uuid_unstable, feature = "zerocopy"))]
macro_rules! unsafe_transmute_ref(
    ($e:expr) => { zerocopy::transmute_ref!($e) }
);

// SAFETY: Callers must ensure this call would be safe when handled by zerocopy
#[cfg(not(all(uuid_unstable, feature = "zerocopy")))]
macro_rules! unsafe_transmute(
    ($e:expr) => { unsafe { core::mem::transmute::<_, _>($e) } }
);

// SAFETY: Callers must ensure this call would be safe when handled by zerocopy
#[cfg(all(uuid_unstable, feature = "zerocopy"))]
macro_rules! unsafe_transmute(
    ($e:expr) => { zerocopy::transmute!($e) }
);

Internal unsafe_transmute_ref! and unsafe_transmute! macros wrap core::mem::transmute. Callers must ensure source and destination layouts are compatible. With the zerocopy unstable feature enabled, the macros instead expand to zerocopy::transmute_ref!/zerocopy::transmute!, which check layout compatibility at compile time.

src/md5.rs

Uses the md-5 crate via the v3 feature to compute MD5 hashes for name-based UUID v3 generation. Calls only update and finalize on the hasher. MD5 is required by RFC 9562 for version 3 UUIDs; the cryptographic weakness of MD5 is not relevant here since UUID v3 is a deterministic identifier and not a security primitive. Justifies uses-crypto and crypto-safe.

src/non_nil.rs

src/non_nil.rs, line 83-90

    /// Creates a non-nil without checking whether the value is non-nil. This results in undefined behavior if the value is nil.
    ///
    /// # Safety
    ///
    /// The value must not be nil.
    pub const unsafe fn new_unchecked(uuid: Uuid) -> Self {
        NonNilUuid(unsafe { NonZeroU128::new_unchecked(uuid.as_u128()) })
    }

new_unchecked is declared unsafe fn with a documented safety contract (caller must ensure the UUID is non-nil). The inner NonZeroU128::new_unchecked call has an equivalent contract that is satisfied when the outer contract holds. Justifies uses-unsafe and unsafe-documented.

src/parser.rs

src/parser.rs, line 147-301

const fn try_parse(input: &'_ [u8]) -> Result<[u8; 16], InvalidUuid<'_>> {
    match (input.len(), input) {
        // Inputs of 32 bytes must be a non-hyphenated UUID
        (32, s) => parse_simple(s),
        // Hyphenated UUIDs may be wrapped in various ways:
        // - `{UUID}` for braced UUIDs
        // - `urn:uuid:UUID` for URNs
        // - `UUID` for a regular hyphenated UUID
        (36, s)
        | (38, [b'{', s @ .., b'}'])
        | (45, [b'u', b'r', b'n', b':', b'u', b'u', b'i', b'd', b':', s @ ..]) => {
            parse_hyphenated(s)
        }
        // Any other shaped input is immediately invalid
        _ => Err(InvalidUuid(input)),
    }
}

#[inline]
#[allow(dead_code)]
pub(crate) const fn parse_braced(input: &'_ [u8]) -> Result<[u8; 16], InvalidUuid<'_>> {
    if let (38, [b'{', s @ .., b'}']) = (input.len(), input) {
        parse_hyphenated(s)
    } else {
        Err(InvalidUuid(input))
    }
}

#[inline]
#[allow(dead_code)]
pub(crate) const fn parse_urn(input: &'_ [u8]) -> Result<[u8; 16], InvalidUuid<'_>> {
    if let (45, [b'u', b'r', b'n', b':', b'u', b'u', b'i', b'd', b':', s @ ..]) =
        (input.len(), input)
    {
        parse_hyphenated(s)
    } else {
        Err(InvalidUuid(input))
    }
}

#[inline]
pub(crate) const fn parse_simple(s: &'_ [u8]) -> Result<[u8; 16], InvalidUuid<'_>> {
    // This length check here removes all other bounds
    // checks in this function
    if s.len() != 32 {
        return Err(InvalidUuid(s));
    }

    let mut buf: [u8; 16] = [0; 16];
    let mut i = 0;

    while i < 16 {
        // Convert a two-char hex value (like `A8`)
        // into a byte (like `10101000`)
        let h1 = HEX_TABLE[s[i * 2] as usize];
        let h2 = HEX_TABLE[s[i * 2 + 1] as usize];

        // We use `0xff` as a sentinel value to indicate
        // an invalid hex character sequence (like the letter `G`)
        if h1 | h2 == 0xff {
            return Err(InvalidUuid(s));
        }

        // The upper nibble needs to be shifted into position
        // to produce the final byte value
        buf[i] = SHL4_TABLE[h1 as usize] | h2;
        i += 1;
    }

    Ok(buf)
}

#[inline]
pub(crate) const fn parse_hyphenated(s: &'_ [u8]) -> Result<[u8; 16], InvalidUuid<'_>> {
    // This length check here removes all other bounds
    // checks in this function
    if s.len() != 36 {
        return Err(InvalidUuid(s));
    }

    // We look at two hex-encoded values (4 chars) at a time because
    // that's the size of the smallest group in a hyphenated UUID.
    // The indexes we're interested in are:
    //
    // uuid     : 936da01f-9abd-4d9d-80c7-02af85c822a8
    //            |   |   ||   ||   ||   ||   |   |
    // hyphens  : |   |   8|  13|  18|  23|   |   |
    // positions: 0   4    9   14   19   24  28  32

    // First, ensure the hyphens appear in the right places
    match [s[8], s[13], s[18], s[23]] {
        [b'-', b'-', b'-', b'-'] => {}
        _ => return Err(InvalidUuid(s)),
    }

    let positions: [u8; 8] = [0, 4, 9, 14, 19, 24, 28, 32];
    let mut buf: [u8; 16] = [0; 16];
    let mut j = 0;

    while j < 8 {
        let i = positions[j];

        // The decoding here is the same as the simple case
        // We're just dealing with two values instead of one
        let h1 = HEX_TABLE[s[i as usize] as usize];
        let h2 = HEX_TABLE[s[(i + 1) as usize] as usize];
        let h3 = HEX_TABLE[s[(i + 2) as usize] as usize];
        let h4 = HEX_TABLE[s[(i + 3) as usize] as usize];

        if h1 | h2 | h3 | h4 == 0xff {
            return Err(InvalidUuid(s));
        }

        buf[j * 2] = SHL4_TABLE[h1 as usize] | h2;
        buf[j * 2 + 1] = SHL4_TABLE[h3 as usize] | h4;
        j += 1;
    }

    Ok(buf)
}

const HEX_TABLE: &[u8; 256] = &{
    let mut buf = [0; 256];
    let mut i: u8 = 0;

    loop {
        buf[i as usize] = match i {
            b'0'..=b'9' => i - b'0',
            b'a'..=b'f' => i - b'a' + 10,
            b'A'..=b'F' => i - b'A' + 10,
            _ => 0xff,
        };

        if i == 255 {
            break buf;
        }

        i += 1
    }
};

const SHL4_TABLE: &[u8; 256] = &{
    let mut buf = [0; 256];
    let mut i: u8 = 0;

    loop {
        buf[i as usize] = i.wrapping_shl(4);

        if i == 255 {
            break buf;
        }

        i += 1;
    }
};

The parser dispatches on input length (32 for simple, 36 for hyphenated, 38 for braced, 45 for URN) and decodes hex digits via precomputed 256-byte HEX_TABLE and SHL4_TABLE lookup tables. Invalid hex characters map to a 0xFF sentinel that is detected via bitwise OR; hyphen positions are checked before digit decoding. Each branch starts with an explicit length check that removes all subsequent bounds checks. The implementation is const fn, panic-free, and uses only arithmetic and bounded indexing. Supports impl-parser, parser-impl-safe, and parser-impl-correct, parser-impl-tested, has-fuzz-tests, unsafe-tested. Combined with no imports of std::net, std::fs, std::env, std::process, jit, or interpreter anywhere in src/, justifies uses-network, uses-filesystem, uses-environment, uses-exec, uses-jit, uses-interpreter, impl-interpreter, impl-jit, impl-protocol, impl-datastructure, impl-algorithm, impl-crypto, impl-concurrency.

src/rng.rs

Wraps platform-dependent randomness sources for v4/v7 UUID generation. On non-wasm platforms uses either rand (fast-rng or rng-rand features) or getrandom (default). On wasm32-unknown-unknown uses one of rand, getrandom (via the uuid-rng-internal workspace member), or WebCrypto via wasm-bindgen (js feature). Each backend calls the underlying API to fill a small fixed-size byte buffer; failures panic with a descriptive message.

src/sha1.rs

Uses the sha1_smol crate via the v5 feature to compute SHA-1 hashes for name-based UUID v5 generation. Calls only update and digest on the hasher. SHA-1 is required by RFC 9562 for version 5 UUIDs.

src/timestamp.rs

src/timestamp.rs, line 481-602

    #[cfg(any(feature = "v1", feature = "v6"))]
    mod v1_support {
        use super::*;

        #[cfg(all(feature = "std", feature = "rng"))]
        use crate::std::sync::LazyLock;

        use atomic::{Atomic, Ordering};

        #[cfg(all(feature = "std", feature = "rng"))]
        static CONTEXT: LazyLock<ContextV1> = LazyLock::new(ContextV1::new_random);

        #[cfg(all(feature = "std", feature = "rng"))]
        pub(crate) fn shared_context_v1() -> &'static ContextV1 {
            &*CONTEXT
        }

        /// An internally synchronized, wrapping counter that produces 14-bit values for version 1 and version 6 UUIDs.
        ///
        /// This type is:
        ///
        /// - **Non-reseeding:** The counter is not reseeded on each time interval (100ns).
        /// - **Non-adjusting:** The timestamp is not incremented when the counter wraps within a time interval (100ns).
        /// - **Thread-safe:** The underlying counter is atomic, so can be shared across threads.
        ///
        /// This type should be used when constructing versions 1 and 6 UUIDs.
        ///
        /// This type should not be used when constructing version 7 UUIDs. When used to
        /// construct a version 7 UUID, the 14-bit counter will be padded with random data.
        /// Counter overflows are more likely with a 14-bit counter than they are with a
        /// 42-bit counter when working at millisecond precision. This type doesn't attempt
        /// to adjust the timestamp on overflow.
        #[derive(Debug)]
        pub struct ContextV1 {
            count: Atomic<u16>,
        }

        impl ContextV1 {
            /// Construct a new context that's initialized with the given value.
            ///
            /// The starting value should be a random number, so that UUIDs from
            /// different systems with the same timestamps are less likely to collide.
            /// When the `rng` feature is enabled, prefer the [`ContextV1::new_random`] method.
            pub const fn new(count: u16) -> Self {
                Self {
                    count: Atomic::<u16>::new(count),
                }
            }

            /// Construct a new context that's initialized with a random value.
            #[cfg(feature = "rng")]
            pub fn new_random() -> Self {
                Self {
                    count: Atomic::<u16>::new(crate::rng::u16()),
                }
            }
        }

        impl ClockSequence for ContextV1 {
            type Output = u16;

            fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output {
                // RFC 9562 reserves 2 bits of the clock sequence so the actual
                // maximum value is smaller than `u16::MAX`. Since we unconditionally
                // increment the clock sequence we want to wrap once it becomes larger
                // than what we can represent in a "u14". Otherwise there'd be patches
                // where the clock sequence doesn't change regardless of the timestamp
                self.count.fetch_add(1, Ordering::AcqRel) & (u16::MAX >> 2)
            }

            fn usable_bits(&self) -> usize {
                14
            }
        }

        #[deprecated(since = "1.23.0", note = "renamed to `ContextV1`")]
        #[doc(hidden)]
        pub type Context = ContextV1;

        #[cfg(test)]
        mod tests {
            use crate::Timestamp;

            use super::*;

            #[test]
            fn context() {
                let seconds = 1_496_854_535;
                let subsec_nanos = 812_946_000;

                let context = ContextV1::new(u16::MAX >> 2);

                let ts = Timestamp::from_unix(&context, seconds, subsec_nanos);
                assert_eq!(16383, ts.counter);
                assert_eq!(14, ts.usable_counter_bits);

                let seconds = 1_496_854_536;

                let ts = Timestamp::from_unix(&context, seconds, subsec_nanos);
                assert_eq!(0, ts.counter);

                let seconds = 1_496_854_535;

                let ts = Timestamp::from_unix(&context, seconds, subsec_nanos);
                assert_eq!(1, ts.counter);
            }

            #[test]
            fn context_overflow() {
                let seconds = u64::MAX;
                let subsec_nanos = u32::MAX;

                let context = ContextV1::new(u16::MAX);

                // Ensure we don't panic
                Timestamp::from_unix(&context, seconds, subsec_nanos);
            }
        }
    }

    #[cfg(any(feature = "v1", feature = "v6"))]
    pub use v1_support::*;

ContextV1 wraps atomic::Atomic<u16> and increments it with fetch_add(1, Ordering::AcqRel), masking to a 14-bit value as required by RFC 9562. The shared instance is initialised via LazyLock and seeded from rng::u16(). Justifies uses-concurrency, concurrency-safe, and concurrency-documented.

src/timestamp.rs, line 705-1046

    #[cfg(feature = "v7")]
    mod v7_support {
        use super::*;

        use core::{cell::Cell, cmp, panic::RefUnwindSafe};

        #[cfg(feature = "std")]
        static CONTEXT_V7: SharedContextV7 =
            SharedContextV7(std::sync::Mutex::new(ContextV7::new()));

        #[cfg(feature = "std")]
        pub(crate) fn shared_context_v7() -> &'static SharedContextV7 {
            &CONTEXT_V7
        }

        const USABLE_BITS: usize = 42;

        // Leave the most significant bit unset
        // This guarantees the counter has at least 2,199,023,255,552
        // values before it will overflow, which is exceptionally unlikely
        // even in the worst case
        const RESEED_MASK: u64 = u64::MAX >> 23;
        const MAX_COUNTER: u64 = u64::MAX >> 22;

        /// An unsynchronized, reseeding counter that produces 42-bit values for version 7 UUIDs.
        ///
        /// This type is:
        ///
        /// - **Reseeding:** The counter is reseeded on each time interval (1ms) with a random 41-bit value.
        ///   The 42nd bit is left unset so the counter can safely increment over the millisecond.
        /// - **Adjusting:** The timestamp is incremented when the counter wraps within a time interval (1ms).
        ///   All subsequent timestamps in that same interval will also be incremented until it changes and
        ///   the counter is reseeded.
        /// - **Non-thread-safe:** The underlying counter uses unsynchronized cells, so needs to be wrapped in
        ///   a mutex to share.
        ///
        /// The counter can use additional sub-millisecond precision from the timestamp to better
        /// synchronize UUID sorting in distributed systems. In these cases, the additional precision
        /// is masked into the left-most 12 bits of the counter. The counter is still reseeded on
        /// each new millisecond, and incremented within the millisecond. This behavior may change
        /// in the future. The only guarantee is monotonicity.
        ///
        /// This type can be used when constructing version 7 UUIDs. When used to construct a
        /// version 7 UUID, the 42-bit counter will be padded with random data. This type can
        /// be used to maintain ordering of UUIDs within the same millisecond.
        ///
        /// This type should not be used when constructing version 1 or version 6 UUIDs.
        /// When used to construct a version 1 or version 6 UUID, only the 14 least significant
        /// bits of the counter will be used.
        #[derive(Debug)]
        pub struct ContextV7 {
            timestamp: Cell<ReseedingTimestamp>,
            counter: Cell<Counter>,
            adjust: Adjust,
            precision: Precision,
        }

        impl RefUnwindSafe for ContextV7 {}

        impl ContextV7 {
            /// Construct a new context that will reseed its counter on the first
            /// non-zero timestamp it receives.
            pub const fn new() -> Self {
                ContextV7 {
                    timestamp: Cell::new(ReseedingTimestamp {
                        last_seed: 0,
                        seconds: 0,
                        subsec_nanos: 0,
                    }),
                    counter: Cell::new(Counter { value: 0 }),
                    adjust: Adjust { by_ns: 0 },
                    precision: Precision {
                        bits: 0,
                        mask: 0,
                        factor: 0,
                        shift: 0,
                    },
                }
            }

            /// Specify an amount to shift timestamps by to obfuscate their actual generation time.
            pub fn with_adjust_by_millis(mut self, millis: u32) -> Self {
                self.adjust = Adjust::by_millis(millis);
                self
            }

            /// Use the leftmost 12 bits of the counter for additional timestamp precision.
            ///
            /// This method can provide better sorting for distributed applications that generate frequent UUIDs
            /// by trading a small amount of entropy for better counter synchronization. Note that the counter
            /// will still be reseeded on millisecond boundaries, even though some of its storage will be
            /// dedicated to the timestamp.
            pub fn with_additional_precision(mut self) -> Self {
                self.precision = Precision::new(12);
                self
            }
        }

        impl ClockSequence for ContextV7 {
            type Output = u64;

            fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
                self.generate_timestamp_sequence(seconds, subsec_nanos).0
            }

            fn generate_timestamp_sequence(
                &self,
                seconds: u64,
                subsec_nanos: u32,
            ) -> (Self::Output, u64, u32) {
                let (seconds, subsec_nanos) = self.adjust.apply(seconds, subsec_nanos);

                let mut counter;
                let (mut timestamp, should_reseed) =
                    self.timestamp.get().advance(seconds, subsec_nanos);

                if should_reseed {
                    // If the observed system time has shifted forwards then regenerate the counter
                    counter = Counter::reseed(&self.precision, &timestamp);
                } else {
                    // If the observed system time has not shifted forwards then increment the counter

                    // If the incoming timestamp is earlier than the last observed one then
                    // use it instead. This may happen if the system clock jitters, or if the counter
                    // has wrapped and the timestamp is artificially incremented

                    counter = self.counter.get().increment(&self.precision, &timestamp);

                    // Unlikely: If the counter has overflowed its 42-bit storage then wrap it
                    // and increment the timestamp. Until the observed system time shifts past
                    // this incremented value, all timestamps will use it to maintain monotonicity
                    if counter.has_overflowed() {
                        // Increment the timestamp by 1 milli and reseed the counter
                        timestamp = timestamp.increment();
                        counter = Counter::reseed(&self.precision, &timestamp);
                    }
                };

                self.timestamp.set(timestamp);
                self.counter.set(counter);

                (counter.value, timestamp.seconds, timestamp.subsec_nanos)
            }

            fn usable_bits(&self) -> usize {
                USABLE_BITS
            }
        }

        /// A timestamp that keeps track of whether a reseed is necessary.
        #[derive(Debug, Default, Clone, Copy)]
        struct ReseedingTimestamp {
            last_seed: u64,
            seconds: u64,
            subsec_nanos: u32,
        }

        impl ReseedingTimestamp {
            #[inline]
            fn from_ts(seconds: u64, subsec_nanos: u32) -> Self {
                // Reseed when the millisecond advances
                let last_seed = seconds
                    .saturating_mul(1_000)
                    .saturating_add((subsec_nanos / 1_000_000) as u64);

                ReseedingTimestamp {
                    last_seed,
                    seconds,
                    subsec_nanos,
                }
            }

            /// Advance the timestamp to a new value, returning whether a reseed is necessary.
            #[inline]
            fn advance(&self, seconds: u64, subsec_nanos: u32) -> (Self, bool) {
                let incoming = ReseedingTimestamp::from_ts(seconds, subsec_nanos);

                if incoming.last_seed > self.last_seed {
                    // The incoming value is part of a new millisecond
                    (incoming, true)
                } else {
                    // The incoming value is part of the same or an earlier millisecond
                    // We may still have advanced the subsecond portion, so use the larger value
                    let mut value = *self;
                    value.subsec_nanos = cmp::max(self.subsec_nanos, subsec_nanos);

                    (value, false)
                }
            }

            /// Advance the timestamp by a millisecond.
            #[inline]
            fn increment(&self) -> Self {
                let (seconds, subsec_nanos) =
                    Adjust::by_millis(1).apply(self.seconds, self.subsec_nanos);

                ReseedingTimestamp::from_ts(seconds, subsec_nanos)
            }

            #[inline]
            fn submilli_nanos(&self) -> u32 {
                self.subsec_nanos % 1_000_000
            }
        }

        /// A counter that initializes to a safe random seed and tracks overflow.
        #[derive(Debug, Clone, Copy)]
        struct Counter {
            value: u64,
        }

        impl Counter {
            #[inline]
            fn reseed(precision: &Precision, timestamp: &ReseedingTimestamp) -> Self {
                Counter {
                    value: precision.apply(crate::rng::u64() & RESEED_MASK, timestamp),
                }
            }

            /// Advance the counter.
            #[inline]
            fn increment(&self, precision: &Precision, timestamp: &ReseedingTimestamp) -> Self {
                let mut counter = Counter {
                    value: precision.apply(self.value, timestamp),
                };

                // We unconditionally increment the counter even though the precision
                // may have set higher bits already. This could technically be avoided,
                // but the higher bits are a coarse approximation so we just avoid the
                // `if` branch and increment it either way

                // Guaranteed to never overflow u64
                counter.value += 1;

                counter
            }

            #[inline]
            fn has_overflowed(&self) -> bool {
                self.value > MAX_COUNTER
            }
        }

        /// A utility that adjusts an input timestamp by a given number of nanoseconds.
        #[derive(Debug)]
        struct Adjust {
            by_ns: u128,
        }

        impl Adjust {
            #[inline]
            fn by_millis(millis: u32) -> Self {
                Adjust {
                    by_ns: (millis as u128).saturating_mul(1_000_000),
                }
            }

            /// Apply the adjustment, returning the adjusted timestamp.
            #[inline]
            fn apply(&self, seconds: u64, subsec_nanos: u32) -> (u64, u32) {
                if self.by_ns == 0 {
                    // No shift applied
                    return (seconds, subsec_nanos);
                }

                let ts = (seconds as u128)
                    .saturating_mul(1_000_000_000)
                    .saturating_add(subsec_nanos as u128)
                    .saturating_add(self.by_ns);

                ((ts / 1_000_000_000) as u64, (ts % 1_000_000_000) as u32)
            }
        }

        /// A utility that overwrites some number of counter bits with additional timestamp precision.
        #[derive(Debug)]
        struct Precision {
            bits: usize,
            factor: u64,
            mask: u64,
            shift: u64,
        }

        impl Precision {
            fn new(bits: usize) -> Self {
                // The mask and shift are used to paste the sub-millisecond precision
                // into the most significant bits of the counter
                let mask = u64::MAX >> (64 - USABLE_BITS + bits);
                let shift = (USABLE_BITS - bits) as u64;

                // The factor reduces the size of the sub-millisecond precision to
                // fit into the specified number of bits
                let factor = (999_999 / u64::pow(2, bits as u32)) + 1;

                Precision {
                    bits,
                    factor,
                    mask,
                    shift,
                }
            }

            /// Apply additional precision from the given timestamp to the counter.
            #[inline]
            fn apply(&self, counter: u64, timestamp: &ReseedingTimestamp) -> u64 {
                if self.bits == 0 {
                    // No additional precision is being used
                    return counter;
                }

                let additional = timestamp.submilli_nanos() as u64 / self.factor;

                (counter & self.mask) | (additional << self.shift)
            }
        }

        #[cfg(feature = "std")]
        pub(crate) struct SharedContextV7(std::sync::Mutex<ContextV7>);

        #[cfg(feature = "std")]
        impl ClockSequence for SharedContextV7 {
            type Output = u64;

            fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
                self.0.generate_sequence(seconds, subsec_nanos)
            }

            fn generate_timestamp_sequence(
                &self,
                seconds: u64,
                subsec_nanos: u32,
            ) -> (Self::Output, u64, u32) {
                self.0.generate_timestamp_sequence(seconds, subsec_nanos)
            }

            fn usable_bits(&self) -> usize
            where
                Self::Output: Sized,
            {
                USABLE_BITS
            }
        }

ContextV7 stores its state in non-thread-safe Cell<> fields; the type's docs explicitly state it is non-thread-safe and must be wrapped in a mutex to share. The shared instance SharedContextV7 wraps it in std::sync::Mutex. The counter is reseeded each millisecond from rng::u64() and incremented otherwise; overflow advances the timestamp by 1 ms and reseeds. Wraparound and overflow are tested (context_wrap, context_overflow). Supports has-unit-tests.

src/timestamp.rs, line 318-320

    let counter_random_version = (counter_random_bytes[1] as u16
        | ((counter_random_bytes[0] as u16) << 8) & 0x0FFF)
        | (0x7 << 12);

Mask expression for v7 version field; relies on operator precedence between & and |. See FINDING-2.

src/timestamp.rs, line 209-209

                "unable to convert the system tie into a Unix timestamp",

Typo in user-visible error message: "system tie" → "system time". See FINDING-1.